私はロックレスの c++11 fifo バッファに取り組んできました。そして、私はほとんどそれを手に入れました。しかし、1つの小さな詳細が私を良くしました。バッファには、次が指すヘッドがあります。
std::shared_ptr<node<T>> m_head;
タイプの:
struct node
{
node(const T data)
:
data(new T(data)),
next(nullptr)
{}
std::shared_ptr<T> data;
std::shared_ptr<node<T>> next;
};
そして、農産物があります:
void produce(const T &&data)
{
//bool indicating whether a notification should be sent after adding
bool l_notifyUponAdding;
//the new node to be added at the end of the array
std::shared_ptr<node<T>> l_newNode(new node<T>(std::forward<const T&&>(data)));
//pointer to the last node
std::shared_ptr<node<T>> l_lastNode(std::atomic_load(&m_head));
//value to compare the next of the last node with
std::shared_ptr<node<T>> l_expectedNullPointer;
//notify if this isn't the only node
l_notifyUponAdding = !l_lastNode;
if (!l_lastNode)//if there are no nodes, add this as the only node
if (std::atomic_compare_exchange_strong(&m_head, &l_expectedNullPointer, l_newNode))
return;
do
{
l_expectedNullPointer.reset();
while (l_lastNode->next)
{
l_lastNode = std::atomic_load(&l_lastNode)->next;
}
} while (!std::atomic_compare_exchange_weak(&l_lastNode->next, &l_expectedNullPointer, l_newNode));
//adding failed since another thread already did this.
l_lastNode = l_expectedNullPointer;
if (l_notifyUponAdding)
m_newDataWaiter.notify_one();
}
そして消費します:
std::shared_ptr<T> consume(bool blockingCall = false)
{
//Check if the head is null if it is:
if (!std::atomic_load(&m_head))
{
if (blockingCall)//And this is a blocking call,
{
do
{
m_newDataWaiter.wait(m_newDataWaiterLock, [this]{return std::atomic_load(&(this->m_head)) == nullptr; });//we block until
} while (!std::atomic_load(&m_head));// the load yields a head that is not null(to avoid unnecessary calls on spurious wake ups)
}
else//And this is not a blocking call we
{
return nullptr;
}
}
//If we've found a valid head we will now try to make the node pointed to by head the new head.
std::shared_ptr<node<T>> l_poppee = atomic_load(&m_head);
std::shared_ptr<node<T>> l_newHead = atomic_load(&m_head);
//note that l_poppee gets updated if the compare exchange fails
while (l_poppee && !std::atomic_compare_exchange_weak(&m_head, &l_poppee, l_poppee->next))
{
}
if (l_poppee)
return l_poppee->data;
else
return std::shared_ptr<T>();
}
機能。
すべてうまくいくようです。しかし、私は1つの欠陥があると考えています。.a の実行中にすべてのノードが消費された場合produce
。データは最後の要素に追加されます。要素は既に削除されていますが。
より正確には、この行が実行された場合:
if (std::atomic_compare_exchange_strong(&m_head, &l_expectedNullPointer, l_newNode))
ロードされたノードはゼロではありませんでした。最後のノードの次の要素が変更されます。その間にノードが削除されているかどうかに関係なく。ポインタが共有されているため、プロデュース関数が実行されている限り、ノードは物理的に削除されません。
ただし、メイン ポインターは NULL に設定されます。したがって、プロデュース関数が終了するとすぐに、新しいノードが削除されます。
誰かがこの問題の解決策を知っているでしょうか:)?