mirror of
https://github.com/rdmsr/zag.git
synced 2026-11-02 08:08:21 +00:00
rcu/hashtable: more comments
This commit is contained in:
+17
-1
@@ -1,6 +1,7 @@
|
|||||||
//! Implementation of a scalable, lock-free hash table.
|
//! Implementation of a scalable, lock-free hash table.
|
||||||
//! The table is lock-free for read operations and has fine-grained locking on
|
//! The table is lock-free for read operations and has fine-grained locking on
|
||||||
//! the buckets for write operations.
|
//! the buckets for write operations.
|
||||||
|
//! The general design is inspired by XNU's smr_shash.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const rtl = @import("rtl");
|
const rtl = @import("rtl");
|
||||||
@@ -236,6 +237,7 @@ const Core = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called in worker thread context when the table needs rehashing.
|
||||||
fn rehash(ptr: ?*anyopaque) void {
|
fn rehash(ptr: ?*anyopaque) void {
|
||||||
const self: *Self = @ptrCast(@alignCast(ptr.?));
|
const self: *Self = @ptrCast(@alignCast(ptr.?));
|
||||||
|
|
||||||
@@ -246,12 +248,21 @@ const Core = struct {
|
|||||||
const state = self.state.load(.monotonic);
|
const state = self.state.load(.monotonic);
|
||||||
const size = size_for_shift(state.current_shift);
|
const size = size_for_shift(state.current_shift);
|
||||||
|
|
||||||
if (self.should_grow(elems, size) or self.should_shrink(elems, size)) {
|
// Growing and shrinking are both under the same 'rehashing_resize'
|
||||||
|
// operation, as this function figures out itself whether or not
|
||||||
|
// it should grow or shrink. We still need a marker to catch
|
||||||
|
// a pending resize operation however (by failing the CAS).
|
||||||
|
const grow = self.should_grow(elems, size);
|
||||||
|
const shrink = self.should_shrink(elems, size);
|
||||||
|
|
||||||
|
if (grow or shrink) {
|
||||||
self.resize(self.target_shift_for(elems));
|
self.resize(self.target_shift_for(elems));
|
||||||
} else if (reason & rehashing_reseed != 0) {
|
} else if (reason & rehashing_reseed != 0) {
|
||||||
self.resize(state.current_shift);
|
self.resize(state.current_shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we loop again, this means that someone enqueued a new rehash
|
||||||
|
// operation, so work on that.
|
||||||
_ = self.rehashing_state.cmpxchgStrong(
|
_ = self.rehashing_state.cmpxchgStrong(
|
||||||
rehashing_running,
|
rehashing_running,
|
||||||
rehashing_none,
|
rehashing_none,
|
||||||
@@ -261,6 +272,7 @@ const Core = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resize (and re-seed) the hash table to a given target shift.
|
||||||
fn resize(self: *Self, new_shift: u5) void {
|
fn resize(self: *Self, new_shift: u5) void {
|
||||||
var state = self.state.load(.monotonic);
|
var state = self.state.load(.monotonic);
|
||||||
state.new_shift = new_shift;
|
state.new_shift = new_shift;
|
||||||
@@ -392,6 +404,10 @@ const Core = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find the appropriate shift for the current number of items.
|
||||||
|
/// This avoids going through multiple steps when resizing a busy table,
|
||||||
|
/// i.e instead of doubling the size and doing 16 -> 32 -> 64,
|
||||||
|
/// we can just do 16 -> 64 in a single resize.
|
||||||
fn target_shift_for(self: *Self, elems: u32) u5 {
|
fn target_shift_for(self: *Self, elems: u32) u5 {
|
||||||
const depth = ResizePolicy.depth_for(self.policy);
|
const depth = ResizePolicy.depth_for(self.policy);
|
||||||
const needed = std.math.divCeil(usize, elems, depth) catch unreachable;
|
const needed = std.math.divCeil(usize, elems, depth) catch unreachable;
|
||||||
|
|||||||
Reference in New Issue
Block a user