aboutsummaryrefslogtreecommitdiff
path: root/docs/SPEC.md
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-15 14:36:10 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-15 14:36:10 +0330
commit24a21f7520ce91681e573833b7f33156e308964a (patch)
tree9b2684173ee515649f4209192d9ed7f4acda6754 /docs/SPEC.md
parentlsp: hold and shared hovers, completion of the keywords, shared variables mar... (diff)
docs: hold blocks in LEARN.md (36-hold), spec 5.16, ACTOR.md; TODO: m:n scheduler and follow-ups
Diffstat (limited to 'docs/SPEC.md')
-rw-r--r--docs/SPEC.md26
1 files changed, 25 insertions, 1 deletions
diff --git a/docs/SPEC.md b/docs/SPEC.md
index b2aa5ea..a4f8397 100644
--- a/docs/SPEC.md
+++ b/docs/SPEC.md
@@ -675,7 +675,7 @@ int y = 7; asm { y @ 1 + y ! } // y is 8
### 5.1 items
-5.1.1 a unit is a sequence of items. an item is: a function definition or prototype, a global variable, `const`, `struct`, `type`, `enum`, `include`, `embed`, `namespace`, `typecheck`, `docs`, `lines`, `memdebug`, `hints`, `enhance`, `extern`, `unchecked;`, `unbounded;`, or a use of a top-level parse word (7.2).
+5.1.1 a unit is a sequence of items. an item is: a function definition or prototype, a global variable, `const`, `struct`, `type`, `enum`, `include`, `embed`, `namespace`, `typecheck`, `docs`, `lines`, `memdebug`, `hints`, `enhance`, `extern`, `unchecked;`, `unbounded;`, a `hold` block (5.16), or a use of a top-level parse word (7.2).
5.1.2 `struct`, `type`, `typecheck`, `docs`, `lines`, `namespace`, `enhance` and `extern` are valid only at the top level. `enum`, `embed`, `memdebug`, `hints` and `const` also appear as statements in functions. `include` is top-level only (5.12.7).
@@ -811,6 +811,30 @@ rt.put_n(util.twice(1)); // error: module access requires `include util;`
5.15.3 the comment `//:lines NAME` names the source after it NAME, counts its lines from the next line, and turns lines on from the next top-level item. it starts a new file: from the same item, `unbounded;` and `unchecked;` of the source before it no longer apply.
+### 5.16 hold blocks
+
+5.16.1 form:
+```
+hold {
+ shared let c = 0; // shared T name [= constant], shared T[n] name, shared let a, b;
+ fn a() { ... } // any function definitions or prototypes
+ int b(int k) { ... }
+}
+```
+`hold` followed by `{` at the top level starts a hold block; elsewhere `hold` and `shared` are ordinary names. a hold block contains shared variable declarations, functions, and any other top-level items. hold blocks don't nest (error: hold blocks don't nest). each block has a rank, its position among the unit's hold blocks, from 1.
+
+5.16.2 a shared variable is a global with one value for the whole program, seen by every actor (docs/ACTOR.md); every other global is each actor's own copy. its type is a number type or a fixed array of one. a pointer, string, struct, growable array or function type is error: x: is shared, so it holds numbers: no pointers, strings or structs (each actor has its own heap). an initializer is a constant, as for any global.
+
+5.16.3 only the functions written in the block can name its shared variables. a use anywhere else, including in another hold block, is error: x: is shared in a hold block; only the functions written inside that block can use it (they hold its lock). a use in a closure, even one written inside the block, is error: x: is shared; a closure can't use it, because it may run after the hold block's lock is released. a function of a hold block can't be `o3` (error: f: is in a hold block and can't be o3).
+
+5.16.4 a call to a function of a hold block holds the block's lock from entry to return. the lock is reentrant: a call from a function of the block to another of the same block (directly or through other functions) doesn't take it again. inside the call the shared variables behave as ordinary globals; the values another actor wrote are in them on entry, and what the call writes is seen by the next holder after it returns. a read followed by a write (`c = c + 1`) is therefore atomic.
+
+5.16.5 blocks are taken in rank order: while holding block j, a thread may enter a block k > j, but entering a block k < j stops the program with `zy: hold block k was entered while block j was held; hold blocks are entered in the order they are declared` (exit status 2, a trap). this rules out lock-order deadlocks between blocks.
+
+5.16.6 an actor that ends (or is closed) while inside a block gives the lock up without writing its changes back. the first entry to a block anywhere in the program takes the shared variables' initial values from the image.
+
+5.16.7 the implementation keeps the shared values in the host, outside every vm's memory: entering a block from outside (ops 57 and 58 of the syscall gate, at the function's entry and exit) copies them in and back, so every backend runs the block's functions unchanged. a vm without threads (the browser, bare metal) has one machine and needs no lock.
+
## 6. ownership and lifetimes
this section describes the analyzer: a pass that runs inside the compiler on every function (including code compiled at run time by `run.eval` and friends), tracks heap objects through local variables, inserts frees, and reports violations. it can be switched off (6.12). what it proves is limited to what is stated here.