8 namespace honey {
namespace lockfree
26 Node*
ptr()
const {
return reinterpret_cast<Node*
>(*data); }
56 template<
class Config>
60 typedef typename Config::Node
Node;
61 typedef typename Config::Link
Link;
62 typedef typename Config::Alloc::template rebind<Node>::other
Alloc;
70 delNodes(
new DelNode[mem._threshClean]),
71 delHazards(mem._alloc),
75 hazards.fill(
nullptr);
76 hazardRefCounts.fill(0);
78 hazardFreeList.reserve(hazards.size());
79 for (
auto i :
range(hazards.size())) hazardFreeList.push_back(i);
81 delNodeFreeList.reserve(mem._threshClean);
82 for (
auto i :
range(mem._threshClean)) delNodeFreeList.push_back(&delNodes[i]);
88 for (DelNode* delNode = delHead; delNode; delNode = delNode->next)
90 mem._alloc.destroy(delNode->node.load());
91 mem._alloc.deallocate(delNode->node, 1);
104 typedef set<Node*, std::less<Node*>,
typename Alloc::template rebind<Node*>::other> NodeLookup;
107 array<Atomic<Node*>, Config::hazardMax> hazards;
108 array<uint8, Config::hazardMax> hazardRefCounts;
109 vector<uint8> hazardFreeList;
111 vector<DelNode*> delNodeFreeList;
112 NodeLookup delHazards;
127 _threadMax(threadMax),
128 _threshClean(_threadMax*(Config::hazardMax + Config::linkMax + Config::linkDelMax + 1)),
129 _threshScan(Config::hazardMax*2 < _threshClean ? Config::hazardMax*2 : _threshClean),
130 _threadDataList(_threadMax),
132 _threadData(bind(&
HazardMem::initThreadData, this)) {}
134 template<
class... Args>
137 Node* node = _alloc.allocate(1);
138 _alloc.construct(node, forward<Args>(args)...);
145 ThreadData& td = threadData();
151 assert(td.delNodeFreeList.size() > 0,
"Not enough del nodes, algorithm problem");
152 auto& delNode = *td.delNodeFreeList.back();
153 td.delNodeFreeList.pop_back();
155 delNode.done =
false;
156 delNode.node = &node;
157 delNode.next = td.delHead;
158 td.delHead = &delNode; ++td.delCount;
161 if (td.delCount == _threshClean) cleanUpLocal();
162 if (td.delCount >= _threshScan) scan();
163 if (td.delCount == _threshClean) cleanUpAll();
171 ThreadData& td = threadData();
173 assert(td.hazardFreeList.size() > 0,
"Not enough hazard pointers");
174 uint8 index = td.hazardFreeList.back();
176 Node* node =
nullptr;
181 td.hazards[index] = node;
183 if (link.ptr() == node)
break;
191 for (
auto i:
range(td.hazards.size()))
if (i != index && td.hazards[i] == node) { found = i;
break; }
195 td.hazards[index] =
nullptr;
200 td.hazardFreeList.pop_back();
201 ++td.hazardRefCounts[index];
209 ThreadData& td = threadData();
213 for (
auto i:
range(td.hazards.size()))
if (td.hazards[i] == &node) { index = i;
break; }
218 assert(td.hazardFreeList.size() > 0,
"Not enough hazard pointers");
219 index = td.hazardFreeList.back();
220 td.hazardFreeList.pop_back();
222 td.hazards[index] = &node;
224 ++td.hazardRefCounts[index];
230 ThreadData& td = threadData();
233 for (
auto i:
range(td.hazards.size()))
if (td.hazards[i] == &node) { index = i;
break; }
234 assert(index >= 0,
"Hazard pointer not found");
237 assert(td.hazardRefCounts[index],
"Hazard pointer already released");
238 if (--td.hazardRefCounts[index])
return;
240 td.hazards[index] =
nullptr;
241 td.hazardFreeList.push_back(index);
245 bool casRef(Link& link,
const Link& val,
const Link& old)
247 if (link.data.cas(val.data, old.data))
252 val.ptr()->trace =
false;
254 if (old.ptr()) --old.ptr()->ref;
268 val.ptr()->trace =
false;
270 if (old.ptr()) --old.ptr()->ref;
275 struct ThreadDataRef { ThreadData& obj; };
277 ThreadDataRef* initThreadData()
281 assert(_threadDataCount < _threadMax,
"Too many threads accessing memory manager");
283 auto threadData =
new ThreadData(*
this);
286 return new ThreadDataRef{*threadData};
289 ThreadData& threadData() {
return _threadData->obj; }
294 ThreadData& td = threadData();
295 for (
auto* delNode = td.delHead; delNode; delNode = delNode->next)
296 _config.cleanUpNode(*delNode->node);
302 for (
auto ti :
range(_threadDataCount.load()))
304 ThreadData* td = _threadDataList[ti];
305 for (
auto i :
range(_threshClean))
307 auto& delNode = td->delNodes[i];
308 Node* node = delNode.node;
309 if (node && !delNode.done)
312 if (node == delNode.node)
313 _config.cleanUpNode(*node);
323 ThreadData& td = threadData();
326 for (
auto* delNode = td.delHead; delNode; delNode = delNode->next)
328 Node& node = *delNode->node;
338 for (
auto ti :
range(_threadDataCount.load()))
340 ThreadData* tdata = _threadDataList[ti];
341 for (
auto i :
range(tdata->hazards.size()))
343 Node* node = tdata->hazards[i];
344 if (node) td.delHazards.insert(node);
349 typename ThreadData::DelNode* newDelHead =
nullptr;
354 auto* delNode = td.delHead;
355 td.delHead = delNode->next;
356 Node& node = *delNode->node;
357 if (node.ref == 0 && node.trace && !td.delHazards.count(&node))
359 delNode->node =
nullptr;
360 if (delNode->claim == 0)
362 _config.terminateNode(node,
false);
364 td.delNodeFreeList.push_back(delNode);
365 _alloc.destroy(&node);
366 _alloc.deallocate(&node, 1);
369 _config.terminateNode(node,
true);
370 delNode->done =
true;
371 delNode->node = &node;
373 delNode->next = newDelHead;
374 newDelHead = delNode;
378 td.delHazards.clear();
379 td.delHead = newDelHead;
380 td.delCount = newDelCount;
385 const int _threadMax;
386 const szt _threshClean;
387 const szt _threshScan;
388 vector<UniquePtr<ThreadData>> _threadDataList;
389 Atomic<int> _threadDataCount;
390 thread::Local<ThreadDataRef> _threadData;
391 SpinLock _threadDataLock;
Atomic< bool > del
Marked for deletion.
Definition: HazardMem.h:18
Base node class, inherit from this class, add link members, and use as Node type in HazardMemConfig...
Definition: HazardMem.h:12
void terminateNode(Node &node, bool concurrent)
Remove all links to other nodes. If concurrent is false then the faster storeRef can be used instead ...
void releaseRef(Node &node)
Release a reference to a node, clears hazard pointer.
Definition: HazardMem.h:228
DelNode()
Definition: HazardMem.h:97
ptrdiff_t sdt
Size difference type, shorthand for ptrdiff_t.
Definition: Core.h:92
Atomic< int > claim
Definition: HazardMem.h:100
Inherit to declare that class is not copyable.
Definition: Meta.h:286
Node & createNode(Args &&...args)
Definition: HazardMem.h:135
Config::Alloc::template rebind< Node >::other Alloc
Definition: HazardMem.h:62
Atomic< intptr_t > data
Definition: HazardMem.h:28
std::enable_if< mt::isIterator< Iter1 >::value, Range_< Iter1, Iter2 > >::type range(Iter1 &&first, Iter2 &&last)
Range from iterators [first, last)
Definition: Range.h:116
static auto _
Definition: Module.cpp:8
static const uint8 linkDelMax
Number of links per node that may transiently point to a deleted node.
Definition: HazardMem.h:42
unsigned char uint8
Definition: Core.h:12
Atomic< bool > trace
Used in scan()
Definition: HazardMem.h:17
HazardMemNode()
Definition: HazardMem.h:14
void ref(Node &node)
Add reference to node, sets up hazard pointer.
Definition: HazardMem.h:207
#define assert(...)
Forwards to assert_#args. See assert_1(), assert_2().
Definition: Debug.h:24
void storeRef(Link &link, const Link &val)
Set link in a single-threaded environment.
Definition: HazardMem.h:261
Atomic< Node * > node
Definition: HazardMem.h:99
void deleteNode(Node &node)
Definition: HazardMem.h:143
void cleanUpNode(Node &node)
Update all links in the node to point to active (non-deleted) nodes.
Config::Node Node
Definition: HazardMem.h:60
HazardMemLink< Node > Link
Definition: HazardMem.h:35
size_t szt
Size type, shorthand for size_t.
Definition: Core.h:90
T * alloc(szt count=1)
Allocate memory for count number of T objects. Objects are not constructed.
Definition: Allocator.h:31
HazardMem(Config &config, const Alloc &alloc, int threadMax)
Definition: HazardMem.h:124
Configuration interface for memory manager. Inherit this class and override types and static members...
Definition: HazardMem.h:32
Global allocator for small memory blocks. To provide a custom pool define SmallAllocator_createSingle...
Definition: SmallAllocator.h:23
static const uint8 linkMax
Number of links per node.
Definition: HazardMem.h:40
Definition: HazardMem.h:95
Atomic< bool > done
Definition: HazardMem.h:101
Node * ptr() const
Get node pointer.
Definition: HazardMem.h:26
HazardMemNode Node
Definition: HazardMem.h:34
DelNode * next
Definition: HazardMem.h:102
Node * deRefLink(Link &link)
Dereference a link, protects with hazard pointer. May return null.
Definition: HazardMem.h:169
Lock-free memory manager, provides safe memory reclamation for concurrent algorithms.
Definition: HazardMem.h:57
Pointer to a unique, non-shared, object. Finalizer is run upon destruction (deletes object by default...
Definition: SharedPtr.h:164
bool casRef(Link &link, const Link &val, const Link &old)
Compare and swap link. Set link in a concurrent environment. Returns false if the link was changed by...
Definition: HazardMem.h:245
Global Honeycomb namespace.
Base link class, contains a generic Cas-able data chunk. The data chunk contains a pointer to a Hazar...
Definition: HazardMem.h:23
static const uint8 hazardMax
Number of thread-local hazard pointers.
Definition: HazardMem.h:44
SmallAllocator< Node > Alloc
Allocator for nodes, should be lock-free.
Definition: HazardMem.h:37
Atomic< int > ref
Reference count by all threads.
Definition: HazardMem.h:16
Config::Link Link
Definition: HazardMem.h:61