diff options
Diffstat (limited to 'docs/SPEC.md')
| -rw-r--r-- | docs/SPEC.md | 26 |
1 files changed, 25 insertions, 1 deletions
diff --git a/docs/SPEC.md b/docs/SPEC.md index b2aa5ea..7dd1247 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 worker 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. |
