@@ -104,8 +104,8 @@ const Op = union(enum) {
104
104
/// graph as /operations/ enqueued to a lock-free Multi Producer, Single Consumer (MPSC) FIFO queue.
105
105
///
106
106
/// When an operation is desired (adding a parent to a child, querying the children or parent of a
107
- /// node, etc.) it is enqueued. Then, if the queue contains entries, that thread becomes the
108
- /// consumer of the MPSC queue temporarily and processes all pending operations in the queue .
107
+ /// node, etc.) it is enqueued. Then, a background thread processes all pending operations. Atomics
108
+ /// are used to wait for reads to complete, and parallel writes are lock-free .
109
109
///
110
110
/// The graph uses lock-free pools to manage all nodes internally, eliminating runtime allocations
111
111
/// during operation processing.
@@ -130,7 +130,15 @@ pub const Graph = struct {
130
130
131
131
preallocate_result_list_size : u32 ,
132
132
133
+ /// Thread that processes operations from the queue
134
+ thread : ? std.Thread = null ,
135
+
136
+ /// Flag to signal the processing thread to stop
137
+ should_stop : std .atomic .Value (bool ) = .init (false ),
138
+
133
139
/// Initialize the graph with the given pre-allocated space for nodes and operations.
140
+ ///
141
+ /// Spawns a backgroound thread for processing operations to the graph.
134
142
pub fn init (
135
143
graph : * Graph ,
136
144
allocator : std.mem.Allocator ,
@@ -173,9 +181,13 @@ pub const Graph = struct {
173
181
try list .ensureTotalCapacity (allocator , preallocate .result_list_size );
174
182
try graph .result_lists .available .append (allocator , list );
175
183
}
184
+
185
+ graph .thread = try std .Thread .spawn (.{ .allocator = allocator }, processThread , .{ graph , allocator });
176
186
}
177
187
178
188
pub fn deinit (graph : * Graph , allocator : std.mem.Allocator ) void {
189
+ graph .should_stop .store (true , .release );
190
+ graph .thread .? .join ();
179
191
for (graph .result_lists .available .items ) | list | {
180
192
list .deinit (allocator );
181
193
allocator .destroy (list );
@@ -193,20 +205,12 @@ pub const Graph = struct {
193
205
return graph .id_to_node .map .get (id );
194
206
}
195
207
196
- /// Tries to take all queued operations to the graph and, if successful, processes them.
197
- ///
198
- /// A different thread which calls processQueue() may beat us to acquiring all of the queued
199
- /// operations, in which case this function may return before they are processed.
200
- fn processQueue (graph : * Graph , allocator : std.mem.Allocator ) void {
201
- if (graph .queue .takeAll ()) | nodes | {
202
- defer graph .queue .releaseAll (nodes );
203
-
204
- // Process the entire chain of nodes
205
- var current : ? * Queue (Op ).Node = nodes ;
206
- while (current ) | node | {
207
- graph .processOp (allocator , node .value );
208
- current = node .next ;
209
- }
208
+ /// The thread that runs continuously in the background to process queue submissions.
209
+ fn processThread (graph : * Graph , allocator : std.mem.Allocator ) void {
210
+ while (! graph .should_stop .load (.acquire )) {
211
+ // Process the entire queue
212
+ while (graph .queue .pop ()) | op | graph .processOp (allocator , op );
213
+ std .Thread .yield () catch {};
210
214
}
211
215
}
212
216
@@ -316,7 +320,6 @@ pub const Graph = struct {
316
320
.parent_id = parent_id ,
317
321
.child_id = child_id ,
318
322
} });
319
- graph .processQueue (allocator );
320
323
}
321
324
322
325
pub fn removeChild (graph : * Graph , allocator : std.mem.Allocator , parent_id : u64 , child_id : u64 ) Error ! void {
@@ -325,7 +328,6 @@ pub const Graph = struct {
325
328
.parent_id = parent_id ,
326
329
.child_id = child_id ,
327
330
} });
328
- graph .processQueue (allocator );
329
331
}
330
332
331
333
pub fn setParent (graph : * Graph , allocator : std.mem.Allocator , child_id : u64 , parent_id : u64 ) Error ! void {
@@ -335,14 +337,12 @@ pub const Graph = struct {
335
337
.child_id = child_id ,
336
338
.parent_id = parent_id ,
337
339
} });
338
- graph .processQueue (allocator );
339
340
}
340
341
341
342
pub fn removeParent (graph : * Graph , allocator : std.mem.Allocator , child_id : u64 ) Error ! void {
342
343
try graph .queue .push (allocator , .{ .remove_parent = .{
343
344
.child_id = child_id ,
344
345
} });
345
- graph .processQueue (allocator );
346
346
}
347
347
348
348
const Results = struct {
@@ -374,7 +374,6 @@ pub const Graph = struct {
374
374
} });
375
375
376
376
while (! done .load (.acquire )) {
377
- graph .processQueue (allocator );
378
377
std .Thread .yield () catch {};
379
378
}
380
379
@@ -429,7 +428,6 @@ pub const Graph = struct {
429
428
} });
430
429
431
430
while (! done .load (.acquire )) {
432
- graph .processQueue (allocator );
433
431
std .Thread .yield () catch {};
434
432
}
435
433
0 commit comments