115 capacity = aCapacity;
123 while (leafNodeCount < requestedLeafNodeCount) {
127 innerLevelCount = treeHeight - 1;
129 debugs(54, 5,
"rounded capacity up from " << capacity <<
" to " << (leafNodeCount*
BitsPerLeaf));
147 return IdSetInnerNode(packed >> 32,
static_cast<uint32_t
>(packed));
153 measurements(capacity),
158 static_assert(
sizeof(
StoredNode) ==
sizeof(
Node),
"atomic locks use no storage");
169 if (measurements.capacity != measurements.leafNodeCount*
BitsPerLeaf)
179 auto pos =
Position(measurements.treeHeight-1, 0);
180 const auto allOnes = ~uint64_t(0);
181 std::fill_n(valueAddress(pos), measurements.leafNodeCount, allOnes);
184 auto nodesAtLevel = measurements.leafNodeCount/2;
189 std::fill_n(valueAddress(pos), nodesAtLevel, value);
192 }
while (!pos.atRoot());
203 leafTruncate(pos, measurements.capacity %
BitsPerLeaf);
204 const auto rightLeaves = measurements.leafNodeCount - measurements.requestedLeafNodeCount;
208 std::fill_n(valueAddress(pos) + 1, rightLeaves-1, 0);
214 const auto direction = pos.ascendDirection();
216 toSubtract = innerTruncate(pos, direction, toSubtract);
217 }
while (!pos.atRoot());
225 assert(
node == std::numeric_limits<Node>::max());
226 static_assert(std::is_unsigned<Node>::value,
"right shift prepends 0s");
237 auto *valuePtr = valueAddress(pos);
241 toSubtractNext = toSubtract + value.right;
242 assert(value.left >= toSubtract);
243 value.left -= toSubtract;
247 toSubtractNext = toSubtract;
248 assert(value.right >= toSubtract);
250 value.right -= toSubtract;
252 *valuePtr = value.pack();
253 return toSubtractNext;
262 const auto previousValue = nodeAt(pos).fetch_add(increment);
264 assert(previousValue <= std::numeric_limits<Node>::max() - increment);
274 auto &
node = nodeAt(pos);
275 auto oldValue =
node.load();
282 }
else if (newValue.
right) {
288 }
while (!
node.compare_exchange_weak(oldValue, newValue.
pack()));
299 const auto oldValue = nodeAt(pos).fetch_or(mask);
301 assert((oldValue & mask) == 0);
313 for (uint64_t mask = 1; !(x & mask); mask <<= 1)
322 auto &
node = nodeAt(pos);
323 auto oldValue =
node.load();
327 const auto mask = oldValue - 1;
328 newValue = oldValue & mask;
329 }
while (!
node.compare_exchange_weak(oldValue, newValue));
367 const auto nodesAbove = (1U << pos.
level) - 1;
371 const auto nodesToTheLeft = pos.
offset;
373 const size_t nodesBefore = nodesAbove + nodesToTheLeft;
374 assert(nodesBefore < measurements.nodeCount());
375 return nodes_[nodesBefore];
383 return &
reinterpret_cast<Node&
>(nodeAt(pos));
390 const auto directionFromRoot = innerPop(rootPos);
391 if (directionFromRoot ==
dirEnd)
394 auto pos = descend(rootPos, directionFromRoot);
395 for (
size_t level = 1; level < measurements.innerLevelCount; ++level) {
396 const auto direction = innerPop(pos);
397 pos = descend(pos, direction);
408 auto pos =
Position(measurements.innerLevelCount, offsetAtLeafLevel);
412 const auto direction = pos.ascendDirection();
414 innerPush(pos, direction);
415 }
while (!pos.atRoot());
433 ids_(config_.capacity)
446 if (!config_.capacity)
450 if (!ids_.pop(pageIndex))
454 const auto newSize = --size_;
455 assert(newSize < config_.capacity);
457 page.
number = pageIndex + 1;
458 page.
pool = config_.poolId;
459 debugs(54, 8, page <<
" size: " << newSize);
460 assert(pageIdIsValid(page));
469 assert(pageIdIsValid(page));
472 const auto newSize = ++size_;
473 assert(newSize <= config_.capacity);
475 const auto pageIndex = page.
number - 1;
476 ids_.push(pageIndex);
478 debugs(54, 8, page <<
" size: " << newSize);
485 return page.
pool == config_.poolId &&
492 return SharedMemorySize(config_);
500 return StackSize(cfg.
capacity) + pagesDataSize + levelsSize;
515 return StackSize(config_.capacity);
521 const auto displacement = StackSize(capacity) %
alignof(
Levels_t);
522 return displacement ?
alignof(
Levels_t) - displacement : 0;
static int trailingZeros(uint64_t x)
a temporary C++20 countr_zero() replacement
a helper class to perform inner node manipulation for IdSet
Packed pack() const
returns a serializes value suitable for shared memory storage
size_type right
the number of available IDs in the right subtree
static IdSetInnerNode Unpack(Packed packed)
de-serializes a given value
uint64_t Packed
(atomically) stored serialized value
size_type left
the number of available IDs in the left subtree
IdSet::size_type size_type
basic IdSet storage parameters, extracted here to keep them constant
uint32_t size_type
we need to fit two size_type counters into one 64-bit lockless atomic
IdSetMeasurements(size_type capacity)
size_type nodeCount() const
the total number of nodes at all levels
IdSetNavigationDirection ascendDirection() const
which direction is this position from our parent node
IdSetPosition()=default
root node position
IdSet::size_type size_type
IdSet::size_type level
the number of levels above us (e.g., zero for the root node)
IdSet::size_type offset
the number of nodes (at our level) to the left of us
bool atRoot() const
whether we are at the top of the tree
a shareable set of positive uint32_t IDs with O(1) insertion/removal ops
size_type leafPop(Position)
extracts and returns an ID from the leaf node at the given position
void leafTruncate(Position pos, size_type idsToKeep)
fill the leaf node at a given position with 0s, leaving only idsToKeep IDs
StoredNode & nodeAt(Position)
void truncateExtras()
effectively removes IDs that exceed the requested capacity after makeFull()
size_type innerTruncate(Position pos, NavigationDirection dir, size_type toSubtract)
void makeFullBeforeSharing()
void innerPush(Position, NavigationDirection)
accounts for an ID added to subtree in the given dir from the given position
uint64_t Node
either leaf or intermediate node
static size_t MemorySize(size_type capacity)
memory size required to store a tree with the given capacity
Position ascend(Position)
Node * valueAddress(Position)
void push(size_type id)
makes id value available to future pop() callers
NavigationDirection innerPop(Position)
IdSet(size_type capacity)
std::atomic< Node > StoredNode
a Node stored in shared memory
Position descend(Position, NavigationDirection)
void leafPush(Position, size_type id)
adds the given ID to the leaf node at the given position
IdSetMeasurements::size_type size_type
Shared memory page identifier, address, or handler.
uint32_t number
page number within the segment
PageStack construction and SharedMemorySize calculation parameters.
PageCount capacity
the maximum number of pages
size_t pageSize
page size, used to calculate shared memory size
bool createFull
whether a newly created PageStack should be prefilled with PageIds
IdSet ids_
free pages (valid with positive capacity_)
static size_t LevelsPaddingSize(const PageCount capacity)
static size_t SharedMemorySize(const Config &)
total shared memory size required to share
bool pageIdIsValid(const PageId &page) const
std::atomic< size_t > Levels_t
std::atomic< PageCount > size_
a lower bound for the number of free pages (for debugging purposes)
PageStack(const Config &)
unsigned int PageCount
the number of (free and/or used) pages in a stack
size_t sharedMemorySize() const
static size_t StackSize(const PageCount capacity)
bool pop(PageId &page)
sets value and returns true unless no free page numbers are found
void push(PageId &page)
makes value available as a free page number to future pop() callers
#define debugs(SECTION, LEVEL, CONTENT)
static const IdSet::size_type BitsPerLeaf
the maximum number of pages that a leaf node can store