4 ms·
> I would like to build a rich "process api" that can fork, merge, pause, yield, yield until, drop while, synchronize, wait (latch), react according to events.
by bluetomcat 3y ago
> I would like to build a rich "process api" that can fork, merge, pause, yield, yield until, drop while, synchronize, wait (latch), react according to events. I feel every distributed systems builds this again and again.
Years ago I attempted to build an experimental language with first-class resumable functions. Every function can be invoked by the caller through a special reference type called "quaint". The caller can resume or stop the execution of the function either after reaching a timeout, or after passing a "wait label":
https://github.com/bbu/quaint-lang https://github.com/bbu/quaint-lang
A typical CPU-intensive example where preemption is done by the caller after a certain timeout:
entry
{
fibq: quaint(u32) = ~fibonacci(32 as u32);
ps("At start: "), pu8(fibq@start), pnl();
iter: u32 = 0:u32;
do {
wait fibq for 1000 msec;
ps("Iteration "), pu32(iter++), pnl();
} while !fibq@end;
ps("At end: "), pu8(fibq@end), pnl();
const value: u32 = *fibq;
ps("Reaped value: "), pu32(value), pnl();
}
fibonacci(number: u32): u32
{
if number == 0:u32 || number == 1:u32 {
return number;
} else {
return fibonacci(number - 1:u32) + fibonacci(number - 2:u32);
}
}
An example that uses "wait labels" to suspend execution of the callee at certain points:
entry
{
q: quaint(u64) = ~pointless_function();
wait q until pointless_function::label_a;
ps("At label_a: "), pu8(q@pointless_function::label_a), pnl();
wait q until pointless_function::label_b;
ps("At label_b: "), pu8(q@pointless_function::label_b), pnl();
wait q until pointless_function::label_c;
ps("At label_c: "), pu8(q@pointless_function::label_c), pnl();
wait q;
ps("At end: "), pu8(q@end), pnl();
ps("Result: "), pu64(*q), pnl();
}
pointless_function: u64
{
i: u64 = 0 as u64;
while ++i < 1000000:u64 {
x: vptr = malloc(1024:usize);
free(x);
if i == 300000:u64 {
[label_a]
} elif i == 600000:u64 {
[label_b]
} elif i == 900000:u64 {
[label_c]
}
}
return i;
}
- samsquire 3y agoI really like this. Thank you for your comment and sharing. I have a lightweight 1:M:N runtime (1 scheduler thread, M kernel threads, N lightweight threads) which preempts by setting hot loops to the limit. https://github.com/samsquire/preemptible-thread https://github.com/samsquire/preemptible-thread (Rust, Java and C) How do you preempt code that is running? Would you like to talk more about your idea?
- bluetomcat 3y ago> How do you preempt code that is running? It runs in its own VM with custom instructions. The "wait" statement corresponds to a special instruction that is able to switch to a separate execution context and restore the parent context when necessary. To implement this in native code, some kind of runtime support would be needed. Perhaps a dedicated thread that is able to stop/resume the main thread, changing the contents of the stack and the registers.
- knome 3y agoHa. Amazing! Someone actually implemented a variant of COME FROM of C-INTERCAL infamy in another language. And moreover made it a reasonable and readable variant!