2020-06-22 11:07:34 +02:00
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
2019-07-30 15:06:16 +02:00
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module sync
2020-05-10 07:58:54 +02:00
2021-02-18 08:02:56 +01:00
[ trusted ]
fn C . atomic_fetch_add_u32 ( voidptr , u32 ) u32
2021-07-27 14:49:51 +02:00
[ trusted ]
fn C . atomic_load_u32 ( voidptr ) u32
[ trusted ]
fn C . atomic_compare_exchange_weak_u32 ( voidptr , voidptr , u32 ) bool
2021-02-13 13:52:27 +01:00
// WaitGroup
2020-06-22 11:07:34 +02:00
// Do not copy an instance of WaitGroup, use a ref instead.
//
2021-02-13 13:52:27 +01:00
// usage: in main thread:
// `wg := sync.new_waitgroup()
// `wg.add(nr_jobs)` before starting jobs with `go ...`
// `wg.wait()` to wait for all jobs to have finished
//
// in each parallel job:
2021-05-08 12:32:29 +02:00
// `wg.done()` when finished
2020-06-22 11:07:34 +02:00
//
2019-12-21 23:41:42 +01:00
// [init_with=new_waitgroup] // TODO: implement support for init_with struct attribute, and disallow WaitGroup{} from outside the sync.new_waitgroup() function.
2021-02-13 15:52:01 +01:00
[ heap ]
2022-06-19 16:42:22 +02:00
pub struct WaitGroup {
2019-07-30 15:06:16 +02:00
mut :
2021-05-08 12:32:29 +02:00
task_count u32 // current task count - reading/writing should be atomic
2021-07-27 14:49:51 +02:00
wait_count u32 // current wait count - reading/writing should be atomic
2021-02-13 13:52:27 +01:00
sem Semaphore // This blocks wait() until tast_countreleased by add()
2019-07-30 15:06:16 +02:00
}
2020-01-19 20:32:22 +01:00
pub fn new_waitgroup ( ) & WaitGroup {
2021-06-14 17:12:47 +02:00
mut wg := WaitGroup { }
2021-02-13 13:52:27 +01:00
wg . init ( )
2021-06-14 17:12:47 +02:00
return & wg
2019-10-25 16:24:40 +02:00
}
2021-02-13 13:52:27 +01:00
pub fn ( mut wg WaitGroup ) init ( ) {
wg . sem . init ( 0 )
}
2020-06-22 11:07:34 +02:00
// add increments (+ve delta) or decrements (-ve delta) task count by delta
// and unblocks any wait() calls if task count becomes zero.
// add panics if task count drops below zero.
2020-05-17 13:51:18 +02:00
pub fn ( mut wg WaitGroup ) add ( delta int ) {
2021-02-13 13:52:27 +01:00
old_nrjobs := int ( C . atomic_fetch_add_u32 ( & wg . task_count , u32 ( delta ) ) )
new_nrjobs := old_nrjobs + delta
2021-07-27 14:49:51 +02:00
mut num_waiters := C . atomic_load_u32 ( & wg . wait_count )
2021-02-13 13:52:27 +01:00
if new_nrjobs < 0 {
2019-08-29 10:48:03 +02:00
panic ( ' N e g a t i v e n u m b e r o f j o b s i n w a i t g r o u p ' )
}
2021-07-27 14:49:51 +02:00
if new_nrjobs == 0 && num_waiters > 0 {
// clear waiters
for ! C . atomic_compare_exchange_weak_u32 ( & wg . wait_count , & num_waiters , 0 ) {
if num_waiters == 0 {
return
}
}
for ( num_waiters > 0 ) {
wg . sem . post ( )
num_waiters --
}
2020-06-22 11:07:34 +02:00
}
2019-07-30 15:06:16 +02:00
}
2020-06-22 11:07:34 +02:00
// done is a convenience fn for add(-1)
2020-05-17 13:51:18 +02:00
pub fn ( mut wg WaitGroup ) done ( ) {
2019-08-29 10:48:03 +02:00
wg . add ( - 1 )
2019-07-30 15:06:16 +02:00
}
2020-06-22 11:07:34 +02:00
// wait blocks until all tasks are done (task count becomes zero)
pub fn ( mut wg WaitGroup ) wait ( ) {
2021-07-27 14:49:51 +02:00
nrjobs := int ( C . atomic_load_u32 ( & wg . task_count ) )
if nrjobs == 0 {
// no need to wait
return
}
C . atomic_fetch_add_u32 ( & wg . wait_count , 1 )
2021-02-13 13:52:27 +01:00
wg . sem . wait ( ) // blocks until task_count becomes 0
2019-07-30 15:06:16 +02:00
}