nomen-lang 0.4.0 → 0.6.0
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/core/System/Arena.nm +6 -6
- package/core/System/Awaitable.nm +21 -0
- package/core/System/Buffer.nm +31 -15
- package/core/System/Channel.nm +82 -29
- package/core/System/ClassBuffer.nm +23 -9
- package/core/System/Controls/Button.nm +2 -2
- package/core/System/Controls/CheckBox.nm +2 -2
- package/core/System/Controls/Container.nm +3 -3
- package/core/System/Controls/TextBox.nm +2 -2
- package/core/System/Fiber.nm +201 -0
- package/core/System/Graph.nm +1 -1
- package/core/System/LinkedList.nm +1 -1
- package/core/System/List.nm +3 -3
- package/core/System/Map.nm +2 -2
- package/core/System/Mutex.nm +50 -54
- package/core/System/Nursery.nm +10 -8
- package/core/System/Option.nm +2 -2
- package/core/System/Random.nm +67 -0
- package/core/System/Result.nm +2 -2
- package/core/System/Set.nm +1 -1
- package/core/System/Spawnable.nm +18 -0
- package/core/System/Stream/File.nm +3 -3
- package/core/System/Stream/Http.nm +85 -436
- package/core/System/Stream/Tcp.nm +441 -0
- package/core/System/String.nm +7 -7
- package/core/System/StringBuilder.nm +1 -1
- package/core/System/Task.nm +145 -12
- package/core/System/Test.nm +20 -11
- package/core/System/Text/CharIndex.nm +8 -8
- package/core/System/Text/Chars.nm +1 -1
- package/core/System/Text/JsonTree.nm +1 -1
- package/core/System/Text/Regex.nm +122 -32
- package/core/System/Text/Utf8.nm +6 -6
- package/core/System/Thread.nm +199 -0
- package/core/System/Tree.nm +1 -1
- package/dist/index.mjs +19473 -13661
- package/package.json +1 -1
package/core/System/Arena.nm
CHANGED
|
@@ -36,17 +36,17 @@ pub struct Arena<T> {
|
|
|
36
36
|
// Slot payloads. T = value struct/primitive routes to Buffer<T>; T =
|
|
37
37
|
// class/trait routes to ClassBuffer<T> (monomorphize rewrites the field),
|
|
38
38
|
// so slot ownership follows the usual element rules.
|
|
39
|
-
var
|
|
39
|
+
var values = Buffer<T>()
|
|
40
40
|
// Per-slot generation, bumped on every free. Slots are calloc'd, so a
|
|
41
41
|
// fresh slot's generation is 0.
|
|
42
|
-
var
|
|
42
|
+
var generations = Buffer<int>()
|
|
43
43
|
// Freed slot indices available for reuse (LIFO free list). A raw Buffer
|
|
44
44
|
// plus an explicit count: a `List<int>` field would need its own
|
|
45
45
|
// monomorphization from inside a generic struct body.
|
|
46
|
-
var
|
|
47
|
-
var
|
|
46
|
+
var free_indices = Buffer<int>()
|
|
47
|
+
var free_count = 0
|
|
48
48
|
// High-water mark: every index < count has a slot (live or free).
|
|
49
|
-
var
|
|
49
|
+
var count = 0
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* A fresh arena with no slots.
|
|
@@ -78,7 +78,7 @@ pub struct Arena<T> {
|
|
|
78
78
|
}
|
|
79
79
|
self.values.grow(index + 1)
|
|
80
80
|
self.values.store(index, value)
|
|
81
|
-
var
|
|
81
|
+
var handle = ArenaRef<T>()
|
|
82
82
|
handle.index = index
|
|
83
83
|
handle.generation = self.generations.load_int(index)
|
|
84
84
|
return handle
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Awaitable — the consumption side of the async model (ASYNC.md,
|
|
2
|
+
// "Awaitable trait"). Park-flavored by design: `wait` MAY park the current
|
|
3
|
+
// execution context (inside a fiber it parks the fiber and frees its
|
|
4
|
+
// worker; on a raw thread it blocks). A Rust-style poll/Waker trait is
|
|
5
|
+
// deliberately rejected — it would smuggle the state-machine transform back
|
|
6
|
+
// in through the trait definition.
|
|
7
|
+
//
|
|
8
|
+
// `Task<T>` is the conformer: `Thread(...).start()` and `Fiber(...).start()`
|
|
9
|
+
// both yield one, so a generic helper taking `Awaitable` waits on either.
|
|
10
|
+
// The trait is deliberately NOT the place for must-start semantics —
|
|
11
|
+
// start-optional lifecycles are legitimate for user primitives; must-start
|
|
12
|
+
// is a per-struct `#destroy` contract (see Thread.nm / Fiber.nm and
|
|
13
|
+
// ASYNC.md).
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Something whose completion can be waited on (may park the current context)
|
|
17
|
+
**/
|
|
18
|
+
pub trait Awaitable {
|
|
19
|
+
func wait = (ref self) {
|
|
20
|
+
}
|
|
21
|
+
}
|
package/core/System/Buffer.nm
CHANGED
|
@@ -37,7 +37,7 @@ pub struct Buffer<T> {
|
|
|
37
37
|
if new_cap == 0 {
|
|
38
38
|
new_cap = 8
|
|
39
39
|
}
|
|
40
|
-
while new_cap < needed; new_cap *= 2 {}
|
|
40
|
+
while new_cap < needed; new_cap *= 2 { }
|
|
41
41
|
self.data = realloc(self.data, new_cap * 4)
|
|
42
42
|
memset(self.data + self.cap * 4, 0, (new_cap - self.cap) * 4)
|
|
43
43
|
self.cap = new_cap
|
|
@@ -107,7 +107,7 @@ pub struct Buffer<T> {
|
|
|
107
107
|
if new_cap == 0 {
|
|
108
108
|
new_cap = 4
|
|
109
109
|
}
|
|
110
|
-
while new_cap < needed; new_cap *= 2 {}
|
|
110
|
+
while new_cap < needed; new_cap *= 2 { }
|
|
111
111
|
self.data = realloc(self.data, new_cap * 8)
|
|
112
112
|
memset(self.data + self.cap * 8, 0, (new_cap - self.cap) * 8)
|
|
113
113
|
self.cap = new_cap
|
|
@@ -204,7 +204,7 @@ pub struct Buffer<T> {
|
|
|
204
204
|
if new_cap == 0 {
|
|
205
205
|
new_cap = 4
|
|
206
206
|
}
|
|
207
|
-
while new_cap < needed; new_cap *= 2 {}
|
|
207
|
+
while new_cap < needed; new_cap *= 2 { }
|
|
208
208
|
self.data = realloc(self.data, new_cap * T_SIZE)
|
|
209
209
|
memset(self.data + self.cap * T_SIZE, 0, (new_cap - self.cap) * T_SIZE)
|
|
210
210
|
self.cap = new_cap
|
|
@@ -382,24 +382,29 @@ pub struct Buffer<T> {
|
|
|
382
382
|
// this raw form is correct for trivially-copyable elements only. NOT
|
|
383
383
|
// `inline`: the body calls through the fn pointer, so splicing the raw
|
|
384
384
|
// block into a caller would bypass the per-element specializations.
|
|
385
|
-
func modify = (ref self, int i: i >= 0 && i < self.cap, func
|
|
385
|
+
func modify = (ref self, int i: i >= 0 && i < self.cap, func(T, out T) f) {
|
|
386
386
|
```
|
|
387
387
|
#arch: c
|
|
388
|
-
((T*)(unsigned long long)self->data)[i] = f(((T*)(unsigned long long)self->data)[i]);
|
|
388
|
+
((T*)(unsigned long long)self->data)[i] = ((T (*)(void *, T))f->code)(f->env, ((T*)(unsigned long long)self->data)[i]);
|
|
389
389
|
```
|
|
390
390
|
```
|
|
391
391
|
#arch: aarch64
|
|
392
|
-
// x19 = self, x1 = i, x2 = f
|
|
392
|
+
// x19 = self, x1 = i, x2 = f (a closure descriptor: { code, env,
|
|
393
|
+
// owned } — the call loads both words and passes env first, shifting
|
|
394
|
+
// the value argument to x1). Scalar elements round-trip through x1
|
|
393
395
|
// (width-matched); larger trivially-copyable structs pass the slot
|
|
394
396
|
// address and an sret pointer (x8) — the fn writes the replacement
|
|
395
397
|
// straight into the slot from a stack-stage copy of the old value.
|
|
396
398
|
stp x20, x21, [sp, #-16]!
|
|
397
399
|
stp x22, x23, [sp, #-16]!
|
|
400
|
+
stp x24, x25, [sp, #-16]!
|
|
398
401
|
mov x22, x2
|
|
399
402
|
ldr x9, [x19, #8]
|
|
400
403
|
mov x3, #T_SIZE
|
|
401
404
|
madd x20, x1, x3, xzr
|
|
402
405
|
add x20, x20, x9
|
|
406
|
+
ldr x24, [x22]
|
|
407
|
+
ldr x0, [x22, #8]
|
|
403
408
|
cmp x3, #8
|
|
404
409
|
b.gt .Lbuffer_modify_T_big
|
|
405
410
|
cmp x3, #1
|
|
@@ -408,18 +413,18 @@ pub struct Buffer<T> {
|
|
|
408
413
|
b.eq .Lbuffer_modify_T_ld2
|
|
409
414
|
cmp x3, #4
|
|
410
415
|
b.eq .Lbuffer_modify_T_ld4
|
|
411
|
-
ldr
|
|
416
|
+
ldr x1, [x20]
|
|
412
417
|
b .Lbuffer_modify_T_call
|
|
413
418
|
.Lbuffer_modify_T_ld1:
|
|
414
|
-
ldrb
|
|
419
|
+
ldrb w1, [x20]
|
|
415
420
|
b .Lbuffer_modify_T_call
|
|
416
421
|
.Lbuffer_modify_T_ld2:
|
|
417
|
-
ldrh
|
|
422
|
+
ldrh w1, [x20]
|
|
418
423
|
b .Lbuffer_modify_T_call
|
|
419
424
|
.Lbuffer_modify_T_ld4:
|
|
420
|
-
ldr
|
|
425
|
+
ldr w1, [x20]
|
|
421
426
|
.Lbuffer_modify_T_call:
|
|
422
|
-
blr
|
|
427
|
+
blr x24
|
|
423
428
|
cmp x3, #1
|
|
424
429
|
b.eq .Lbuffer_modify_T_st1
|
|
425
430
|
cmp x3, #2
|
|
@@ -445,11 +450,13 @@ pub struct Buffer<T> {
|
|
|
445
450
|
mov x1, x20
|
|
446
451
|
mov x2, x3
|
|
447
452
|
bl _memcpy
|
|
448
|
-
|
|
453
|
+
ldr x0, [x22, #8]
|
|
454
|
+
mov x1, sp
|
|
449
455
|
mov x8, x20
|
|
450
|
-
blr
|
|
456
|
+
blr x24
|
|
451
457
|
add sp, sp, x23
|
|
452
458
|
.Lbuffer_modify_T_end:
|
|
459
|
+
ldp x24, x25, [sp], #16
|
|
453
460
|
ldp x22, x23, [sp], #16
|
|
454
461
|
ldp x20, x21, [sp], #16
|
|
455
462
|
```
|
|
@@ -513,7 +520,11 @@ pub struct Buffer<T> {
|
|
|
513
520
|
// version that also reclaims dst's previous value first (see
|
|
514
521
|
// owning_buffer_specialize.ts); this raw form is correct for
|
|
515
522
|
// trivially-destructible elements only.
|
|
516
|
-
inline func shift = (
|
|
523
|
+
inline func shift = (
|
|
524
|
+
ref self,
|
|
525
|
+
int dst: dst >= 0 && dst < self.cap,
|
|
526
|
+
int src: src >= 0 && src < self.cap
|
|
527
|
+
) {
|
|
517
528
|
```
|
|
518
529
|
#arch: c
|
|
519
530
|
T* _slots = (T*)(unsigned long long)self->data;
|
|
@@ -580,7 +591,12 @@ pub struct Buffer<T> {
|
|
|
580
591
|
// self's scope and is invalidated if self is reassigned. This is the single
|
|
581
592
|
// primitive every other slice (Array<T>, List<T>, user containers) builds
|
|
582
593
|
// on — higher layers just `return self.items.slice(start, end)`.
|
|
583
|
-
func slice = (
|
|
594
|
+
func slice = (
|
|
595
|
+
self,
|
|
596
|
+
int start: start >= 0,
|
|
597
|
+
int end: end >= start && end <= self.cap,
|
|
598
|
+
out view T
|
|
599
|
+
) {
|
|
584
600
|
```
|
|
585
601
|
#arch: c
|
|
586
602
|
nomen_view _r;
|
package/core/System/Channel.nm
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// Channel — thread-safe single-direction queue for passing values between
|
|
2
2
|
// tasks. Unbounded, FIFO, blocking receive.
|
|
3
3
|
//
|
|
4
|
-
// The queue node's payload is
|
|
5
|
-
// (value, len)
|
|
6
|
-
// `send_string`/`receive_string` move fat strings
|
|
7
|
-
//
|
|
8
|
-
//
|
|
4
|
+
// The queue node's payload is three words wide: every message carries a
|
|
5
|
+
// (value, len, kind) triple. `send`/`receive` move uint64 values (len and
|
|
6
|
+
// kind stay 0); `send_string`/`receive_string` move fat strings (kind = 1)
|
|
7
|
+
// without truncating the len half. `#destroy` frees only string payloads —
|
|
8
|
+
// a raw uint64 payload is a value, not a pointer. A typed Channel<T>
|
|
9
|
+
// wrapper can come later — the storage story is identical, only the
|
|
10
|
+
// call-site types change.
|
|
9
11
|
//
|
|
10
12
|
// String payloads are copied in and moved out: `send_string` duplicates the
|
|
11
13
|
// value into the node (the sender keeps its own string), and
|
|
@@ -25,17 +27,20 @@ pub class Channel: Sendable {
|
|
|
25
27
|
pub var uint64 not_empty_cv = 0
|
|
26
28
|
pub var uint64 head = 0
|
|
27
29
|
pub var uint64 tail = 0
|
|
30
|
+
// Head of the parked-fiber wait list (guarded by mu). Send wakes it.
|
|
31
|
+
pub var uint64 waiters = 0
|
|
28
32
|
|
|
29
33
|
pub func #init = (self) {
|
|
30
34
|
```
|
|
31
35
|
#arch: c
|
|
32
36
|
#scope: file
|
|
33
37
|
#include <pthread.h>
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
struct nomen_channel_node {
|
|
39
|
+
unsigned long long value;
|
|
40
|
+
unsigned long long len;
|
|
41
|
+
unsigned long long kind; // 0 = uint64 payload, 1 = owned string
|
|
42
|
+
struct nomen_channel_node *next;
|
|
43
|
+
};
|
|
39
44
|
```
|
|
40
45
|
```
|
|
41
46
|
#arch: c
|
|
@@ -45,6 +50,7 @@ pub class Channel: Sendable {
|
|
|
45
50
|
pthread_cond_init((pthread_cond_t *)self->not_empty_cv, NULL);
|
|
46
51
|
self->head = 0;
|
|
47
52
|
self->tail = 0;
|
|
53
|
+
self->waiters = 0;
|
|
48
54
|
```
|
|
49
55
|
```
|
|
50
56
|
#arch: aarch64
|
|
@@ -60,6 +66,7 @@ pub class Channel: Sendable {
|
|
|
60
66
|
bl _pthread_cond_init
|
|
61
67
|
str xzr, [x19, #24]
|
|
62
68
|
str xzr, [x19, #32]
|
|
69
|
+
str xzr, [x19, #40]
|
|
63
70
|
```
|
|
64
71
|
}
|
|
65
72
|
|
|
@@ -69,6 +76,7 @@ pub class Channel: Sendable {
|
|
|
69
76
|
struct nomen_channel_node *n = (struct nomen_channel_node *)malloc(sizeof(struct nomen_channel_node));
|
|
70
77
|
n->value = value;
|
|
71
78
|
n->len = 0;
|
|
79
|
+
n->kind = 0;
|
|
72
80
|
n->next = NULL;
|
|
73
81
|
pthread_mutex_lock((pthread_mutex_t *)self->mu);
|
|
74
82
|
if (self->tail) {
|
|
@@ -78,25 +86,27 @@ pub class Channel: Sendable {
|
|
|
78
86
|
}
|
|
79
87
|
self->tail = (unsigned long long)n;
|
|
80
88
|
pthread_cond_signal((pthread_cond_t *)self->not_empty_cv);
|
|
89
|
+
__nomen_fiber_waitq_wake((struct nomen_fiber **)&self->waiters);
|
|
81
90
|
pthread_mutex_unlock((pthread_mutex_t *)self->mu);
|
|
82
91
|
```
|
|
83
92
|
```
|
|
84
93
|
#arch: aarch64
|
|
85
94
|
// x19 = self (set by prologue), x1 = value
|
|
86
95
|
str x1, [sp, #-16]! // save value on stack
|
|
87
|
-
mov x0, #
|
|
96
|
+
mov x0, #32 // node = { value, len, kind, next }
|
|
88
97
|
bl _malloc // x0 = node
|
|
89
98
|
ldr x1, [sp], #16 // restore value
|
|
90
99
|
str x1, [x0, #0] // node->value = value
|
|
91
100
|
str xzr, [x0, #8] // node->len = 0
|
|
92
|
-
str xzr, [x0, #16] // node->
|
|
101
|
+
str xzr, [x0, #16] // node->kind = 0 (uint64 payload)
|
|
102
|
+
str xzr, [x0, #24] // node->next = NULL
|
|
93
103
|
str x0, [sp, #-16]! // save node on stack
|
|
94
104
|
ldr x0, [x19, #8] // self->mu
|
|
95
105
|
bl _pthread_mutex_lock
|
|
96
106
|
ldr x2, [sp], #16 // restore node
|
|
97
107
|
ldr x1, [x19, #32] // tail
|
|
98
108
|
cbz x1, .Lchannel_send_empty
|
|
99
|
-
str x2, [x1, #
|
|
109
|
+
str x2, [x1, #24] // tail->next = node
|
|
100
110
|
b .Lchannel_send_enqueued
|
|
101
111
|
.Lchannel_send_empty:
|
|
102
112
|
str x2, [x19, #24] // head = node
|
|
@@ -104,6 +114,8 @@ pub class Channel: Sendable {
|
|
|
104
114
|
str x2, [x19, #32] // tail = node
|
|
105
115
|
ldr x0, [x19, #16] // not_empty_cv
|
|
106
116
|
bl _pthread_cond_signal
|
|
117
|
+
add x0, x19, #40 // &self->waiters
|
|
118
|
+
bl ___nomen_fiber_waitq_wake
|
|
107
119
|
ldr x0, [x19, #8] // self->mu
|
|
108
120
|
bl _pthread_mutex_unlock
|
|
109
121
|
```
|
|
@@ -120,6 +132,7 @@ pub class Channel: Sendable {
|
|
|
120
132
|
nomen_string _v = value.ptr ? nomen_str_dup(value) : (nomen_string){0, 0};
|
|
121
133
|
n->value = (unsigned long long)_v.ptr;
|
|
122
134
|
n->len = (unsigned long long)_v.len;
|
|
135
|
+
n->kind = 1;
|
|
123
136
|
n->next = NULL;
|
|
124
137
|
pthread_mutex_lock((pthread_mutex_t *)self->mu);
|
|
125
138
|
if (self->tail) {
|
|
@@ -129,13 +142,14 @@ pub class Channel: Sendable {
|
|
|
129
142
|
}
|
|
130
143
|
self->tail = (unsigned long long)n;
|
|
131
144
|
pthread_cond_signal((pthread_cond_t *)self->not_empty_cv);
|
|
145
|
+
__nomen_fiber_waitq_wake((struct nomen_fiber **)&self->waiters);
|
|
132
146
|
pthread_mutex_unlock((pthread_mutex_t *)self->mu);
|
|
133
147
|
```
|
|
134
148
|
```
|
|
135
149
|
#arch: aarch64
|
|
136
150
|
// x19 = self (set by prologue), x1 = value.ptr, x2 = value.len
|
|
137
151
|
stp x1, x2, [sp, #-32]! // [sp]=ptr [sp+8]=len [sp+16]=node scratch
|
|
138
|
-
mov x0, #
|
|
152
|
+
mov x0, #32 // node = { value, len, kind, next }
|
|
139
153
|
bl _malloc // x0 = node
|
|
140
154
|
str x0, [sp, #16] // save node across the strdup call
|
|
141
155
|
ldr x1, [sp, #0] // value.ptr
|
|
@@ -150,13 +164,15 @@ pub class Channel: Sendable {
|
|
|
150
164
|
str x0, [x2, #0] // node->value = copied ptr
|
|
151
165
|
ldr x1, [sp, #8] // value.len
|
|
152
166
|
str x1, [x2, #8] // node->len = len
|
|
153
|
-
|
|
167
|
+
mov x1, #1
|
|
168
|
+
str x1, [x2, #16] // node->kind = 1 (owned string payload)
|
|
169
|
+
str xzr, [x2, #24] // node->next = NULL
|
|
154
170
|
ldr x0, [x19, #8] // self->mu
|
|
155
171
|
bl _pthread_mutex_lock
|
|
156
172
|
ldr x2, [sp, #16] // restore node
|
|
157
173
|
ldr x1, [x19, #32] // tail
|
|
158
174
|
cbz x1, .Lchannel_send_str_empty
|
|
159
|
-
str x2, [x1, #
|
|
175
|
+
str x2, [x1, #24] // tail->next = node
|
|
160
176
|
b .Lchannel_send_str_enqueued
|
|
161
177
|
.Lchannel_send_str_empty:
|
|
162
178
|
str x2, [x19, #24] // head = node
|
|
@@ -164,6 +180,8 @@ pub class Channel: Sendable {
|
|
|
164
180
|
str x2, [x19, #32] // tail = node
|
|
165
181
|
ldr x0, [x19, #16] // not_empty_cv
|
|
166
182
|
bl _pthread_cond_signal
|
|
183
|
+
add x0, x19, #40 // &self->waiters — wake parked fibers
|
|
184
|
+
bl ___nomen_fiber_waitq_wake
|
|
167
185
|
ldr x0, [x19, #8] // self->mu
|
|
168
186
|
bl _pthread_mutex_unlock
|
|
169
187
|
add sp, sp, #32
|
|
@@ -175,7 +193,13 @@ pub class Channel: Sendable {
|
|
|
175
193
|
#arch: c
|
|
176
194
|
pthread_mutex_lock((pthread_mutex_t *)self->mu);
|
|
177
195
|
while (!self->head) {
|
|
178
|
-
|
|
196
|
+
// Fibers park instead of blocking; a cancelled fiber returns the
|
|
197
|
+
// zero value rather than waiting forever (cooperative cancel).
|
|
198
|
+
if (!__nomen_fiber_waitq_park(
|
|
199
|
+
(struct nomen_fiber **)&self->waiters, (void *)self->mu, (void *)self->not_empty_cv)) {
|
|
200
|
+
pthread_mutex_unlock((pthread_mutex_t *)self->mu);
|
|
201
|
+
return 0;
|
|
202
|
+
}
|
|
179
203
|
}
|
|
180
204
|
struct nomen_channel_node *n = (struct nomen_channel_node *)self->head;
|
|
181
205
|
self->head = (unsigned long long)n->next;
|
|
@@ -193,13 +217,21 @@ pub class Channel: Sendable {
|
|
|
193
217
|
.Lchannel_receive_wait:
|
|
194
218
|
ldr x1, [x19, #24] // head
|
|
195
219
|
cbnz x1, .Lchannel_receive_got
|
|
196
|
-
|
|
220
|
+
add x0, x19, #40 // &self->waiters
|
|
197
221
|
ldr x1, [x19, #8] // mu
|
|
198
|
-
|
|
222
|
+
ldr x2, [x19, #16] // not_empty_cv
|
|
223
|
+
bl ___nomen_fiber_waitq_park
|
|
224
|
+
cbz x0, .Lchannel_receive_cancelled
|
|
199
225
|
b .Lchannel_receive_wait
|
|
226
|
+
.Lchannel_receive_cancelled:
|
|
227
|
+
// Cancelled while waiting: return the zero value.
|
|
228
|
+
ldr x0, [x19, #8] // self->mu
|
|
229
|
+
bl _pthread_mutex_unlock
|
|
230
|
+
mov x0, #0
|
|
231
|
+
b .Lchannel_receive_end
|
|
200
232
|
.Lchannel_receive_got:
|
|
201
233
|
// x1 = node
|
|
202
|
-
ldr x2, [x1, #
|
|
234
|
+
ldr x2, [x1, #24] // node->next
|
|
203
235
|
str x2, [x19, #24] // head = node->next
|
|
204
236
|
cbnz x2, .Lchannel_receive_not_empty
|
|
205
237
|
str xzr, [x19, #32] // tail = 0
|
|
@@ -211,6 +243,7 @@ pub class Channel: Sendable {
|
|
|
211
243
|
ldr x0, [x19, #8] // self->mu
|
|
212
244
|
bl _pthread_mutex_unlock
|
|
213
245
|
ldr x0, [sp], #16 // pop value → x0 (return)
|
|
246
|
+
.Lchannel_receive_end:
|
|
214
247
|
```
|
|
215
248
|
}
|
|
216
249
|
|
|
@@ -223,7 +256,11 @@ pub class Channel: Sendable {
|
|
|
223
256
|
#arch: c
|
|
224
257
|
pthread_mutex_lock((pthread_mutex_t *)self->mu);
|
|
225
258
|
while (!self->head) {
|
|
226
|
-
|
|
259
|
+
if (!__nomen_fiber_waitq_park(
|
|
260
|
+
(struct nomen_fiber **)&self->waiters, (void *)self->mu, (void *)self->not_empty_cv)) {
|
|
261
|
+
pthread_mutex_unlock((pthread_mutex_t *)self->mu);
|
|
262
|
+
return (nomen_string){ 0, 0 };
|
|
263
|
+
}
|
|
227
264
|
}
|
|
228
265
|
struct nomen_channel_node *n = (struct nomen_channel_node *)self->head;
|
|
229
266
|
self->head = (unsigned long long)n->next;
|
|
@@ -243,13 +280,23 @@ pub class Channel: Sendable {
|
|
|
243
280
|
.Lchannel_receive_str_wait:
|
|
244
281
|
ldr x1, [x19, #24] // head
|
|
245
282
|
cbnz x1, .Lchannel_receive_str_got
|
|
246
|
-
|
|
283
|
+
add x0, x19, #40 // &self->waiters
|
|
247
284
|
ldr x1, [x19, #8] // mu
|
|
248
|
-
|
|
285
|
+
ldr x2, [x19, #16] // not_empty_cv
|
|
286
|
+
bl ___nomen_fiber_waitq_park
|
|
287
|
+
cbz x0, .Lchannel_receive_str_cancelled
|
|
249
288
|
b .Lchannel_receive_str_wait
|
|
289
|
+
.Lchannel_receive_str_cancelled:
|
|
290
|
+
// Cancelled while waiting: return the empty fat string (x0 = ptr,
|
|
291
|
+
// x1 = len).
|
|
292
|
+
ldr x0, [x19, #8] // self->mu
|
|
293
|
+
bl _pthread_mutex_unlock
|
|
294
|
+
mov x0, #0
|
|
295
|
+
mov x1, #0
|
|
296
|
+
b .Lchannel_receive_str_end
|
|
250
297
|
.Lchannel_receive_str_got:
|
|
251
298
|
// x1 = node
|
|
252
|
-
ldr x2, [x1, #
|
|
299
|
+
ldr x2, [x1, #24] // node->next
|
|
253
300
|
str x2, [x19, #24] // head = node->next
|
|
254
301
|
cbnz x2, .Lchannel_receive_str_not_empty
|
|
255
302
|
str xzr, [x19, #32] // tail = 0
|
|
@@ -264,6 +311,7 @@ pub class Channel: Sendable {
|
|
|
264
311
|
ldr x0, [x19, #8] // self->mu
|
|
265
312
|
bl _pthread_mutex_unlock
|
|
266
313
|
ldp x0, x1, [sp], #16 // pop pair → x0/x1 (return)
|
|
314
|
+
.Lchannel_receive_str_end:
|
|
267
315
|
```
|
|
268
316
|
}
|
|
269
317
|
|
|
@@ -271,12 +319,13 @@ pub class Channel: Sendable {
|
|
|
271
319
|
```
|
|
272
320
|
#arch: c
|
|
273
321
|
if (self->mu) {
|
|
274
|
-
// Drain any pending nodes. A string payload that was
|
|
275
|
-
// received still owns its buffer — free it with the node.
|
|
322
|
+
// Drain any pending nodes. A string payload (kind = 1) that was
|
|
323
|
+
// never received still owns its buffer — free it with the node.
|
|
324
|
+
// A uint64 payload (kind = 0) is a raw value, never a pointer.
|
|
276
325
|
struct nomen_channel_node *n = (struct nomen_channel_node *)self->head;
|
|
277
326
|
while (n) {
|
|
278
327
|
struct nomen_channel_node *next = n->next;
|
|
279
|
-
if (n->value) free((void *)n->value);
|
|
328
|
+
if (n->kind == 1 && n->value) free((void *)n->value);
|
|
280
329
|
free(n);
|
|
281
330
|
n = next;
|
|
282
331
|
}
|
|
@@ -295,14 +344,18 @@ pub class Channel: Sendable {
|
|
|
295
344
|
// x19 = self (set by prologue)
|
|
296
345
|
ldr x0, [x19, #8] // self->mu
|
|
297
346
|
cbz x0, .Lchannel_destroy_done
|
|
298
|
-
// Drain nodes (freeing
|
|
347
|
+
// Drain nodes (freeing only owned-string payloads; a uint64
|
|
348
|
+
// payload is a raw value, never a pointer)
|
|
299
349
|
ldr x1, [x19, #24] // head
|
|
300
350
|
.Lchannel_destroy_drain:
|
|
301
351
|
cbz x1, .Lchannel_destroy_drained
|
|
302
|
-
ldr x2, [x1, #
|
|
352
|
+
ldr x2, [x1, #24] // next (before calls clobber x1)
|
|
303
353
|
stp x1, x2, [sp, #-16]! // save node + next
|
|
354
|
+
ldr x3, [x1, #16] // payload kind (1 = owned string)
|
|
304
355
|
ldr x0, [x1, #0] // payload ptr
|
|
305
356
|
cbz x0, .Lchannel_destroy_no_payload
|
|
357
|
+
cmp x3, #1
|
|
358
|
+
b.ne .Lchannel_destroy_no_payload
|
|
306
359
|
bl _free
|
|
307
360
|
.Lchannel_destroy_no_payload:
|
|
308
361
|
ldr x0, [sp, #0] // node
|
|
@@ -37,7 +37,7 @@ pub struct ClassBuffer<T> {
|
|
|
37
37
|
if new_cap == 0 {
|
|
38
38
|
new_cap = 4
|
|
39
39
|
}
|
|
40
|
-
while new_cap < needed; new_cap *= 2 {}
|
|
40
|
+
while new_cap < needed; new_cap *= 2 { }
|
|
41
41
|
self.data = realloc(self.data, new_cap * 8)
|
|
42
42
|
memset(self.data + self.cap * 8, 0, (new_cap - self.cap) * 8)
|
|
43
43
|
self.cap = new_cap
|
|
@@ -73,7 +73,7 @@ pub struct ClassBuffer<T> {
|
|
|
73
73
|
if new_cap == 0 {
|
|
74
74
|
new_cap = 4
|
|
75
75
|
}
|
|
76
|
-
while new_cap < needed; new_cap *= 2 {}
|
|
76
|
+
while new_cap < needed; new_cap *= 2 { }
|
|
77
77
|
self.data = realloc(self.data, new_cap * 8)
|
|
78
78
|
memset(self.data + self.cap * 8, 0, (new_cap - self.cap) * 8)
|
|
79
79
|
self.cap = new_cap
|
|
@@ -150,12 +150,12 @@ pub struct ClassBuffer<T> {
|
|
|
150
150
|
// unless `f` returned it. Storing the returned instance raw is sound:
|
|
151
151
|
// the slot takes sole ownership and `f`'s return is not retained
|
|
152
152
|
// elsewhere. NOT `inline`: the body calls through the fn pointer.
|
|
153
|
-
func modify = (ref self, int i: i >= 0 && i < self.cap, func
|
|
153
|
+
func modify = (ref self, int i: i >= 0 && i < self.cap, func(T, out T) f) {
|
|
154
154
|
```
|
|
155
155
|
#arch: c
|
|
156
156
|
long *slots = (long*)(unsigned long long)self->data;
|
|
157
157
|
void *old = (void*)slots[i];
|
|
158
|
-
void *new_value = (void*)f(old);
|
|
158
|
+
void *new_value = (void*)((void *(*)(void *, void *))f->code)(f->env, old);
|
|
159
159
|
slots[i] = (long)new_value;
|
|
160
160
|
if (old != new_value) {
|
|
161
161
|
T_destroy(old);
|
|
@@ -164,15 +164,19 @@ pub struct ClassBuffer<T> {
|
|
|
164
164
|
```
|
|
165
165
|
```
|
|
166
166
|
#arch: aarch64
|
|
167
|
-
// x19 = self, x1 = i, x2 = f
|
|
167
|
+
// x19 = self, x1 = i, x2 = f (a closure descriptor — env goes in x0,
|
|
168
|
+
// the old slot value shifts to x1). Class slots are one pointer wide.
|
|
168
169
|
stp x20, x21, [sp, #-16]!
|
|
169
170
|
stp x22, x23, [sp, #-16]!
|
|
171
|
+
stp x24, x25, [sp, #-16]!
|
|
170
172
|
mov x22, x2
|
|
171
173
|
ldr x9, [x19, #8]
|
|
172
174
|
lsl x3, x1, #3
|
|
173
175
|
add x20, x9, x3
|
|
174
|
-
ldr
|
|
175
|
-
|
|
176
|
+
ldr x24, [x22]
|
|
177
|
+
ldr x0, [x22, #8]
|
|
178
|
+
ldr x1, [x20]
|
|
179
|
+
blr x24
|
|
176
180
|
mov x23, x0
|
|
177
181
|
ldr x9, [x20]
|
|
178
182
|
str x23, [x20]
|
|
@@ -183,6 +187,7 @@ pub struct ClassBuffer<T> {
|
|
|
183
187
|
mov x0, x9
|
|
184
188
|
bl _free
|
|
185
189
|
.Lcb_modify_T_done:
|
|
190
|
+
ldp x24, x25, [sp], #16
|
|
186
191
|
ldp x22, x23, [sp], #16
|
|
187
192
|
ldp x20, x21, [sp], #16
|
|
188
193
|
```
|
|
@@ -294,7 +299,11 @@ pub struct ClassBuffer<T> {
|
|
|
294
299
|
// instance afterwards. The class-buffer counterpart of Buffer.shift,
|
|
295
300
|
// used by Map/Set.remove's backward-shift so shifting entries neither
|
|
296
301
|
// duplicates nor orphans owned instances.
|
|
297
|
-
func shift = (
|
|
302
|
+
func shift = (
|
|
303
|
+
ref self,
|
|
304
|
+
int dst: dst >= 0 && dst < self.cap,
|
|
305
|
+
int src: src >= 0 && src < self.cap
|
|
306
|
+
) {
|
|
298
307
|
```
|
|
299
308
|
#arch: c
|
|
300
309
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -338,7 +347,12 @@ pub struct ClassBuffer<T> {
|
|
|
338
347
|
// pointers, returned as a `view T` (T is a class, so each element is an
|
|
339
348
|
// 8-byte pointer). Borrows from self — invalidated on reassignment. Mirrors
|
|
340
349
|
// Buffer.slice; ClassBuffer stores 8-byte slots, so the element width is 8.
|
|
341
|
-
func slice = (
|
|
350
|
+
func slice = (
|
|
351
|
+
self,
|
|
352
|
+
int start: start >= 0,
|
|
353
|
+
int end: end >= start && end <= self.cap,
|
|
354
|
+
out view T
|
|
355
|
+
) {
|
|
342
356
|
```
|
|
343
357
|
#arch: c
|
|
344
358
|
nomen_view _r;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A push-button control that triggers an action when clicked
|
|
3
3
|
**/
|
|
4
|
-
pub class Button
|
|
4
|
+
pub class Button: Control {
|
|
5
5
|
var uint64 handle = 0
|
|
6
6
|
|
|
7
7
|
pub func #init = (self, Window window, string title) {
|
|
@@ -162,7 +162,7 @@ pub class Button : Control {
|
|
|
162
162
|
|
|
163
163
|
pub func intrinsic_size = (self, out Size) {
|
|
164
164
|
var [w, h] = native_intrinsic(self.handle)
|
|
165
|
-
var
|
|
165
|
+
var s = Size()
|
|
166
166
|
s.width = w
|
|
167
167
|
s.height = h
|
|
168
168
|
return s
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A toggle control with a title and checked/hidden state
|
|
3
3
|
**/
|
|
4
|
-
pub class CheckBox
|
|
4
|
+
pub class CheckBox: Control {
|
|
5
5
|
var uint64 handle = 0
|
|
6
6
|
|
|
7
7
|
pub func #init = (self, Window window) {
|
|
@@ -199,7 +199,7 @@ pub class CheckBox : Control {
|
|
|
199
199
|
// ── Control trait conformance ──
|
|
200
200
|
pub func intrinsic_size = (self, out Size) {
|
|
201
201
|
var [w, h] = native_intrinsic(self.handle)
|
|
202
|
-
var
|
|
202
|
+
var s = Size()
|
|
203
203
|
s.width = w
|
|
204
204
|
s.height = h
|
|
205
205
|
return s
|
|
@@ -248,7 +248,7 @@ pub func native_intrinsic = (uint64 handle, out [int, int]) {
|
|
|
248
248
|
* so a `Container` can also be driven polymorphically as a `Control` (passed to
|
|
249
249
|
* a `Control`-taking function, stored in a `ClassBuffer<Control>`, etc.).
|
|
250
250
|
**/
|
|
251
|
-
pub class Container
|
|
251
|
+
pub class Container: Control {
|
|
252
252
|
var kinds = Buffer<int>()
|
|
253
253
|
// Per-leaf requested size, stored as a (kind, value) pair per axis. The
|
|
254
254
|
// kind is one of LEN_* (auto/fixed/percent/fill); the value is the pixel
|
|
@@ -1358,7 +1358,7 @@ pub class Container : Control {
|
|
|
1358
1358
|
constraints.max_height,
|
|
1359
1359
|
)
|
|
1360
1360
|
var int h = self.rh.load_int(self.root)
|
|
1361
|
-
var
|
|
1361
|
+
var s = Size()
|
|
1362
1362
|
s.width = w
|
|
1363
1363
|
s.height = h
|
|
1364
1364
|
return s
|
|
@@ -1371,7 +1371,7 @@ pub class Container : Control {
|
|
|
1371
1371
|
pub func intrinsic_size = (self, out Size) {
|
|
1372
1372
|
var int w = self.measure_node(self.root, 0, INF, 0, INF)
|
|
1373
1373
|
var int h = self.rh.load_int(self.root)
|
|
1374
|
-
var
|
|
1374
|
+
var s = Size()
|
|
1375
1375
|
s.width = w
|
|
1376
1376
|
s.height = h
|
|
1377
1377
|
return s
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* An editable single-line text input control
|
|
3
3
|
**/
|
|
4
|
-
pub class TextBox
|
|
4
|
+
pub class TextBox: Control {
|
|
5
5
|
var uint64 handle = 0
|
|
6
6
|
|
|
7
7
|
pub func #init = (self, Window window) {
|
|
@@ -203,7 +203,7 @@ pub class TextBox : Control {
|
|
|
203
203
|
// ── Control trait conformance ──
|
|
204
204
|
pub func intrinsic_size = (self, out Size) {
|
|
205
205
|
var [w, h] = native_intrinsic(self.handle)
|
|
206
|
-
var
|
|
206
|
+
var s = Size()
|
|
207
207
|
s.width = w
|
|
208
208
|
s.height = h
|
|
209
209
|
return s
|