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; }