@@ -102,14 +102,15 @@ class TrieSubtrie final : public TrieNode {
102
102
};
103
103
} // end namespace
104
104
105
- static size_t getTrieTailSize (size_t StartBit, size_t NumBits) {
106
- assert (NumBits < 20 && " Tries should have fewer than ~1M slots" );
105
+ // Compute the trailing object size in the trie node. This is the size of \c
106
+ // Slots in TrieNodes that pointing to the children.
107
+ static size_t getTrieTailSize (size_t NumBits) {
107
108
return sizeof (TrieNode *) * (1u << NumBits);
108
109
}
109
110
110
111
std::unique_ptr<TrieSubtrie> TrieSubtrie::create (size_t StartBit,
111
112
size_t NumBits) {
112
- size_t Size = sizeof (TrieSubtrie) + getTrieTailSize (StartBit, NumBits);
113
+ size_t Size = sizeof (TrieSubtrie) + getTrieTailSize (NumBits);
113
114
void *Memory = ::malloc (Size);
114
115
TrieSubtrie *S = ::new (Memory) TrieSubtrie (StartBit, NumBits);
115
116
return std::unique_ptr<TrieSubtrie>(S);
@@ -128,15 +129,22 @@ TrieSubtrie::TrieSubtrie(size_t StartBit, size_t NumBits)
128
129
" Expected no work in destructor for TrieNode" );
129
130
}
130
131
132
+ // Sink the nodes down sub-trie when the object being inserted collides with
133
+ // the index of existing object in the trie. In this case, a new sub-trie needs
134
+ // to be allocated to hold existing object.
131
135
TrieSubtrie *TrieSubtrie::sink (
132
136
size_t I, TrieContent &Content, size_t NumSubtrieBits, size_t NewI,
133
137
function_ref<TrieSubtrie *(std::unique_ptr<TrieSubtrie>)> Saver) {
138
+ // Create a new sub-trie that points to the existing object with the new
139
+ // index for the next level.
134
140
assert (NumSubtrieBits > 0 );
135
141
std::unique_ptr<TrieSubtrie> S = create (StartBit + NumBits, NumSubtrieBits);
136
142
137
143
assert (NewI < S->Slots .size ());
138
144
S->Slots [NewI].store (&Content);
139
145
146
+ // Using compare_exchange to atomically add back the new sub-trie to the trie
147
+ // in the place of the exsiting object.
140
148
TrieNode *ExistingNode = &Content;
141
149
assert (I < Slots.size ());
142
150
if (Slots[I].compare_exchange_strong (ExistingNode, S.get ()))
@@ -149,12 +157,15 @@ TrieSubtrie *TrieSubtrie::sink(
149
157
150
158
struct ThreadSafeTrieRawHashMapBase ::ImplType {
151
159
static std::unique_ptr<ImplType> create (size_t StartBit, size_t NumBits) {
152
- size_t Size = sizeof (ImplType) + getTrieTailSize (StartBit, NumBits);
160
+ size_t Size = sizeof (ImplType) + getTrieTailSize (NumBits);
153
161
void *Memory = ::malloc (Size);
154
162
ImplType *Impl = ::new (Memory) ImplType (StartBit, NumBits);
155
163
return std::unique_ptr<ImplType>(Impl);
156
164
}
157
165
166
+ // Save the Subtrie into the ownship list of the trie structure in a
167
+ // thread-safe way. The ownership transfer is done by compare_exchange the
168
+ // pointer value inside the unique_ptr.
158
169
TrieSubtrie *save (std::unique_ptr<TrieSubtrie> S) {
159
170
assert (!S->Next && " Expected S to a freshly-constructed leaf" );
160
171
@@ -306,12 +317,7 @@ ThreadSafeTrieRawHashMapBase::ThreadSafeTrieRawHashMapBase(
306
317
ContentOffset(ContentOffset),
307
318
NumRootBits(NumRootBits ? *NumRootBits : DefaultNumRootBits),
308
319
NumSubtrieBits(NumSubtrieBits ? *NumSubtrieBits : DefaultNumSubtrieBits),
309
- ImplPtr(nullptr ) {
310
- assert ((!NumRootBits || *NumRootBits < 20 ) &&
311
- " Root should have fewer than ~1M slots" );
312
- assert ((!NumSubtrieBits || *NumSubtrieBits < 10 ) &&
313
- " Subtries should have fewer than ~1K slots" );
314
- }
320
+ ImplPtr(nullptr ) {}
315
321
316
322
ThreadSafeTrieRawHashMapBase::ThreadSafeTrieRawHashMapBase (
317
323
ThreadSafeTrieRawHashMapBase &&RHS)
@@ -413,7 +419,7 @@ std::string ThreadSafeTrieRawHashMapBase::getTriePrefixAsString(
413
419
TrieContent *Node = nullptr ;
414
420
while (Current) {
415
421
TrieSubtrie *Next = nullptr ;
416
- // find first used slot in the trie.
422
+ // Find first used slot in the trie.
417
423
for (unsigned I = 0 , E = Current->Slots .size (); I < E; ++I) {
418
424
auto *S = Current->get (I);
419
425
if (!S)
0 commit comments