標籤:
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