blob: 006fca19219e7bf2b1b25c8ee3a35aa624d74e05 [file]
import { Layout, Context } from "rive/host";
import { Job, Jobs } from "rive/jobs";
import { RegionView, Ring, SharedRegion, Snapshot } from "rive/shared";
export class Point {
x: f64 = 0;
y: f64 = 0;
}
@job export function bumpAtomic(region: SharedRegion, times: i32): i32 {
for (let i = 0; i < times; i++) {
region.atomicAdd(0, 1);
}
return times;
}
@job export function bumpLocked(region: SharedRegion, times: i32): i32 {
for (let i = 0; i < times; i++) {
region.exclusive((view: RegionView): void => {
view.setU32(4, view.getU32(4) + 1);
});
}
return times;
}
@job export function produce(ring: Ring<Point>, count: i32): i32 {
for (let i = 0; i < count; i++) {
let p = new Point();
p.x = <f64>i;
ring.send(p);
}
return count;
}
@job export function publishes(snapshot: Snapshot<Point>, count: i32): i32 {
for (let i = 0; i < count; i++) {
let p = new Point();
p.x = <f64>i;
snapshot.publish(p);
}
return count;
}
@job export function trapsHolding(region: SharedRegion): i32 {
region.exclusive((view: RegionView): void => {
throw new Error("trap while holding");
});
return 0;
}
// Each step polls its jobs across advances rather than joining them, so no
// one call waits on many jobs within its budget.
export class Main extends Layout {
context: Context? = null;
region: SharedRegion = SharedRegion.create(16);
ring: Ring<Point> = Ring.create<Point>(4096);
snapshot: Snapshot<Point> = Snapshot.create<Point>();
poisoned: SharedRegion = SharedRegion.create(8);
jobs: Array<Job<i32>> = new Array<Job<i32>>();
step: i32 = 0;
override init(context: Context): bool {
this.context = context;
for (let i = 0; i < 8; i++) {
this.jobs.push(Jobs.run(bumpAtomic, this.region, 100));
this.jobs.push(Jobs.run(bumpLocked, this.region, 50));
}
// A handle rebuilt from an id takes its own hold, and releasing it
// inside exclusive still unlocks.
let probe = SharedRegion.create(8);
let rebuilt = new SharedRegion(probe.id);
rebuilt.exclusive((view: RegionView): void => {
rebuilt.release();
});
let unlocked = probe.tryExclusive((view: RegionView): void => {});
context.log("shared rebuilt handle " + (probe.size == 8 && unlocked ? "held its own" : "took the creator's"));
// The view reaches the held region after a release, value bounds too.
let kept = SharedRegion.create(64);
let loaded = new Array<f64>();
kept.exclusive((view: RegionView): void => {
kept.release();
let p = new Point();
p.x = 7;
view.storeValue<Point>(0, p);
loaded.push(view.loadValue<Point>(0).x);
});
context.log("shared value after release " + loaded[0].toString());
return true;
}
override advance(seconds: f64): bool {
let context = this.context!;
for (let i = 0; i < this.jobs.length; i++) {
if (this.jobs[i].running) {
return true;
}
}
let last = this.jobs.length > 0 ? this.jobs[this.jobs.length - 1].peek() : null;
this.jobs = new Array<Job<i32>>();
this.step++;
if (this.step == 1) {
let counts = new Array<u32>();
this.region.exclusive((view: RegionView): void => {
counts.push(view.getU32(0));
counts.push(view.getU32(4));
});
context.log("shared atomic " + counts[0].toString() + " locked " + counts[1].toString());
this.jobs.push(Jobs.run(produce, this.ring, 20));
} else if (this.step == 2) {
let sum = 0.0;
let received = 0;
let p = new Point();
while (this.ring.receiveInto(p)) {
sum += p.x;
received++;
}
context.log("shared ring " + received.toString() + " sum " + sum.toString());
this.jobs.push(Jobs.run(publishes, this.snapshot, 5));
} else if (this.step == 3) {
context.log("shared snapshot " + this.snapshot.latest()!.value.x.toString());
this.jobs.push(Jobs.run(trapsHolding, this.poisoned));
} else if (this.step == 4) {
let result = last!;
context.log("shared trap " + (result.ok ? "ok" : "failed") + " poisoned " + (this.poisoned.poisoned ? "yes" : "no"));
this.poisoned.exclusive((view: RegionView): void => {});
context.log("shared lock freed");
}
return true;
}
}