Wednesday, November 9, 2011

lock free intrusive FIFO - Part2

Попробовал Relacy Race Detector. Либа оправдала все ожидания - сходу нашла 1 баг в pop_front:

#include "windows.h"

template<class Type>
Type * Exchange(Type ** ppDestinationType * pData)
{
    return (Type * )InterlockedExchangePointer((void**)ppDestination, pData);
}

struct BaseNode
{
    BaseNode * pNext;

    BaseNode()
        : pNext(0)
    {
    }

private:
    BaseNode(const BaseNode &);
    BaseNode & operator = (const BaseNode &);
};

template<class Type>
struct ValueNode:public BaseNode
{
    Type value;
public:
    ValueNode(Type obj = 0)
        : value(obj)
    {
    }
    ~ValueNode()
    {
    }
};

template <class NodeType>
class CList
{
    CList(const CList &);
    CList & operator = (const CList &);

    BaseNode   m_processingNode;
    BaseNode   m_fakeNode;
    BaseNode * m_pFirst;
    BaseNode * m_pLast;

    long m_fakeNodeAdded;
protected:
    void push_back_impl(BaseNode * pNode)
    {
        BaseNode * pRealLast = Exchange(&m_pLast, pNode);
        // if pRealLast is fakenode, only after that node will be ready to be fetched:
        pRealLast->pNext = pNode;
    }

    bool pop_front_impl(BaseNode ** ppNode)
    {
       *ppNode = 0;

       while(1)
       {
           // take a first node
           BaseNode * pFirst = Exchange(&m_pFirst, &m_processingNode);
           if (pFirst == &m_processingNode)
                continue;

           // pFirst is valid
           if (!pFirst->pNext)
           {
               // there is no second element
               if (pFirst == &m_fakeNode)
               {
                   // cleanup
                   Exchange(&m_pFirst, pFirst);
                   return false;
               }

               // push back fake node
               if (InterlockedIncrement(&m_fakeNodeAdded) != 1)
               {
                   InterlockedDecrement(&m_fakeNodeAdded);

                   // cleanup
                   Exchange(&m_pFirst, pFirst);
                   continue;
               }

               push_back_impl(&m_fakeNode);

               // still not added
               if (!pFirst->pNext)
               {
                   // cleanup
                   Exchange(&m_pFirst, pFirst);
                   continue;
               }
           }

           *ppNode = pFirst;
           Exchange(&m_pFirst, pFirst->pNext);
           pFirst->pNext = 0;
           return true;
       }
    }

public:
    CList()
        : m_pFirst(0), m_pLast(0), m_fakeNodeAdded(1)
    {
        m_pFirst = m_pLast = &m_fakeNode;
    }

    // interface:
    void push_back(NodeType * pNode)
    {
        push_back_impl(pNode);
    }

    bool pop_front(NodeType ** pNode)
    {
        while(1)
        {
            bool res = pop_front_impl((BaseNode **)pNode);
            if (!res)
                return res;

            if (*pNode != &m_fakeNode)
            {
                return res;
            }
            InterlockedDecrement(&m_fakeNodeAdded);
        }
    }
};

Действительно, довольно дурацкий ляп - pop_front_impl может вернуть &m_fakeNode и во второй раз, нужно вызывать ее в цикле. В остальном все вроде бы ок, больше багов нет.
З.Ы. Похоже, фреймворк вправду крут.

No comments: