@shd101wyy/yo 0.1.30 → 0.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,6 +23,7 @@ pragma(Pragma.AllowUnsafe);
23
23
  { malloc, free } :: GlobalAllocator;
24
24
  { Mutex } :: import("./mutex");
25
25
  { Cond } :: import("./cond");
26
+ { AtomicBool, AtomicUsize, MemoryOrder } :: import("./atomic");
26
27
  /// Thread-safe bounded channel for passing values between threads.
27
28
  /// Uses atomic reference counting — implements `Send` and can be
28
29
  /// safely shared across threads without `Arc` wrapping.
@@ -32,12 +33,12 @@ Channel :: (fn(comptime(T) : Type, where(T <: Send)) -> comptime(Type))(
32
33
  object(
33
34
  _data : *(T),
34
35
  _head : usize,
35
- _len : usize,
36
+ _len : AtomicUsize,
36
37
  _capacity : usize,
37
- _mutex : Mutex,
38
+ _mutex : Mutex(bool),
38
39
  _not_empty : Cond,
39
40
  _not_full : Cond,
40
- _closed : bool
41
+ _closed : AtomicBool
41
42
  )
42
43
  )
43
44
  );
@@ -54,12 +55,12 @@ impl(
54
55
  Self(
55
56
  _data : ptr,
56
57
  _head : usize(0),
57
- _len : usize(0),
58
+ _len : AtomicUsize(usize(0)),
58
59
  _capacity : capacity,
59
- _mutex : Mutex.new(),
60
+ _mutex : Mutex(bool).new(false),
60
61
  _not_empty : Cond.new(),
61
62
  _not_full : Cond.new(),
62
- _closed : false
63
+ _closed : AtomicBool(false)
63
64
  )
64
65
  );
65
66
  }),
@@ -67,38 +68,45 @@ impl(
67
68
  /// Blocks if the channel is full until space becomes available.
68
69
  /// Returns .Ok(()) on success, .Err(()) if the channel is closed.
69
70
  send : (fn(self : Self, value : T) -> Result(unit, unit))({
70
- self._mutex.lock();
71
+ self._mutex._raw_lock();
71
72
  // Wait while buffer is full and channel is not closed
72
- while(runtime((self._len >= self._capacity) && (!(self._closed))), {
73
- self._not_full.wait(self._mutex);
73
+ while(runtime(
74
+ (self._len.load(MemoryOrder.Relaxed) >= self._capacity)
75
+ && (!(self._closed.load(MemoryOrder.Relaxed)))
76
+ ), {
77
+ self._not_full.wait(self._mutex._raw_handle_ptr());
74
78
  });
75
79
  cond(
76
- self._closed => {
77
- self._mutex.unlock();
80
+ self._closed.load(MemoryOrder.Relaxed) => {
81
+ self._mutex._raw_unlock();
78
82
  return(.Err(()));
79
83
  },
80
84
  true => ()
81
85
  );
82
86
  // Write value at tail position
83
- (tail : usize) = ((self._head + self._len) % self._capacity);
87
+ (current_len : usize) = self._len.load(MemoryOrder.Relaxed);
88
+ (tail : usize) = ((self._head + current_len) % self._capacity);
84
89
  consume((self._data &+ tail).* = value);
85
- self._len = (self._len + usize(1));
90
+ self._len.store(current_len + usize(1), MemoryOrder.Release);
86
91
  self._not_empty.signal();
87
- self._mutex.unlock();
92
+ self._mutex._raw_unlock();
88
93
  return(.Ok(()));
89
94
  }),
90
95
  /// Receive a value from the channel.
91
96
  /// Blocks if the channel is empty until a value is available.
92
97
  /// Returns .Some(value) on success, .None if the channel is closed and empty.
93
98
  recv : (fn(self : Self) -> ?(T))({
94
- self._mutex.lock();
99
+ self._mutex._raw_lock();
95
100
  // Wait while buffer is empty and channel is not closed
96
- while(runtime((self._len == usize(0)) && (!(self._closed))), {
97
- self._not_empty.wait(self._mutex);
101
+ while(runtime(
102
+ (self._len.load(MemoryOrder.Relaxed) == usize(0))
103
+ && (!(self._closed.load(MemoryOrder.Relaxed)))
104
+ ), {
105
+ self._not_empty.wait(self._mutex._raw_handle_ptr());
98
106
  });
99
107
  cond(
100
- (self._len == usize(0)) => {
101
- self._mutex.unlock();
108
+ (self._len.load(MemoryOrder.Relaxed) == usize(0)) => {
109
+ self._mutex._raw_unlock();
102
110
  return(.None);
103
111
  },
104
112
  true => ()
@@ -108,36 +116,40 @@ impl(
108
116
  (val : T) = element_ptr.*;
109
117
  unsafe.drop(element_ptr.*);
110
118
  self._head = ((self._head + usize(1)) % self._capacity);
111
- self._len = (self._len - usize(1));
119
+ self._len.store(self._len.load(MemoryOrder.Relaxed) - usize(1), MemoryOrder.Release);
112
120
  self._not_full.signal();
113
- self._mutex.unlock();
121
+ self._mutex._raw_unlock();
114
122
  return(.Some(val));
115
123
  }),
116
124
  /// Try to send a value without blocking.
117
125
  /// Returns .Ok(()) if sent, .Err(()) if full or closed.
118
126
  try_send : (fn(self : Self, value : T) -> Result(unit, unit))({
119
- self._mutex.lock();
127
+ self._mutex._raw_lock();
120
128
  cond(
121
- (self._closed || (self._len >= self._capacity)) => {
122
- self._mutex.unlock();
129
+ (
130
+ self._closed.load(MemoryOrder.Relaxed)
131
+ || (self._len.load(MemoryOrder.Relaxed) >= self._capacity)
132
+ ) => {
133
+ self._mutex._raw_unlock();
123
134
  return(.Err(()));
124
135
  },
125
136
  true => ()
126
137
  );
127
- (tail : usize) = ((self._head + self._len) % self._capacity);
138
+ (current_len : usize) = self._len.load(MemoryOrder.Relaxed);
139
+ (tail : usize) = ((self._head + current_len) % self._capacity);
128
140
  consume((self._data &+ tail).* = value);
129
- self._len = (self._len + usize(1));
141
+ self._len.store(current_len + usize(1), MemoryOrder.Release);
130
142
  self._not_empty.signal();
131
- self._mutex.unlock();
143
+ self._mutex._raw_unlock();
132
144
  return(.Ok(()));
133
145
  }),
134
146
  /// Try to receive a value without blocking.
135
147
  /// Returns .Some(value) if available, .None if empty.
136
148
  try_recv : (fn(self : Self) -> ?(T))({
137
- self._mutex.lock();
149
+ self._mutex._raw_lock();
138
150
  cond(
139
- (self._len == usize(0)) => {
140
- self._mutex.unlock();
151
+ (self._len.load(MemoryOrder.Relaxed) == usize(0)) => {
152
+ self._mutex._raw_unlock();
141
153
  return(.None);
142
154
  },
143
155
  true => ()
@@ -146,32 +158,34 @@ impl(
146
158
  (val : T) = element_ptr.*;
147
159
  unsafe.drop(element_ptr.*);
148
160
  self._head = ((self._head + usize(1)) % self._capacity);
149
- self._len = (self._len - usize(1));
161
+ self._len.store(self._len.load(MemoryOrder.Relaxed) - usize(1), MemoryOrder.Release);
150
162
  self._not_full.signal();
151
- self._mutex.unlock();
163
+ self._mutex._raw_unlock();
152
164
  return(.Some(val));
153
165
  }),
154
166
  /// Close the channel. No more values can be sent.
155
167
  /// Pending recv() calls will return .None once the buffer is drained.
156
168
  /// Pending send() calls will return .Err(()).
157
169
  close : (fn(self : Self) -> unit)({
158
- self._mutex.lock();
159
- self._closed = true;
170
+ self._mutex._raw_lock();
171
+ self._closed.store(true, MemoryOrder.Release);
160
172
  self._not_empty.broadcast();
161
173
  self._not_full.broadcast();
162
- self._mutex.unlock();
174
+ self._mutex._raw_unlock();
163
175
  }),
164
- /// Check if the channel is closed.
176
+ /// Check if the channel is closed (lock-free atomic load).
165
177
  is_closed : (fn(self : Self) -> bool)(
166
- self._closed
178
+ self._closed.load(MemoryOrder.Acquire)
167
179
  ),
168
- /// Return the number of values currently buffered.
180
+ /// Return the number of values currently buffered (lock-free atomic load).
181
+ /// Snapshot semantics: the value may be stale by the time the caller acts on it,
182
+ /// matching Rust's `mpsc::channel` idiom.
169
183
  len : (fn(self : Self) -> usize)(
170
- self._len
184
+ self._len.load(MemoryOrder.Acquire)
171
185
  ),
172
- /// Check if the channel buffer is empty.
186
+ /// Check if the channel buffer is empty (lock-free atomic load).
173
187
  is_empty : (fn(self : Self) -> bool)(
174
- self._len == usize(0)
188
+ self._len.load(MemoryOrder.Acquire) == usize(0)
175
189
  )
176
190
  );
177
191
  // Dispose: drop remaining elements and free the buffer
@@ -181,8 +195,9 @@ impl(
181
195
  where(T <: Send),
182
196
  Dispose(
183
197
  dispose : (fn(self : Self) -> unit)({
198
+ (current_len : usize) = self._len.load(MemoryOrder.Relaxed);
184
199
  (i : usize) = usize(0);
185
- while(runtime(i < self._len), {
200
+ while(runtime(i < current_len), {
186
201
  (idx : usize) = ((self._head + i) % self._capacity);
187
202
  unsafe.drop((self._data &+ idx).*);
188
203
  i = (i + usize(1));
package/std/sync/cond.yo CHANGED
@@ -1,6 +1,6 @@
1
1
  //! Condition variable for thread synchronization.
2
2
  pragma(Pragma.AllowUnsafe);
3
- { __YO_THREAD_SYNC_TYPE, mutex_t, Mutex } :: import("./mutex");
3
+ { __YO_THREAD_SYNC_TYPE, mutex_t } :: import("./mutex");
4
4
  extern(
5
5
  "Yo",
6
6
  __YO_COND_TYPE : Type,
@@ -10,6 +10,10 @@ extern(
10
10
  __yo_cond_broadcast : (fn(cv : *(__YO_COND_TYPE)) -> unit),
11
11
  __yo_cond_destroy : (fn(cv : *(__YO_COND_TYPE)) -> unit)
12
12
  );
13
+ // SAFETY: __YO_COND_TYPE is an opaque C-level condition variable
14
+ // (pthread_cond_t or equivalent). It carries its own internal
15
+ // synchronization and is designed to be shared across threads. It is
16
+ // trivially Acyclic — opaque C types don't participate in ARC.
13
17
  impl(__YO_COND_TYPE, Send());
14
18
  impl(__YO_COND_TYPE, Acyclic());
15
19
  /// Low-level condition variable (manual lifetime via `destroy`).
@@ -46,8 +50,8 @@ impl(
46
50
  new : (fn() -> Self)({
47
51
  return(Self(__yo_cond_create()));
48
52
  }),
49
- wait : (fn(self : Self, mutex : Mutex) -> unit)({
50
- return(__yo_cond_wait(&(self.cv), &(mutex.mutex)));
53
+ wait : (fn(self : Self, handle : *(__YO_THREAD_SYNC_TYPE)) -> unit)({
54
+ return(__yo_cond_wait(&(self.cv), handle));
51
55
  }),
52
56
  signal : (fn(self : Self) -> unit)({
53
57
  return(__yo_cond_signal(&(self.cv)));
package/std/sync/mutex.yo CHANGED
@@ -1,5 +1,12 @@
1
1
  //! Mutual exclusion lock primitives.
2
+ //!
3
+ //! Phase D (THREAD_SAFETY): Mutex parameterized over the protected data.
4
+ //! Access is granted via `with_lock(closure)` — the closure receives a
5
+ //! `ref(v): T` that is second-class (cannot escape the closure scope).
6
+ //! The private `__MutexUnlocker` handles unlock on both normal return
7
+ //! and unwind via its `Drop`.
2
8
  pragma(Pragma.AllowUnsafe);
9
+
3
10
  extern(
4
11
  "Yo",
5
12
  __YO_THREAD_SYNC_TYPE : Type,
@@ -8,8 +15,13 @@ extern(
8
15
  __yo_mutex_unlock : (fn(mutex : *(__YO_THREAD_SYNC_TYPE)) -> unit),
9
16
  __yo_mutex_destroy : (fn(mutex : *(__YO_THREAD_SYNC_TYPE)) -> unit)
10
17
  );
18
+ // SAFETY: __YO_THREAD_SYNC_TYPE is an opaque C-level synchronization
19
+ // primitive (pthread_mutex_t or equivalent). It carries its own internal
20
+ // synchronization and is designed to be shared across threads. It is
21
+ // trivially Acyclic — opaque C types don't participate in ARC.
11
22
  impl(__YO_THREAD_SYNC_TYPE, Send());
12
23
  impl(__YO_THREAD_SYNC_TYPE, Acyclic());
24
+
13
25
  /// Low-level mutex (manual lifetime via `destroy`).
14
26
  mutex_t :: newtype(
15
27
  mutex : __YO_THREAD_SYNC_TYPE
@@ -29,35 +41,83 @@ impl(
29
41
  return(__yo_mutex_destroy(&(self.mutex)));
30
42
  })
31
43
  );
32
- /// Reference-counted mutex with automatic cleanup via `Dispose`.
33
- /// Uses atomic reference counting for safe cross-thread sharing.
34
- Mutex :: atomic(
44
+
45
+ // ============================================================================
46
+ // Mutex(T) atomic-object mutex wrapper with closure-scoped lock API
47
+ // ============================================================================
48
+ Mutex :: (fn(comptime(T) : Type, where(T <: Send)) -> comptime(Type))(
49
+ atomic(
50
+ object(
51
+ _handle : __YO_THREAD_SYNC_TYPE,
52
+ _value : T
53
+ )
54
+ )
55
+ );
56
+
57
+ // __MutexUnlocker — private object that calls _raw_unlock on Drop.
58
+ // Allocated locally inside with_lock; Yo's drop-on-scope-exit and
59
+ // drop-on-unwind machinery guarantees unlock under both normal return
60
+ // and unwind(...).
61
+ __MutexUnlocker :: (fn(comptime(T) : Type, where(T <: Send)) -> comptime(Type))(
35
62
  object(
36
- mutex : __YO_THREAD_SYNC_TYPE
63
+ _mutex : Mutex(T)
37
64
  )
38
65
  );
39
66
  impl(
40
- Mutex,
41
- new : (fn() -> Self)({
42
- return(Self(__yo_mutex_create()));
67
+ forall(T : Type),
68
+ where(T <: Send),
69
+ __MutexUnlocker(T),
70
+ Dispose(
71
+ dispose : (fn(self : Self) -> unit)({
72
+ self._mutex._raw_unlock();
73
+ })
74
+ )
75
+ );
76
+
77
+ impl(
78
+ forall(T : Type),
79
+ where(T <: Send),
80
+ Mutex(T),
81
+ new : (fn(value : T) -> Self)({
82
+ return(Self(__yo_mutex_create(), value));
43
83
  }),
44
- lock : (fn(self : Self) -> unit)({
45
- return(__yo_mutex_lock(&(self.mutex)));
84
+ with_lock : (
85
+ fn(
86
+ forall(R : Type),
87
+ self : Self,
88
+ body : Impl(Fn(ref(v) : T) -> R)
89
+ ) -> R
90
+ )({
91
+ self._raw_lock();
92
+ _guard := __MutexUnlocker(T)(self);
93
+ body(self._value)
46
94
  }),
47
- unlock : (fn(self : Self) -> unit)({
48
- return(__yo_mutex_unlock(&(self.mutex)));
49
- })
95
+ // Internal methods pragma-only. These are public to the module system
96
+ // but the _-prefixed naming convention signals "internal API".
97
+ _raw_lock : (fn(self : Self) -> unit)(
98
+ __yo_mutex_lock(&(self._handle))
99
+ ),
100
+ _raw_unlock : (fn(self : Self) -> unit)(
101
+ __yo_mutex_unlock(&(self._handle))
102
+ ),
103
+ _raw_handle_ptr : (fn(ref(self) : Self) -> *(__YO_THREAD_SYNC_TYPE))(
104
+ &(self._handle)
105
+ )
50
106
  );
51
107
  impl(
52
- Mutex,
108
+ forall(T : Type),
109
+ where(T <: Send),
110
+ Mutex(T),
53
111
  Dispose(
54
112
  dispose : (fn(self : Self) -> unit)({
55
- return(__yo_mutex_destroy(&(self.mutex)));
113
+ return(__yo_mutex_destroy(&(self._handle)));
56
114
  })
57
115
  )
58
116
  );
117
+
59
118
  export(
60
119
  __YO_THREAD_SYNC_TYPE,
61
120
  mutex_t,
62
- Mutex
121
+ Mutex,
122
+ __MutexUnlocker
63
123
  );
package/std/sync/once.yo CHANGED
@@ -1,4 +1,5 @@
1
1
  //! One-time initialization primitive.
2
+ pragma(Pragma.AllowUnsafe);
2
3
  //!
3
4
  //! # Example
4
5
  //!
@@ -15,43 +16,48 @@
15
16
  //! });
16
17
  //! ```
17
18
  { Mutex } :: import("./mutex");
19
+ { AtomicBool, MemoryOrder } :: import("./atomic");
20
+ {
21
+ atomic_bool,
22
+ atomic_load_explicit,
23
+ atomic_store_explicit,
24
+ memory_order_relaxed,
25
+ memory_order_acquire,
26
+ memory_order_release
27
+ } :: import("std/libc/stdatomic");
18
28
  /// Execute a function exactly once, thread-safely.
19
29
  /// Uses atomic reference counting for safe cross-thread sharing.
20
30
  Once :: atomic(
21
31
  object(
22
- _done : bool,
23
- _mutex : Mutex
32
+ _done : atomic_bool,
33
+ _mutex : Mutex(bool)
24
34
  )
25
35
  );
26
36
  impl(
27
37
  Once,
28
38
  // Create a new Once.
29
- new : (fn() -> Self)(
30
- Self(
31
- _done : false,
32
- _mutex : Mutex.new()
33
- )
34
- ),
39
+ new : (fn() -> Self)({
40
+ v := Self(false, Mutex(bool).new(false));
41
+ unsafe(atomic_store_explicit(&(v._done), false, memory_order_release));
42
+ v
43
+ }),
35
44
  // Execute the function exactly once.
36
45
  // Subsequent calls are no-ops.
37
- // Note: The fast-path read of _done is not strictly atomic, but since _done
38
- // is only ever set from false to true and the double-check inside the lock
39
- // prevents the function from running twice, this is safe in practice.
40
46
  call : (fn(self : Self, f : Impl(Fn() -> unit)) -> unit)({
41
- // Fast path: already done
47
+ // Fast path: already done (atomic_bool with Acquire load)
42
48
  cond(
43
- self._done => (),
49
+ (unsafe(atomic_load_explicit(&(self._done), memory_order_acquire)) == true) => (),
44
50
  true => {
45
- self._mutex.lock();
51
+ self._mutex._raw_lock();
46
52
  // Double-check after acquiring lock
47
53
  cond(
48
- self._done => {
49
- self._mutex.unlock();
54
+ (unsafe(atomic_load_explicit(&(self._done), memory_order_relaxed)) == true) => {
55
+ self._mutex._raw_unlock();
50
56
  },
51
57
  true => {
52
58
  f();
53
- self._done = true;
54
- self._mutex.unlock();
59
+ unsafe(atomic_store_explicit(&(self._done), true, memory_order_release));
60
+ self._mutex._raw_unlock();
55
61
  }
56
62
  );
57
63
  }
@@ -59,7 +65,7 @@ impl(
59
65
  }),
60
66
  // Check if the function has been called.
61
67
  is_done : (fn(self : Self) -> bool)(
62
- self._done
68
+ unsafe(atomic_load_explicit(&(self._done), memory_order_acquire)) == true
63
69
  )
64
70
  );
65
71
  export(Once);
@@ -1,4 +1,5 @@
1
1
  //! Reader-writer lock allowing multiple concurrent readers or one exclusive writer.
2
+ pragma(Pragma.AllowUnsafe);
2
3
  //!
3
4
  //! # Example
4
5
  //!
@@ -24,7 +25,7 @@ RwLock :: atomic(
24
25
  object(
25
26
  _readers : i32,
26
27
  _writer : bool,
27
- _mutex : Mutex,
28
+ _mutex : Mutex(bool),
28
29
  _read_cv : Cond,
29
30
  _write_cv : Cond
30
31
  )
@@ -36,7 +37,7 @@ impl(
36
37
  Self(
37
38
  _readers : i32(0),
38
39
  _writer : false,
39
- _mutex : Mutex.new(),
40
+ _mutex : Mutex(bool).new(false),
40
41
  _read_cv : Cond.new(),
41
42
  _write_cv : Cond.new()
42
43
  )
@@ -44,39 +45,41 @@ impl(
44
45
  // Acquire a read lock. Blocks if a writer holds the lock.
45
46
  // Multiple readers can hold the lock simultaneously.
46
47
  read_lock : (fn(self : Self) -> unit)({
47
- self._mutex.lock();
48
+ self._mutex._raw_lock();
48
49
  while(runtime(self._writer), {
49
- self._read_cv.wait(self._mutex);
50
+ self._read_cv.wait(self._mutex._raw_handle_ptr());
50
51
  });
51
52
  self._readers = (self._readers + i32(1));
52
- self._mutex.unlock();
53
+ self._mutex._raw_unlock();
53
54
  }),
54
55
  // Release a read lock.
55
56
  read_unlock : (fn(self : Self) -> unit)({
56
- self._mutex.lock();
57
+ self._mutex._raw_lock();
57
58
  self._readers = (self._readers - i32(1));
58
59
  cond(
59
60
  (self._readers == i32(0)) => self._write_cv.signal(),
60
61
  true => ()
61
62
  );
62
- self._mutex.unlock();
63
+ self._mutex._raw_unlock();
63
64
  }),
64
- // Acquire a write lock. Blocks until all readers and writers release.
65
+ // Acquire a write lock. Blocks if readers or a writer hold the lock.
65
66
  write_lock : (fn(self : Self) -> unit)({
66
- self._mutex.lock();
67
+ self._mutex._raw_lock();
67
68
  while(runtime(self._writer || (self._readers > i32(0))), {
68
- self._write_cv.wait(self._mutex);
69
+ self._write_cv.wait(self._mutex._raw_handle_ptr());
69
70
  });
70
71
  self._writer = true;
71
- self._mutex.unlock();
72
+ self._mutex._raw_unlock();
72
73
  }),
73
74
  // Release a write lock.
74
75
  write_unlock : (fn(self : Self) -> unit)({
75
- self._mutex.lock();
76
+ self._mutex._raw_lock();
76
77
  self._writer = false;
77
- self._read_cv.broadcast();
78
- self._write_cv.signal();
79
- self._mutex.unlock();
78
+ cond(
79
+ (self._readers > i32(0)) => self._read_cv.broadcast(),
80
+ true => self._write_cv.signal()
81
+ );
82
+ self._mutex._raw_unlock();
80
83
  })
81
84
  );
82
85
  export(RwLock);
@@ -1,4 +1,5 @@
1
1
  //! Wait for a group of tasks to complete, similar to Go's `sync.WaitGroup`.
2
+ pragma(Pragma.AllowUnsafe);
2
3
  //!
3
4
  //! # Example
4
5
  //!
@@ -20,12 +21,18 @@
20
21
  //! ```
21
22
  { Mutex } :: import("./mutex");
22
23
  { Cond } :: import("./cond");
24
+ { AtomicI32, MemoryOrder } :: import("./atomic");
23
25
  /// Synchronization primitive that blocks until a counter reaches zero.
24
26
  /// Uses atomic reference counting for safe cross-thread sharing.
27
+ ///
28
+ /// The internal counter is an atomic so `count()` can read without
29
+ /// acquiring the mutex (matches Rust's mpsc query-method idiom). Writes
30
+ /// to the counter still happen under the mutex so the `_count == 0`
31
+ /// observation that triggers `broadcast()` is serialized with `wait()`.
25
32
  WaitGroup :: atomic(
26
33
  object(
27
- _count : i32,
28
- _mutex : Mutex,
34
+ _count : AtomicI32,
35
+ _mutex : Mutex(bool),
29
36
  _cv : Cond
30
37
  )
31
38
  );
@@ -34,24 +41,26 @@ impl(
34
41
  // Create a new WaitGroup with count 0.
35
42
  new : (fn() -> Self)(
36
43
  Self(
37
- _count : i32(0),
38
- _mutex : Mutex.new(),
44
+ _count : AtomicI32(i32(0)),
45
+ _mutex : Mutex(bool).new(false),
39
46
  _cv : Cond.new()
40
47
  )
41
48
  ),
42
49
  // Add delta to the counter (can be negative).
43
50
  // If the counter becomes zero, all waiters are woken.
44
51
  add : (fn(self : Self, delta : i32) -> unit)({
45
- self._mutex.lock();
46
- self._count = (self._count + delta);
52
+ self._mutex._raw_lock();
53
+ new_count := (self._count.load(MemoryOrder.Relaxed) + delta);
47
54
  cond(
48
- (self._count <= i32(0)) => {
49
- self._count = i32(0);
55
+ (new_count <= i32(0)) => {
56
+ self._count.store(i32(0), MemoryOrder.Release);
50
57
  self._cv.broadcast();
51
58
  },
52
- true => ()
59
+ true => {
60
+ self._count.store(new_count, MemoryOrder.Release);
61
+ }
53
62
  );
54
- self._mutex.unlock();
63
+ self._mutex._raw_unlock();
55
64
  }),
56
65
  // Decrement the counter by one (equivalent to add(-1)).
57
66
  done : (fn(self : Self) -> unit)(
@@ -59,15 +68,15 @@ impl(
59
68
  ),
60
69
  // Block until the counter reaches zero.
61
70
  wait : (fn(self : Self) -> unit)({
62
- self._mutex.lock();
63
- while(runtime(self._count > i32(0)), {
64
- self._cv.wait(self._mutex);
71
+ self._mutex._raw_lock();
72
+ while(runtime(self._count.load(MemoryOrder.Acquire) > i32(0)), {
73
+ self._cv.wait(self._mutex._raw_handle_ptr());
65
74
  });
66
- self._mutex.unlock();
75
+ self._mutex._raw_unlock();
67
76
  }),
68
- // Get the current count (for debugging).
77
+ // Get the current count (lock-free atomic load).
69
78
  count : (fn(self : Self) -> i32)(
70
- self._count
79
+ self._count.load(MemoryOrder.Acquire)
71
80
  )
72
81
  );
73
82
  export(WaitGroup);