使用風險指標(hazard pointer) 處理無鎖棧的 push 與 pop

來源:互聯網
上載者:User

標籤:

constexpr size_t maxHazardPointers = 100;struct HazardPointer{    std::atomic<std::thread::id> id;    std::atomic<void*>           pointer;};array<HazardPointer, maxHazardPointers> hazardPointers;class HazardPointerOwner{    HazardPointer* hazardPointer;public:    HazardPointerOwner(const HazardPointerOwner&)  = delete;    HazardPointerOwner& operator=(const HazardPointerOwner&) = delete;    ~HazardPointerOwner()    {        hazardPointer->pointer.store(nullptr);        hazardPointer->id.store(std::thread::id());    }    HazardPointerOwner():        hazardPointer(nullptr)    {        // Get hazard pointer of this thread.        for(size_t i = 0; i < maxHazardPointers; ++i){            std:thread::id oldId;            // If a hazard pointer‘s id is not be set,            // replace it with current thread‘s id.            if(hazardPointers[i].id.compare_exchange_strong(                                                            oldId,                                                            std::this_thread::get_id())){                // Then get the referrence of this hazard pointer.                hazardPointer = &hazardPointers[i];                break;            }        }        if (hazardPointer == nullptr){            throw std::runtime_error("No hazard pointers available");        }    }        std::atomic<void*>& getPointer()    {        return hazardPointer->pointer;    }};std::atomic<void*>& getHazardPointerForCurrentThread(){    // Every thread have it‘s own hazard pointer.    thread_local static HazardPointerOwner hazard;    return hazard.getPointer();}// If other threads have hazard pointer point to p.bool isOutstandingHazardPointer(void* p){    for (size_t i = 0; i < maxHazardPointers; ++i){        if (hazardPointers[i].pointer.load() == p) {            return true;        }    }    return false;}template <typename T>void doDelete(void* p){    delete static_cast<T*>(p);}struct DataToReclaim{    void* data;    std::function<void(void*)> deleter;    DataToReclaim* next;        template<typename T>    DataToReclaim(T* p):        data(p),        deleter(&doDelete<T>),        next(0)    {}        ~DataToReclaim()    {        deleter(data);    }};std::atomic<DataToReclaim*> nodesToReclaim;void addToReclaimList(DataToReclaim* node){    node->next = nodesToReclaim.load();    while(!nodesToReclaim.compare_exchange_weak(node->next, node));}template<typename T>void reclaimLater(T* data){    addToReclaimList(new DataToReclaim(data));}void deleteNodesWithoutHazards(){    // To make sure that get the head of the reclaim list atomically,    // use exchange instead of sentence like "auto current = nodesToReclaim;"    auto currentData = nodesToReclaim.exchange(nullptr);    while(currentData != nullptr){        auto const next = currentData->next;        if (!isOutstandingHazardPointer(currentData->data)) {            delete currentData;        }        else{            reclaimLater(currentData);        }        currentData = next;    }}template<typename T>class LockFreeStack{private:    struct Node    {        std::shared_ptr<T> data;        Node*              next;        Node(T const& value):        data(std::make_shared<T>(value))        {}    };        std::atomic<Node*>  head;    std::atomic<size_t> threadsInPopCount;    std::atomic<Node*>  toBeDeletedChainHead;        static void deleteNodes(Node* nodes)    {        while(nodes != nullptr){            auto const next = nodes->next;            delete nodes;            nodes = next;        }    }        void tryReclaim(Node* oldHead)    {        // If only this thread invoking pop().        if (threadsInPopCount == 1) {            auto toBeDeletedList = toBeDeletedChainHead.exchange(nullptr);            // Now, if only this thread in the pop.            if (--threadsInPopCount == 0){                deleteNodes(toBeDeletedList);            }            // If some nodes to be deleted still waiting.            else if (toBeDeletedList != nullptr){                pushListToDelChain(toBeDeletedList);            }            delete oldHead;        }        else{            // If more than one thread in the pop.            pushNodeToDelChain(oldHead);            --threadsInPopCount;        }    }        void pushListToDelChain(Node* nodes)    {        auto last = nodes;        while (auto const next = last->next) {            last = next;        }        pushListToDelChain(nodes, last);    }        void pushListToDelChain(Node* first, Node* last)    {        last->next = toBeDeletedChainHead;        // Put current head of delete chain to the next of the last.        // Then let the first be the head.        while(!toBeDeletedChainHead.compare_exchange_weak(last->next, first));    }        void pushNodeToDelChain(Node* node)    {        pushListToDelChain(node, node);    }public:    void push(T const& value)    {        auto const newNode = new Node(value);        newNode->next = head.load();        while (!head.compare_exchange_weak(newNode->next, newNode));    }        std::shared_ptr<T> pop()    {        auto& hazardPointer = getHazardPointerForCurrentThread();        auto  oldHead = head.load();        do{            Node* tempHead = nullptr;            do{                tempHead = oldHead;                hazardPointer.store(oldHead);                oldHead = head.load();            }while(oldHead != tempHead);        }while(oldHead &&               !head.compare_exchange_strong(oldHead, oldHead->next)); // 1                // If not set it‘s hazard pointer nullptr.        // when sime thread break at ‘//1‘ will cause endless loop.        hazardPointer.store(nullptr);                std::shared_ptr<T> result;        if (oldHead != nullptr){            result.swap(oldHead);            // Shouldn‘t check itself.            // So must clear current thread‘s hazard pointer before.            if (isOutstandingHazardPointer(oldHead)){                reclaimLater(oldHead);            }            else{                delete oldHead;            }            deleteNodesWithoutHazards();        }            }};

 

使用風險指標(hazard pointer) 處理無鎖棧的 push 與 pop

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.