diff options
| author | 2026-10-14 18:18:11 +0330 | |
|---|---|---|
| committer | 2026-10-14 18:18:11 +0330 | |
| commit | 0a67504c1715c5865e7fc646980b153ef346e601 (patch) | |
| tree | 7724b26c7bdda9d73da0334af1ef3039eb7c63ba | |
| parent | todo: correct self-modifying region caveat (diff) | |
examples: actor demo
| -rw-r--r-- | examples/actors_demo.zc | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/examples/actors_demo.zc b/examples/actors_demo.zc new file mode 100644 index 0000000..656e131 --- /dev/null +++ b/examples/actors_demo.zc @@ -0,0 +1,92 @@ +namespace; +include rt, actor; +typecheck on; + +// actors demo: a ring of isolated worker processes passes a token around, +// then a fan-out of workers crunches collatz ranges in parallel. +// run: build/bin/zy < examples/actors_demo.zc + +const N = 6, LAPS = 3, W = 4; + +void color(int c) { rt.put_c(27); rt.put_s("[1;"); rt.put_n(30 + c); rt.put_c('m'); return; } +void plain() { rt.put_c(27); rt.put_s("[0m"); return; } +void bar(int n, int c) { color(c); for (int i = 0; i < n; i++) rt.put_s("#"); plain(); return; } + +// one stage of the ring: mixes the token and counts its own hops. +// `seen` lives in the worker, so the caller's copy stays 0. +actor Hop { + int seen; + int handle(int tok) { + this.seen += 1; + return (tok * 31 + this.seen * 7) % 9973; + } +} + +// a cruncher: longest collatz chain that starts in [lo, lo + 20000) +actor Crunch { + int handle(int lo) { + int best = 0; + for (int s = lo; s < lo + 20000; s++) { + int n = s, k = 0; + while (n != 1) { if (n % 2) n = 3 * n + 1; else n = n / 2; k++; } + if (k > best) best = k; + } + return best; + } +} + +fn main() { + color(5); rt.put_s("== tinyvm actors: isolated workers, typed channels ==\n"); plain(); + + // the ring: the receive callback of stage i forwards to stage i + 1 + Hop*[6] ring; + for (int i = 0; i < N; i++) { ring[i] = rt.new(Hop); ring[i].spawn(); } + Hop** rp = &ring[0]; // closures capture a pointer, not the array + let hops = 0, last = 0, done = 0; + for (int i = 0; i < N; i++) { + let me = i; + ring[i].recv(fn(int v) { + hops++; last = v; + color(1 + me % 6); rt.put_s(" stage "); rt.put_n(me); plain(); + rt.put_s(" -> token "); rt.put_n(v); rt.put_c(' '); + bar(v % 40 + 1, 1 + me % 6); rt.put_c(10); + if (hops < N * LAPS) { + if (me == N - 1) { color(3); rt.put_s(" -- lap "); rt.put_n(hops / N); rt.put_s(" done --\n"); plain(); } + rp[(me + 1) % N].send(v); + } else done = 1; + return 0; + }); + } + color(2); rt.put_s("ring of "); rt.put_n(N); rt.put_s(" actors, "); rt.put_n(LAPS); rt.put_s(" laps, seed 42\n"); plain(); + ring[0].send(42); + while (!done) for (int i = 0; i < N; i++) ring[i].poll(); + for (int i = 0; i < N; i++) ring[i].close(); + + // fan-out: W workers in parallel, results arrive as they finish + color(2); rt.put_s("\nfan-out: "); rt.put_n(W); rt.put_s(" crunchers, collatz over 80000 starts\n"); plain(); + Crunch*[4] pool; + let answers = 0, best = 0; + for (int i = 0; i < W; i++) { + pool[i] = rt.new(Crunch); pool[i].spawn(); + let me = i; + pool[i].recv(fn(int k) { + answers++; if (k > best) best = k; + color(4); rt.put_s(" worker "); rt.put_n(me); plain(); + rt.put_s(" ["); rt.put_n(me * 20000 + 1); rt.put_s(".."); rt.put_n(me * 20000 + 20000); rt.put_s("] longest chain "); + color(6); rt.put_n(k); plain(); rt.put_c(' '); bar(k / 8, 4 + me % 3); rt.put_c(10); + return 0; + }); + pool[i].send(i * 20000 + 1); + } + for (int i = 0; i < W; i++) pool[i].await(); + for (int i = 0; i < W; i++) pool[i].close(); + + color(5); rt.put_s("\n== summary ==\n"); plain(); + rt.put_s(" ring hops: "); color(3); rt.put_n(hops); plain(); rt.put_c(10); + rt.put_s(" final token: "); color(3); rt.put_n(last); plain(); rt.put_c(10); + rt.put_s(" worker replies: "); color(3); rt.put_n(answers); plain(); rt.put_c(10); + rt.put_s(" longest chain: "); color(3); rt.put_n(best); plain(); rt.put_c(10); + rt.put_s(" caller's ring[0].seen: "); color(3); rt.put_n(ring[0].seen); plain(); + rt.put_s(" (state stayed in the worker)\n"); + return 0; +} |
