aboutsummaryrefslogtreecommitdiff
path: root/docs/LEARN.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/LEARN.md')
-rw-r--r--docs/LEARN.md41
1 files changed, 41 insertions, 0 deletions
diff --git a/docs/LEARN.md b/docs/LEARN.md
index 8e0d369..081498d 100644
--- a/docs/LEARN.md
+++ b/docs/LEARN.md
@@ -1003,6 +1003,47 @@ output:
16640
```
+## hold blocks
+
+actors share nothing by default: each one runs on its own copy of the program's globals and its own heap, so a global an actor changes is changed only for that actor. state that every actor sees is declared `shared` inside a top-level `hold` block, a monitor. only the functions written in the block can use its shared variables, and each call to one of them holds the block's lock for the whole call, so everything such a function does is atomic. a call from one of the block's functions to another doesn't lock again.
+
+using a shared variable anywhere else is an error, and so is using it in a closure, which could run after the lock is released. shared variables hold numbers or arrays of numbers; a pointer would point into one actor's heap. when a program has several hold blocks, a function of one block may call into a block declared after it, but not back into an earlier one while it holds a later one: that order rules out deadlocks between blocks, and breaking it stops the program.
+
+[example source](../examples/learn/36-hold.zc):
+
+```zc
+namespace;
+include rt, actor;
+typecheck on;
+
+// one counter for every actor. only the functions written in the block can touch it,
+// and each call holds the block's lock, so a read and the write after it can't interleave.
+hold {
+ shared let hits = 0;
+ fn hit() { let seen = hits; hits = seen + 1; return seen; }
+ fn total() { return hits; }
+}
+
+int work(int n) { let i = 0; while (i < n) { hit(); i++; } return n; }
+
+fn main() {
+ actor.Actor<int,int>* a = rt.new(actor.Actor<int,int>), b = rt.new(actor.Actor<int,int>);
+ a.spawn(work); b.spawn(work);
+ a.send(1000); b.send(1000);
+ a.recv(fn(int n) { return 0; }); b.recv(fn(int n) { return 0; });
+ a.await(); b.await();
+ rt.put_n(total()); rt.put_c(10);
+ a.close(); b.close();
+ return 0;
+}
+```
+
+output:
+
+```text
+2000
+```
+
## the optional typechecker
`typecheck on;` enables type checking for the declarations that follow; `typecheck off;` disables it. errors report the expected and actual types. the lifetime analyzer runs independently of this switch. a literal 0 can be a null pointer, `word` matches any type, and arrays can decay to pointers.