@shd101wyy/yo 0.1.29 → 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.
- package/.github/skills/yo-async-effects/SKILL.md +3 -3
- package/.github/skills/yo-async-effects/async-effects-recipes.md +19 -11
- package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +33 -13
- package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +1 -1
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +59 -21
- package/README.md +4 -3
- package/out/cjs/index.cjs +964 -710
- package/out/cjs/yo-cli.cjs +1119 -855
- package/out/cjs/yo-lsp.cjs +1003 -749
- package/out/esm/index.mjs +964 -710
- package/out/types/src/codegen/exprs/async.d.ts +2 -0
- package/out/types/src/codegen/exprs/await.d.ts +1 -0
- package/out/types/src/codegen/exprs/closures.d.ts +4 -0
- package/out/types/src/codegen/functions/context.d.ts +6 -0
- package/out/types/src/codegen/index.d.ts +1 -1
- package/out/types/src/compiler-utils.d.ts +1 -1
- package/out/types/src/env.d.ts +2 -0
- package/out/types/src/evaluator/builtins/pragma.d.ts +9 -0
- package/out/types/src/evaluator/builtins/unsafe.d.ts +8 -0
- package/out/types/src/evaluator/context.d.ts +2 -0
- package/out/types/src/evaluator/index.d.ts +1 -1
- package/out/types/src/evaluator/memory-safety.d.ts +14 -0
- package/out/types/src/evaluator/types/flowability.d.ts +6 -0
- package/out/types/src/evaluator/utils/closure.d.ts +2 -1
- package/out/types/src/evaluator/utils.d.ts +3 -0
- package/out/types/src/evaluator/values/impl.d.ts +14 -0
- package/out/types/src/expr-traversal.d.ts +1 -0
- package/out/types/src/expr.d.ts +4 -1
- package/out/types/src/public-safe-report.d.ts +19 -0
- package/out/types/src/tests/comptime-ref-gate.test.d.ts +1 -0
- package/out/types/src/tests/pragma-validation.test.d.ts +1 -0
- package/out/types/src/tests/public-safe-report.test.d.ts +1 -0
- package/out/types/src/tests/thread-safety-codegen.test.d.ts +1 -0
- package/out/types/src/tests/type-representation-pointer.test.d.ts +1 -0
- package/out/types/src/tests/unsafe-gate.test.d.ts +1 -0
- package/out/types/src/tests/unsafe-report-classify.test.d.ts +1 -0
- package/out/types/src/types/definitions.d.ts +2 -0
- package/out/types/src/types/utils.d.ts +4 -0
- package/out/types/src/unsafe-report.d.ts +29 -0
- package/out/types/src/value.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/scripts/add-pragma-for-pointer-decls.ts +134 -0
- package/scripts/add-pragma.ts +58 -0
- package/scripts/migrate-amp-method-calls.ts +186 -0
- package/scripts/migrate-clone-calls.ts +93 -0
- package/scripts/migrate-get-unwrap.ts +166 -0
- package/scripts/migrate-index-patterns.ts +210 -0
- package/scripts/migrate-index-trait.ts +142 -0
- package/scripts/migrate-iterator.ts +150 -0
- package/scripts/migrate-self-ptr.ts +220 -0
- package/scripts/migrate-skip-pragmas.ts +109 -0
- package/scripts/migrate-tostring.ts +134 -0
- package/scripts/trim-pragma.ts +130 -0
- package/scripts/wrap-extern-calls.ts +161 -0
- package/std/alg/hash.yo +3 -2
- package/std/allocator.yo +6 -5
- package/std/async.yo +2 -2
- package/std/build.yo +5 -2
- package/std/collections/array_list.yo +59 -40
- package/std/collections/btree_map.yo +19 -18
- package/std/collections/deque.yo +9 -8
- package/std/collections/hash_map.yo +101 -13
- package/std/collections/hash_set.yo +5 -4
- package/std/collections/linked_list.yo +39 -4
- package/std/collections/ordered_map.yo +3 -3
- package/std/collections/priority_queue.yo +14 -13
- package/std/crypto/md5.yo +2 -1
- package/std/crypto/random.yo +16 -15
- package/std/crypto/sha256.yo +2 -1
- package/std/encoding/base64.yo +14 -14
- package/std/encoding/hex.yo +3 -3
- package/std/encoding/json.yo +59 -10
- package/std/encoding/punycode.yo +24 -23
- package/std/encoding/toml.yo +4 -3
- package/std/encoding/utf16.yo +2 -2
- package/std/env.yo +43 -28
- package/std/error.yo +6 -6
- package/std/fmt/display.yo +2 -2
- package/std/fmt/index.yo +6 -5
- package/std/fmt/to_string.yo +39 -38
- package/std/fmt/writer.yo +9 -8
- package/std/fs/dir.yo +34 -33
- package/std/fs/file.yo +52 -51
- package/std/fs/metadata.yo +10 -9
- package/std/fs/temp.yo +24 -13
- package/std/fs/walker.yo +10 -9
- package/std/gc.yo +1 -0
- package/std/glob.yo +7 -7
- package/std/http/client.yo +15 -14
- package/std/http/http.yo +6 -6
- package/std/http/index.yo +1 -1
- package/std/imm/list.yo +34 -1
- package/std/imm/map.yo +2 -1
- package/std/imm/set.yo +1 -0
- package/std/imm/sorted_map.yo +2 -1
- package/std/imm/sorted_set.yo +1 -0
- package/std/imm/string.yo +27 -23
- package/std/imm/vec.yo +18 -2
- package/std/io/reader.yo +2 -1
- package/std/io/writer.yo +3 -2
- package/std/libc/assert.yo +1 -0
- package/std/libc/ctype.yo +1 -0
- package/std/libc/dirent.yo +1 -0
- package/std/libc/errno.yo +1 -0
- package/std/libc/fcntl.yo +1 -0
- package/std/libc/float.yo +1 -0
- package/std/libc/limits.yo +1 -0
- package/std/libc/math.yo +1 -0
- package/std/libc/signal.yo +1 -0
- package/std/libc/stdatomic.yo +251 -1
- package/std/libc/stdint.yo +1 -0
- package/std/libc/stdio.yo +1 -0
- package/std/libc/stdlib.yo +1 -0
- package/std/libc/string.yo +1 -0
- package/std/libc/sys/stat.yo +1 -0
- package/std/libc/time.yo +1 -0
- package/std/libc/unistd.yo +1 -0
- package/std/libc/wctype.yo +1 -0
- package/std/libc/windows.yo +2 -0
- package/std/log.yo +7 -6
- package/std/net/addr.yo +5 -4
- package/std/net/dns.yo +7 -6
- package/std/net/errors.yo +8 -8
- package/std/net/tcp.yo +19 -18
- package/std/net/udp.yo +13 -12
- package/std/os/signal.yo +3 -3
- package/std/path.yo +1 -0
- package/std/prelude.yo +398 -184
- package/std/process/command.yo +40 -23
- package/std/process/index.yo +2 -1
- package/std/regex/compiler.yo +10 -9
- package/std/regex/index.yo +41 -41
- package/std/regex/match.yo +2 -2
- package/std/regex/parser.yo +21 -21
- package/std/regex/vm.yo +42 -41
- package/std/string/rune.yo +4 -0
- package/std/string/string.yo +95 -40
- package/std/string/string_builder.yo +9 -9
- package/std/string/unicode.yo +50 -49
- package/std/sync/atomic.yo +557 -0
- package/std/sync/channel.yo +59 -43
- package/std/sync/cond.yo +12 -7
- package/std/sync/mutex.yo +79 -18
- package/std/sync/once.yo +25 -19
- package/std/sync/rwlock.yo +18 -15
- package/std/sync/waitgroup.yo +25 -16
- package/std/sys/advise.yo +1 -0
- package/std/sys/bufio/buf_reader.yo +17 -16
- package/std/sys/bufio/buf_writer.yo +10 -9
- package/std/sys/clock.yo +1 -0
- package/std/sys/copy.yo +1 -0
- package/std/sys/dir.yo +10 -9
- package/std/sys/dns.yo +6 -5
- package/std/sys/errors.yo +11 -11
- package/std/sys/events.yo +1 -0
- package/std/sys/externs.yo +38 -37
- package/std/sys/file.yo +17 -16
- package/std/sys/future.yo +4 -3
- package/std/sys/iov.yo +1 -0
- package/std/sys/mmap.yo +1 -0
- package/std/sys/path.yo +1 -0
- package/std/sys/perm.yo +2 -1
- package/std/sys/pipe.yo +1 -0
- package/std/sys/process.yo +5 -4
- package/std/sys/signal.yo +1 -0
- package/std/sys/socketpair.yo +1 -0
- package/std/sys/sockinfo.yo +1 -0
- package/std/sys/statfs.yo +2 -1
- package/std/sys/statx.yo +1 -0
- package/std/sys/sysinfo.yo +1 -0
- package/std/sys/tcp.yo +15 -14
- package/std/sys/temp.yo +1 -0
- package/std/sys/time.yo +2 -1
- package/std/sys/timer.yo +6 -6
- package/std/sys/tty.yo +2 -1
- package/std/sys/udp.yo +13 -12
- package/std/sys/unix.yo +12 -11
- package/std/testing/bench.yo +4 -3
- package/std/thread.yo +7 -6
- package/std/time/datetime.yo +18 -15
- package/std/time/duration.yo +11 -10
- package/std/time/instant.yo +4 -4
- package/std/time/sleep.yo +1 -0
- package/std/url/index.yo +3 -3
- package/std/worker.yo +4 -3
package/std/sync/channel.yo
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//! Bounded multi-producer multi-consumer (MPMC) channel.
|
|
2
|
-
//! Uses blocking send/recv via condition variables — does not require
|
|
2
|
+
//! Uses blocking send/recv via condition variables — does not require Io.
|
|
3
3
|
//!
|
|
4
4
|
//! Channel uses `atomic object` with atomic reference counting, so it can be
|
|
5
5
|
//! safely shared across threads without `Arc` wrapping.
|
|
@@ -18,10 +18,12 @@
|
|
|
18
18
|
//! assert((val.unwrap() == i32(42)), "received value");
|
|
19
19
|
//! t.join();
|
|
20
20
|
//! ```
|
|
21
|
+
pragma(Pragma.AllowUnsafe);
|
|
21
22
|
{ GlobalAllocator } :: import("../allocator");
|
|
22
23
|
{ malloc, free } :: GlobalAllocator;
|
|
23
24
|
{ Mutex } :: import("./mutex");
|
|
24
25
|
{ Cond } :: import("./cond");
|
|
26
|
+
{ AtomicBool, AtomicUsize, MemoryOrder } :: import("./atomic");
|
|
25
27
|
/// Thread-safe bounded channel for passing values between threads.
|
|
26
28
|
/// Uses atomic reference counting — implements `Send` and can be
|
|
27
29
|
/// safely shared across threads without `Arc` wrapping.
|
|
@@ -31,12 +33,12 @@ Channel :: (fn(comptime(T) : Type, where(T <: Send)) -> comptime(Type))(
|
|
|
31
33
|
object(
|
|
32
34
|
_data : *(T),
|
|
33
35
|
_head : usize,
|
|
34
|
-
_len :
|
|
36
|
+
_len : AtomicUsize,
|
|
35
37
|
_capacity : usize,
|
|
36
|
-
_mutex : Mutex,
|
|
38
|
+
_mutex : Mutex(bool),
|
|
37
39
|
_not_empty : Cond,
|
|
38
40
|
_not_full : Cond,
|
|
39
|
-
_closed :
|
|
41
|
+
_closed : AtomicBool
|
|
40
42
|
)
|
|
41
43
|
)
|
|
42
44
|
);
|
|
@@ -53,12 +55,12 @@ impl(
|
|
|
53
55
|
Self(
|
|
54
56
|
_data : ptr,
|
|
55
57
|
_head : usize(0),
|
|
56
|
-
_len : usize(0),
|
|
58
|
+
_len : AtomicUsize(usize(0)),
|
|
57
59
|
_capacity : capacity,
|
|
58
|
-
_mutex : Mutex.new(),
|
|
60
|
+
_mutex : Mutex(bool).new(false),
|
|
59
61
|
_not_empty : Cond.new(),
|
|
60
62
|
_not_full : Cond.new(),
|
|
61
|
-
_closed : false
|
|
63
|
+
_closed : AtomicBool(false)
|
|
62
64
|
)
|
|
63
65
|
);
|
|
64
66
|
}),
|
|
@@ -66,38 +68,45 @@ impl(
|
|
|
66
68
|
/// Blocks if the channel is full until space becomes available.
|
|
67
69
|
/// Returns .Ok(()) on success, .Err(()) if the channel is closed.
|
|
68
70
|
send : (fn(self : Self, value : T) -> Result(unit, unit))({
|
|
69
|
-
self._mutex.
|
|
71
|
+
self._mutex._raw_lock();
|
|
70
72
|
// Wait while buffer is full and channel is not closed
|
|
71
|
-
while(runtime(
|
|
72
|
-
self.
|
|
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());
|
|
73
78
|
});
|
|
74
79
|
cond(
|
|
75
|
-
self._closed => {
|
|
76
|
-
self._mutex.
|
|
80
|
+
self._closed.load(MemoryOrder.Relaxed) => {
|
|
81
|
+
self._mutex._raw_unlock();
|
|
77
82
|
return(.Err(()));
|
|
78
83
|
},
|
|
79
84
|
true => ()
|
|
80
85
|
);
|
|
81
86
|
// Write value at tail position
|
|
82
|
-
(
|
|
87
|
+
(current_len : usize) = self._len.load(MemoryOrder.Relaxed);
|
|
88
|
+
(tail : usize) = ((self._head + current_len) % self._capacity);
|
|
83
89
|
consume((self._data &+ tail).* = value);
|
|
84
|
-
self._len
|
|
90
|
+
self._len.store(current_len + usize(1), MemoryOrder.Release);
|
|
85
91
|
self._not_empty.signal();
|
|
86
|
-
self._mutex.
|
|
92
|
+
self._mutex._raw_unlock();
|
|
87
93
|
return(.Ok(()));
|
|
88
94
|
}),
|
|
89
95
|
/// Receive a value from the channel.
|
|
90
96
|
/// Blocks if the channel is empty until a value is available.
|
|
91
97
|
/// Returns .Some(value) on success, .None if the channel is closed and empty.
|
|
92
98
|
recv : (fn(self : Self) -> ?(T))({
|
|
93
|
-
self._mutex.
|
|
99
|
+
self._mutex._raw_lock();
|
|
94
100
|
// Wait while buffer is empty and channel is not closed
|
|
95
|
-
while(runtime(
|
|
96
|
-
self.
|
|
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());
|
|
97
106
|
});
|
|
98
107
|
cond(
|
|
99
|
-
(self._len == usize(0)) => {
|
|
100
|
-
self._mutex.
|
|
108
|
+
(self._len.load(MemoryOrder.Relaxed) == usize(0)) => {
|
|
109
|
+
self._mutex._raw_unlock();
|
|
101
110
|
return(.None);
|
|
102
111
|
},
|
|
103
112
|
true => ()
|
|
@@ -107,36 +116,40 @@ impl(
|
|
|
107
116
|
(val : T) = element_ptr.*;
|
|
108
117
|
unsafe.drop(element_ptr.*);
|
|
109
118
|
self._head = ((self._head + usize(1)) % self._capacity);
|
|
110
|
-
self._len
|
|
119
|
+
self._len.store(self._len.load(MemoryOrder.Relaxed) - usize(1), MemoryOrder.Release);
|
|
111
120
|
self._not_full.signal();
|
|
112
|
-
self._mutex.
|
|
121
|
+
self._mutex._raw_unlock();
|
|
113
122
|
return(.Some(val));
|
|
114
123
|
}),
|
|
115
124
|
/// Try to send a value without blocking.
|
|
116
125
|
/// Returns .Ok(()) if sent, .Err(()) if full or closed.
|
|
117
126
|
try_send : (fn(self : Self, value : T) -> Result(unit, unit))({
|
|
118
|
-
self._mutex.
|
|
127
|
+
self._mutex._raw_lock();
|
|
119
128
|
cond(
|
|
120
|
-
(
|
|
121
|
-
self.
|
|
129
|
+
(
|
|
130
|
+
self._closed.load(MemoryOrder.Relaxed)
|
|
131
|
+
|| (self._len.load(MemoryOrder.Relaxed) >= self._capacity)
|
|
132
|
+
) => {
|
|
133
|
+
self._mutex._raw_unlock();
|
|
122
134
|
return(.Err(()));
|
|
123
135
|
},
|
|
124
136
|
true => ()
|
|
125
137
|
);
|
|
126
|
-
(
|
|
138
|
+
(current_len : usize) = self._len.load(MemoryOrder.Relaxed);
|
|
139
|
+
(tail : usize) = ((self._head + current_len) % self._capacity);
|
|
127
140
|
consume((self._data &+ tail).* = value);
|
|
128
|
-
self._len
|
|
141
|
+
self._len.store(current_len + usize(1), MemoryOrder.Release);
|
|
129
142
|
self._not_empty.signal();
|
|
130
|
-
self._mutex.
|
|
143
|
+
self._mutex._raw_unlock();
|
|
131
144
|
return(.Ok(()));
|
|
132
145
|
}),
|
|
133
146
|
/// Try to receive a value without blocking.
|
|
134
147
|
/// Returns .Some(value) if available, .None if empty.
|
|
135
148
|
try_recv : (fn(self : Self) -> ?(T))({
|
|
136
|
-
self._mutex.
|
|
149
|
+
self._mutex._raw_lock();
|
|
137
150
|
cond(
|
|
138
|
-
(self._len == usize(0)) => {
|
|
139
|
-
self._mutex.
|
|
151
|
+
(self._len.load(MemoryOrder.Relaxed) == usize(0)) => {
|
|
152
|
+
self._mutex._raw_unlock();
|
|
140
153
|
return(.None);
|
|
141
154
|
},
|
|
142
155
|
true => ()
|
|
@@ -145,32 +158,34 @@ impl(
|
|
|
145
158
|
(val : T) = element_ptr.*;
|
|
146
159
|
unsafe.drop(element_ptr.*);
|
|
147
160
|
self._head = ((self._head + usize(1)) % self._capacity);
|
|
148
|
-
self._len
|
|
161
|
+
self._len.store(self._len.load(MemoryOrder.Relaxed) - usize(1), MemoryOrder.Release);
|
|
149
162
|
self._not_full.signal();
|
|
150
|
-
self._mutex.
|
|
163
|
+
self._mutex._raw_unlock();
|
|
151
164
|
return(.Some(val));
|
|
152
165
|
}),
|
|
153
166
|
/// Close the channel. No more values can be sent.
|
|
154
167
|
/// Pending recv() calls will return .None once the buffer is drained.
|
|
155
168
|
/// Pending send() calls will return .Err(()).
|
|
156
169
|
close : (fn(self : Self) -> unit)({
|
|
157
|
-
self._mutex.
|
|
158
|
-
self._closed
|
|
170
|
+
self._mutex._raw_lock();
|
|
171
|
+
self._closed.store(true, MemoryOrder.Release);
|
|
159
172
|
self._not_empty.broadcast();
|
|
160
173
|
self._not_full.broadcast();
|
|
161
|
-
self._mutex.
|
|
174
|
+
self._mutex._raw_unlock();
|
|
162
175
|
}),
|
|
163
|
-
/// Check if the channel is closed.
|
|
176
|
+
/// Check if the channel is closed (lock-free atomic load).
|
|
164
177
|
is_closed : (fn(self : Self) -> bool)(
|
|
165
|
-
self._closed
|
|
178
|
+
self._closed.load(MemoryOrder.Acquire)
|
|
166
179
|
),
|
|
167
|
-
/// 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.
|
|
168
183
|
len : (fn(self : Self) -> usize)(
|
|
169
|
-
self._len
|
|
184
|
+
self._len.load(MemoryOrder.Acquire)
|
|
170
185
|
),
|
|
171
|
-
/// Check if the channel buffer is empty.
|
|
186
|
+
/// Check if the channel buffer is empty (lock-free atomic load).
|
|
172
187
|
is_empty : (fn(self : Self) -> bool)(
|
|
173
|
-
self._len == usize(0)
|
|
188
|
+
self._len.load(MemoryOrder.Acquire) == usize(0)
|
|
174
189
|
)
|
|
175
190
|
);
|
|
176
191
|
// Dispose: drop remaining elements and free the buffer
|
|
@@ -180,8 +195,9 @@ impl(
|
|
|
180
195
|
where(T <: Send),
|
|
181
196
|
Dispose(
|
|
182
197
|
dispose : (fn(self : Self) -> unit)({
|
|
198
|
+
(current_len : usize) = self._len.load(MemoryOrder.Relaxed);
|
|
183
199
|
(i : usize) = usize(0);
|
|
184
|
-
while(runtime(i <
|
|
200
|
+
while(runtime(i < current_len), {
|
|
185
201
|
(idx : usize) = ((self._head + i) % self._capacity);
|
|
186
202
|
unsafe.drop((self._data &+ idx).*);
|
|
187
203
|
i = (i + usize(1));
|
package/std/sync/cond.yo
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
//! Condition variable for thread synchronization.
|
|
2
|
-
|
|
2
|
+
pragma(Pragma.AllowUnsafe);
|
|
3
|
+
{ __YO_THREAD_SYNC_TYPE, mutex_t } :: import("./mutex");
|
|
3
4
|
extern(
|
|
4
5
|
"Yo",
|
|
5
6
|
__YO_COND_TYPE : Type,
|
|
@@ -9,6 +10,10 @@ extern(
|
|
|
9
10
|
__yo_cond_broadcast : (fn(cv : *(__YO_COND_TYPE)) -> unit),
|
|
10
11
|
__yo_cond_destroy : (fn(cv : *(__YO_COND_TYPE)) -> unit)
|
|
11
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.
|
|
12
17
|
impl(__YO_COND_TYPE, Send());
|
|
13
18
|
impl(__YO_COND_TYPE, Acyclic());
|
|
14
19
|
/// Low-level condition variable (manual lifetime via `destroy`).
|
|
@@ -20,16 +25,16 @@ impl(
|
|
|
20
25
|
new : (fn() -> Self)({
|
|
21
26
|
return(Self(__yo_cond_create()));
|
|
22
27
|
}),
|
|
23
|
-
wait : (fn(self :
|
|
28
|
+
wait : (fn(ref(self) : Self, ref(mutex) : mutex_t) -> unit)({
|
|
24
29
|
return(__yo_cond_wait(&(self.cv), &(mutex.mutex)));
|
|
25
30
|
}),
|
|
26
|
-
signal : (fn(self :
|
|
31
|
+
signal : (fn(ref(self) : Self) -> unit)({
|
|
27
32
|
return(__yo_cond_signal(&(self.cv)));
|
|
28
33
|
}),
|
|
29
|
-
broadcast : (fn(self :
|
|
34
|
+
broadcast : (fn(ref(self) : Self) -> unit)({
|
|
30
35
|
return(__yo_cond_broadcast(&(self.cv)));
|
|
31
36
|
}),
|
|
32
|
-
destroy : (fn(self :
|
|
37
|
+
destroy : (fn(ref(self) : Self) -> unit)({
|
|
33
38
|
return(__yo_cond_destroy(&(self.cv)));
|
|
34
39
|
})
|
|
35
40
|
);
|
|
@@ -45,8 +50,8 @@ impl(
|
|
|
45
50
|
new : (fn() -> Self)({
|
|
46
51
|
return(Self(__yo_cond_create()));
|
|
47
52
|
}),
|
|
48
|
-
wait : (fn(self : Self,
|
|
49
|
-
return(__yo_cond_wait(&(self.cv),
|
|
53
|
+
wait : (fn(self : Self, handle : *(__YO_THREAD_SYNC_TYPE)) -> unit)({
|
|
54
|
+
return(__yo_cond_wait(&(self.cv), handle));
|
|
50
55
|
}),
|
|
51
56
|
signal : (fn(self : Self) -> unit)({
|
|
52
57
|
return(__yo_cond_signal(&(self.cv)));
|
package/std/sync/mutex.yo
CHANGED
|
@@ -1,4 +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`.
|
|
8
|
+
pragma(Pragma.AllowUnsafe);
|
|
9
|
+
|
|
2
10
|
extern(
|
|
3
11
|
"Yo",
|
|
4
12
|
__YO_THREAD_SYNC_TYPE : Type,
|
|
@@ -7,8 +15,13 @@ extern(
|
|
|
7
15
|
__yo_mutex_unlock : (fn(mutex : *(__YO_THREAD_SYNC_TYPE)) -> unit),
|
|
8
16
|
__yo_mutex_destroy : (fn(mutex : *(__YO_THREAD_SYNC_TYPE)) -> unit)
|
|
9
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.
|
|
10
22
|
impl(__YO_THREAD_SYNC_TYPE, Send());
|
|
11
23
|
impl(__YO_THREAD_SYNC_TYPE, Acyclic());
|
|
24
|
+
|
|
12
25
|
/// Low-level mutex (manual lifetime via `destroy`).
|
|
13
26
|
mutex_t :: newtype(
|
|
14
27
|
mutex : __YO_THREAD_SYNC_TYPE
|
|
@@ -18,45 +31,93 @@ impl(
|
|
|
18
31
|
new : (fn() -> Self)({
|
|
19
32
|
return(Self(__yo_mutex_create()));
|
|
20
33
|
}),
|
|
21
|
-
lock : (fn(self :
|
|
34
|
+
lock : (fn(ref(self) : Self) -> unit)({
|
|
22
35
|
return(__yo_mutex_lock(&(self.mutex)));
|
|
23
36
|
}),
|
|
24
|
-
unlock : (fn(self :
|
|
37
|
+
unlock : (fn(ref(self) : Self) -> unit)({
|
|
25
38
|
return(__yo_mutex_unlock(&(self.mutex)));
|
|
26
39
|
}),
|
|
27
|
-
destroy : (fn(self :
|
|
40
|
+
destroy : (fn(ref(self) : Self) -> unit)({
|
|
28
41
|
return(__yo_mutex_destroy(&(self.mutex)));
|
|
29
42
|
})
|
|
30
43
|
);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Mutex
|
|
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))(
|
|
34
62
|
object(
|
|
35
|
-
|
|
63
|
+
_mutex : Mutex(T)
|
|
36
64
|
)
|
|
37
65
|
);
|
|
38
66
|
impl(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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));
|
|
42
83
|
}),
|
|
43
|
-
|
|
44
|
-
|
|
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)
|
|
45
94
|
}),
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
+
)
|
|
49
106
|
);
|
|
50
107
|
impl(
|
|
51
|
-
|
|
108
|
+
forall(T : Type),
|
|
109
|
+
where(T <: Send),
|
|
110
|
+
Mutex(T),
|
|
52
111
|
Dispose(
|
|
53
112
|
dispose : (fn(self : Self) -> unit)({
|
|
54
|
-
return(__yo_mutex_destroy(&(self.
|
|
113
|
+
return(__yo_mutex_destroy(&(self._handle)));
|
|
55
114
|
})
|
|
56
115
|
)
|
|
57
116
|
);
|
|
117
|
+
|
|
58
118
|
export(
|
|
59
119
|
__YO_THREAD_SYNC_TYPE,
|
|
60
120
|
mutex_t,
|
|
61
|
-
Mutex
|
|
121
|
+
Mutex,
|
|
122
|
+
__MutexUnlocker
|
|
62
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 :
|
|
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
|
-
|
|
32
|
-
|
|
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.
|
|
51
|
+
self._mutex._raw_lock();
|
|
46
52
|
// Double-check after acquiring lock
|
|
47
53
|
cond(
|
|
48
|
-
self._done => {
|
|
49
|
-
self._mutex.
|
|
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
|
|
54
|
-
self._mutex.
|
|
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);
|
package/std/sync/rwlock.yo
CHANGED
|
@@ -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.
|
|
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.
|
|
53
|
+
self._mutex._raw_unlock();
|
|
53
54
|
}),
|
|
54
55
|
// Release a read lock.
|
|
55
56
|
read_unlock : (fn(self : Self) -> unit)({
|
|
56
|
-
self._mutex.
|
|
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.
|
|
63
|
+
self._mutex._raw_unlock();
|
|
63
64
|
}),
|
|
64
|
-
// Acquire a write lock. Blocks
|
|
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.
|
|
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.
|
|
72
|
+
self._mutex._raw_unlock();
|
|
72
73
|
}),
|
|
73
74
|
// Release a write lock.
|
|
74
75
|
write_unlock : (fn(self : Self) -> unit)({
|
|
75
|
-
self._mutex.
|
|
76
|
+
self._mutex._raw_lock();
|
|
76
77
|
self._writer = false;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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);
|
package/std/sync/waitgroup.yo
CHANGED
|
@@ -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 :
|
|
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.
|
|
46
|
-
|
|
52
|
+
self._mutex._raw_lock();
|
|
53
|
+
new_count := (self._count.load(MemoryOrder.Relaxed) + delta);
|
|
47
54
|
cond(
|
|
48
|
-
(
|
|
49
|
-
self._count
|
|
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.
|
|
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.
|
|
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.
|
|
75
|
+
self._mutex._raw_unlock();
|
|
67
76
|
}),
|
|
68
|
-
// Get the current count (
|
|
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);
|
package/std/sys/advise.yo
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
//!
|
|
3
3
|
//! Advisory wrappers for file and memory access pattern hints.
|
|
4
4
|
//! Returns 0 on success, -errno / negative platform error on failure.
|
|
5
|
+
pragma(Pragma.AllowUnsafe);
|
|
5
6
|
{ __yo_sync_fadvise, __yo_sync_madvise } :: import("./externs.yo");
|
|
6
7
|
POSIX_FADV_NORMAL :: i32(0);
|
|
7
8
|
POSIX_FADV_RANDOM :: i32(1);
|