OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-cogutil commits / 41c1168

Simplify the barrier code to use std::latch
41c11684f08913af8ace8d0fea065ec0274b8aa6
by Linas Vepstas <linasvepstas@gmail.com> · 2025-12-07T04:31:33.000Z

Changed files (2) +21 -49

opencog/util/async_buffer.h +11 -25
……@@ -27,6 +27,7 @@
2727
2828#include <atomic>
2929#include <chrono>
30#include <latch>
3031#include <mutex>
3132#include <thread>
3233#include <vector>
……@@ -143,7 +144,7 @@ class async_buffer
143144
144145 // Per-thread barrier synchronization
145146 std::atomic<unsigned> _barrier_phase;
146 std::atomic<unsigned> _barrier_remaining;
147 std::latch* _barrier_latch;
147148
148149 void start_writer_thread();
149150 void stop_writer_threads();
……@@ -217,7 +218,7 @@ async_buffer<Writer, Element>::async_buffer(Writer* wr,
217218 _stall_writers = false;
218219 _in_drain = false;
219220 _barrier_phase = 0;
220 _barrier_remaining = 0;
221 _barrier_latch = nullptr;
221222
222223 _high_watermark = DEFAULT_HIGH_WATER_MARK;
223224 _low_watermark = DEFAULT_LOW_WATER_MARK;
……@@ -358,7 +359,7 @@ void async_buffer<Writer, Element>::stop_writer_threads()
358359 }
359360
360361 _barrier_phase = 0;
361 _barrier_remaining = 0;
362 _barrier_latch = nullptr;
362363
363364 // Its now OK to start new threads, if desired ...(!)
364365 _stopping_writers = false;
……@@ -452,21 +453,18 @@ void async_buffer<Writer, Element>::barrier(const Element& elt)
452453 // First, drain existing work
453454 drain();
454455
455 // Set up the barrier: each thread must process exactly one element
456 _barrier_remaining = _thread_count;
456 // Set up the barrier using std::latch for synchronization
457 std::latch completion(_thread_count);
458 _barrier_latch = &completion;
457459 _barrier_phase++;
458 _barrier_remaining.notify_all(); // Wake workers checking phase condition
459460
460461 // Enqueue one element for each thread.
461462 for (unsigned int i = 0; i < _thread_count; i++)
462463 do_insert(elt);
463464
464465 // Wait for all threads to complete their barrier work
465 while (_barrier_remaining.load() > 0)
466 {
467 unsigned rem = _barrier_remaining.load();
468 if (rem > 0) _barrier_remaining.wait(rem);
469 }
466 completion.wait();
467 _barrier_latch = nullptr;
470468}
471469
472470/// A single write thread. Reads elements from set, and invokes the
……@@ -482,17 +480,6 @@ void async_buffer<Writer, Element>::write_loop()
482480 {
483481 while (true)
484482 {
485 // If a barrier is active and we've done our part, wait for others.
486 // This prevents us from grabbing a second barrier element before
487 // all other threads have grabbed their first.
488 while (_barrier_remaining.load() > 0 and
489 my_last_phase >= _barrier_phase.load())
490 {
491 unsigned rem = _barrier_remaining.load();
492 if (rem > 0 and my_last_phase >= _barrier_phase.load())
493 _barrier_remaining.wait(rem);
494 }
495
496483 // Do nothing, if asked to stall.
497484 while (_stall_writers and _store_set.size() < _low_watermark)
498485 {
……@@ -505,11 +492,10 @@ void async_buffer<Writer, Element>::write_loop()
505492 unsigned current_phase = _barrier_phase.load();
506493 if (my_last_phase < current_phase)
507494 {
508 // This is barrier work - process and count toward completion
495 // This is barrier work - process and signal completion
509496 my_last_phase = current_phase;
510497 (_writer->*_do_write)(elt);
511 _barrier_remaining.fetch_sub(1);
512 _barrier_remaining.notify_all();
498 _barrier_latch->count_down();
513499 }
514500 else
515501 {
opencog/util/async_method_caller.h +10 -24
……@@ -27,6 +27,7 @@
2727
2828#include <atomic>
2929#include <chrono>
30#include <latch>
3031#include <mutex>
3132#include <thread>
3233#include <vector>
……@@ -116,7 +117,7 @@ class async_caller
116117
117118 // Per-thread barrier synchronization
118119 std::atomic<unsigned> _barrier_phase;
119 std::atomic<unsigned> _barrier_remaining;
120 std::latch* _barrier_latch;
120121
121122 void start_writer_thread();
122123 void stop_writer_threads();
……@@ -180,7 +181,7 @@ async_caller<Writer, Element>::async_caller(Writer* wr,
180181 _pending = 0;
181182 _in_drain = false;
182183 _barrier_phase = 0;
183 _barrier_remaining = 0;
184 _barrier_latch = nullptr;
184185
185186 _high_watermark = DEFAULT_HIGH_WATER_MARK;
186187 _low_watermark = DEFAULT_LOW_WATER_MARK;
……@@ -278,7 +279,7 @@ void async_caller<Writer, Element>::stop_writer_threads()
278279 }
279280
280281 _barrier_phase = 0;
281 _barrier_remaining = 0;
282 _barrier_latch = nullptr;
282283
283284 // Its now OK to start new threads, if desired ...(!)
284285 _stopping_writers = false;
……@@ -366,10 +367,10 @@ void async_caller<Writer, Element>::barrier(const Element& elt)
366367 // First, drain existing work
367368 drain();
368369
369 // Set up the barrier: each thread must process exactly one element
370 _barrier_remaining = _thread_count;
370 // Set up the latch for barrier completion
371 std::latch completion(_thread_count);
372 _barrier_latch = &completion;
371373 _barrier_phase++;
372 _barrier_remaining.notify_all(); // Wake workers checking phase condition
373374
374375 // Enqueue one element for each thread.
375376 for (unsigned int i = 0; i < _thread_count; i++)
……@@ -380,11 +381,8 @@ void async_caller<Writer, Element>::barrier(const Element& elt)
380381 }
381382
382383 // Wait for all threads to complete their barrier work
383 while (_barrier_remaining.load() > 0)
384 {
385 unsigned rem = _barrier_remaining.load();
386 if (rem > 0) _barrier_remaining.wait(rem);
387 }
384 completion.wait();
385 _barrier_latch = nullptr;
388386}
389387
390388/// A single write thread. Reads elements from queue, and invokes the
……@@ -400,17 +398,6 @@ void async_caller<Writer, Element>::write_loop()
400398 {
401399 while (true)
402400 {
403 // If a barrier is active and we've done our part, wait for others.
404 // This prevents us from grabbing a second barrier element before
405 // all other threads have grabbed their first.
406 while (_barrier_remaining.load() > 0 and
407 my_last_phase >= _barrier_phase.load())
408 {
409 unsigned rem = _barrier_remaining.load();
410 if (rem > 0 and my_last_phase >= _barrier_phase.load())
411 _barrier_remaining.wait(rem);
412 }
413
414401 Element elt = _store_queue.value_pop();
415402 _busy_writers ++;
416403
……@@ -420,8 +407,7 @@ void async_caller<Writer, Element>::write_loop()
420407 // This is barrier work - process and count toward completion
421408 my_last_phase = current_phase;
422409 (_writer->*_do_write)(elt);
423 _barrier_remaining.fetch_sub(1);
424 _barrier_remaining.notify_all();
410 _barrier_latch->count_down();
425411 }
426412 else
427413 {