3

I've been working on a lockless c++11 fifo buffer. And I've almost got it. However one small detail has gotten the better of me. The buffer has a head pointed to by:

std::shared_ptr<node<T>> m_head;

Of the type:

    struct node
    {
        node(const T data)
            :
            data(new T(data)),
            next(nullptr)
        {}
        std::shared_ptr<T> data;
        std::shared_ptr<node<T>> next;
    };

And then there are the produce:

    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();
        }    

And consume:

        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>();
    }

Functions.

All seems to work well. However I reckon there is one flaw. If all nodes are consumed whilst executing a produce. The data will be added to the last element. Even though the element already has been deleted.

To be more precise, if this line has been executed:

if (std::atomic_compare_exchange_strong(&m_head, &l_expectedNullPointer, l_newNode))

And the loaded node wasn't zero. The next element of the last node will be changed. Regardless of whether the nodes are being deleted in the meantime or not. The nodes will not be physically deleted as long as the produce function is being excuted, because of the shared pointers.

However, the main pointer will be set to NULL. And therefore the new node will be deleted as soon as the produce function is exited.

Would anybody happen to know a solution for this problem:)?

4

1 回答 1

4

这种情况总是在无锁列表中通过在列表中保留一个虚拟节点来解决。头总是指向虚拟节点,它是列表中的第一个节点。

当队列变空时,head 和 tail 都指向一个虚拟节点。

您可以查看http://www.research.ibm.com/people/m/michael/podc-1996.pdf了解详细信息,这样我就不会歪曲这个概念,因为它很容易从文章中挑选出来。

于 2015-05-11T20:26:18.757 回答