/* * Progress condition: lock-free * * The dequeue() marks the node that has the item as "logically removed" * by setting the "marked" bit in node.next * By default, the "head" is pointing to the first node that has not been * "logically removed", but if it's the last node (node.next is nullptr), * then the head will be pointing to the last "logically removed" node. */ T* dequeue(const int tid) { HPGuard hpguard { hp, tid }; // RAII to call hp.clear(tid) when returning while (true) { Node* lhead = hp.protectPtr(kHpHead, head.load(), tid); if (lhead != head.load()) continue; Node* lcurr = lhead; for (int i = 0; ;) { Node* lnext = lcurr->next.load(); if (lnext == getMarked(nullptr)) { if (lhead != lcurr && casHead(lhead, lcurr)) retireSubList(lhead, lcurr, tid); return nullptr; // Queue is empty } if (isMarked(lnext)) { hp.protectPtr(kHpNext+(i&0x1), getUnmarked(lnext), tid); // Alternate hps during traversal if (lhead != head.load()) break; lcurr = getUnmarked(lnext); i++; continue; } if (!lcurr->next.compare_exchange_strong(lnext, getMarked(lnext))) continue; T* item = lcurr->item; if (lcurr != lhead && casHead(lhead, lcurr)) retireSubList(lhead, lcurr, tid); return item; } } }
/* * Progress condition: lock-free * * If we don't know maxThreads, we can replace the for() loop with a * while(true) and it will still be correct. */ void enqueue(T* item, const int tid) { if (item == nullptr) throw std::invalid_argument("item can not be nullptr"); HPGuard hpguard { hp, tid }; // RAII to call hp.clear(tid) when returning Node* newNode = new Node(item); while (true) { Node* ltail = hp.protectPtr(kHpTail, tail.load(), tid); if (ltail != tail.load()) continue; Node* lnext = ltail->next.load(); if (getUnmarked(lnext) != nullptr) { // Advance the tail first casTail(ltail, getUnmarked(lnext)); // "tail" is always unmarked } else { for (int i=0; i < 2; i++) { Node* newNodeMark = isMarked(lnext) ? getMarked(newNode) : newNode; // lnext here is either nullptr or nullptr|0x1 newNode->next.store(nullptr, std::memory_order_relaxed); if (ltail->next.compare_exchange_strong(lnext, newNodeMark)) { casTail(ltail, newNode); return; } lnext = ltail->next.load(); if (getUnmarked(lnext) != nullptr) { casTail(ltail, getUnmarked(lnext)); // "tail" is always unmarked break; } } } for (int i = 0; i < maxThreads-1; i++) { // This loop will run at most maxThreads because the CAS can fail at most maxThreads lnext = ltail->next.load(); if (isMarked(lnext)) break; // This node has been dequeued, must re-read tail. It's ok to be marked as long as it's the first and therefore, nullptr newNode->next.store(lnext, std::memory_order_relaxed); if (ltail->next.compare_exchange_strong(lnext, newNode)) return; } } }
BitNextLazyHeadQueue(int maxThreads=MAX_THREADS) : maxThreads{maxThreads} { Node* sentinelNode = new Node(nullptr); // The sentinel is already "logically removed" sentinelNode->next.store(getMarked(nullptr), std::memory_order_relaxed); head.store(sentinelNode, std::memory_order_relaxed); tail.store(sentinelNode, std::memory_order_relaxed); }
/** * This method is named 'Delete()' in the original paper. * Taken from Figure 7 of the paper: * "High Performance Dynamic Lock-Free Hash Tables and List-Based Sets" */ bool remove(T* key, const int tid) { Node *curr, *next; std::atomic<Node*> *prev; while (true) { /* Try to find the key in the list. */ if (!find(key, &prev, &curr, &next, tid)) { return false; } /* Mark if needed. */ Node *tmp = getUnmarked(next); if (!curr->next.compare_exchange_strong(tmp, getMarked(next))) { continue; /* Another thread interfered. */ } tmp = getUnmarked(curr); prev->compare_exchange_strong(tmp, getUnmarked(next)); /* Unlink */ /* * If we want to prevent the possibility of there being an * unbounded number of unmarked nodes, add "else _find(head,key)." * This is not necessary for correctness. */ return true; } }
string Polynomial::toString(bool latex/*, bool mathMode*/)const { stringstream s; /* if(latex && !mathMode) s << "$"; */ bool first=true; if(terms.empty()) { s << "0"; return s.str(); } // If the polynomial has a marked term it is written first // printString("_"); IntegerVector e=getMarked().m.exponent; for(TermMap::const_iterator i=terms.begin();i!=terms.end();i++) if(e==i->first.exponent) { s << i->second.toString(i->first.exponent.isZero(),!first,latex); if((!i->first.exponent.isZero())&&(!i->second.isOne())&&(!(-(i->second)).isOne()))s<<"*"; s << i->first.toString(false,false,latex); first=false; } // printString("_"); for(TermMap::const_iterator i=terms.begin();i!=terms.end();i++) if(e!=i->first.exponent) { s << i->second.toString(i->first.exponent.isZero(),!first,latex); if((!i->first.exponent.isZero())&&(!i->second.isOne())&&(!(-(i->second)).isOne()))s<<"*"; s << i->first.toString(false,false,latex); /* printFieldElement(i->second,i->first.exponent.isZero(),!first); if((!i->first.exponent.isZero())&&(!i->second.isOne())&&(!(-(i->second)).isOne()))printString("*"); printMonomial(i->first,false,false);*/ first=false; } /* if(latex && !mathMode) s << "$"; */ return s.str(); }