Something went wrong. Try again.
An experimental alternative to Rust sans-io that enables the direct style
Something went wrong. Try again.
3.5 kB · 92 lines
Java
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293import java.util.concurrent.CompletableFuture;import java.util.function.LongSupplier;
/// Drives one automaton over FFI.////// The automaton is either the effect-routine greeter or the capabilities/// greeter; both are spawned into the same table on the Rust side and answer/// to the same calls, so the only difference here is which `spawn` runs.////// Each `resume` returns a batch of effects. Fire-and-forget effects are/// performed inline; the awaited effect (always last) is answered with the/// matching `effectRoutineReply*` call on the automaton handle, and the next/// `resume` is scheduled — immediately for answers the host has to hand, later/// for `Sleep`. Every step is a fresh pool task, so the automaton hops threads.final class EffectRoutineDriver implements Scheduler.Machine { private final Native lib; private final Host host; private final long handle; private final CompletableFuture<Void> done = new CompletableFuture<>();
EffectRoutineDriver(Native lib, Host host) { this(lib, host, lib::effectRoutineSpawn); }
EffectRoutineDriver(Native lib, Host host, LongSupplier spawn) { this.lib = lib; this.host = host; this.handle = spawn.getAsLong(); }
@Override public CompletableFuture<Void> run(Scheduler scheduler) { scheduler.submit(() -> step(scheduler)); return done; }
private void step(Scheduler scheduler) { var out = lib.effectRoutineResume(handle);
if (out.status() < 0) { host.write("<error: " + Native.describe(out.status()) + ">"); lib.effectRoutineFree(handle); done.completeExceptionally(new IllegalStateException(Native.describe(out.status()))); return; }
Runnable next = () -> step(scheduler); long sleepMillis = -1;
for (var effect : Codec.decode(out.bytes())) { switch (effect) { case Codec.Effect.Write w -> host.write(w.text()); case Codec.Effect.ReadLine r -> check(lib.effectRoutineReplyString(handle, host.readLine())); case Codec.Effect.Lookup l -> check(lib.effectRoutineReplyString(handle, Host.greetingFor(l.name()))); case Codec.Effect.Count c -> check(lib.effectRoutineReplyU32(handle, host.count())); case Codec.Effect.Sleep s -> { // Fulfil the reply when the timer fires, then resume. sleepMillis = s.millis(); next = () -> { check(lib.effectRoutineReplyUnit(handle)); step(scheduler); }; } } }
switch (out.status()) { case Native.AWAITING -> { if (sleepMillis >= 0) { scheduler.after(sleepMillis, next); } else { scheduler.submit(next); } } case Native.COMPLETE -> { lib.effectRoutineFree(handle); done.complete(null); } default -> { host.write("<stalled>"); lib.effectRoutineFree(handle); done.completeExceptionally(new IllegalStateException("stalled")); } } }
private static void check(int code) { if (code < 0) { throw new IllegalStateException("reply failed: " + Native.describe(code)); } }}