3

私はロックレスの 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 に設定されます。したがって、プロデュース関数が終了するとすぐに、新しいノードが削除されます。

誰かがこの問題の解決策を知っているでしょうか:)?

4

1 に答える 1