2020-06-22 12:07:34 +03:00
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
2019-07-30 16:06:16 +03: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 08:58:54 +03:00
2021-02-18 10:02:56 +03:00
[ trusted ]
fn C . atomic_fetch_add_u32 ( voidptr , u32 ) u32
2021-07-27 15:49:51 +03:00
[ trusted ]
fn C . atomic_load_u32 ( voidptr ) u32
[ trusted ]
fn C . atomic_compare_exchange_weak_u32 ( voidptr , voidptr , u32 ) bool
2021-02-13 15:52:27 +03:00
// WaitGroup
2020-06-22 12:07:34 +03:00
// Do not copy an instance of WaitGroup, use a ref instead.
//
2021-02-13 15:52:27 +03: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 13:32:29 +03:00
// `wg.done()` when finished
2020-06-22 12:07:34 +03:00
//
2019-12-22 01:41:42 +03: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 17:52:01 +03:00
[ heap ]
2022-06-19 17:42:22 +03:00
pub struct WaitGroup {
2019-07-30 16:06:16 +03:00
mut :
2021-05-08 13:32:29 +03:00
task_count u32 // current task count - reading/writing should be atomic
2021-07-27 15:49:51 +03:00
wait_count u32 // current wait count - reading/writing should be atomic
2021-02-13 15:52:27 +03:00
sem Semaphore // This blocks wait() until tast_countreleased by add()
2019-07-30 16:06:16 +03:00
}
2020-01-19 22:32:22 +03:00
pub fn new_waitgroup ( ) & WaitGroup {
2021-06-14 18:12:47 +03:00
mut wg := WaitGroup { }
2021-02-13 15:52:27 +03:00
wg . init ( )
2021-06-14 18:12:47 +03:00
return & wg
2019-10-25 17:24:40 +03:00
}
2021-02-13 15:52:27 +03:00
pub fn ( mut wg WaitGroup ) init ( ) {
wg . sem . init ( 0 )
}
2020-06-22 12:07:34 +03: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 14:51:18 +03:00
pub fn ( mut wg WaitGroup ) add ( delta int ) {
2021-02-13 15:52:27 +03:00
old_nrjobs := int ( C . atomic_fetch_add_u32 ( & wg . task_count , u32 ( delta ) ) )
new_nrjobs := old_nrjobs + delta
2021-07-27 15:49:51 +03:00
mut num_waiters := C . atomic_load_u32 ( & wg . wait_count )
2021-02-13 15:52:27 +03:00
if new_nrjobs < 0 {
2019-08-29 11:48:03 +03: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 15:49:51 +03: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 12:07:34 +03:00
}
2019-07-30 16:06:16 +03:00
}
2020-06-22 12:07:34 +03:00
// done is a convenience fn for add(-1)
2020-05-17 14:51:18 +03:00
pub fn ( mut wg WaitGroup ) done ( ) {
2019-08-29 11:48:03 +03:00
wg . add ( - 1 )
2019-07-30 16:06:16 +03:00
}
2020-06-22 12:07:34 +03:00
// wait blocks until all tasks are done (task count becomes zero)
pub fn ( mut wg WaitGroup ) wait ( ) {
2021-07-27 15:49:51 +03: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 15:52:27 +03:00
wg . sem . wait ( ) // blocks until task_count becomes 0
2019-07-30 16:06:16 +03:00
}