1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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;
}
|