3 ms·
I think that developers have minimal scheduling primitives available to them, to schedule complicated work, in the order and timings you want it to have. I don
by samsquire 3y ago
I think that developers have minimal scheduling primitives available to them, to schedule complicated work, in the order and timings you want it to have.
I don't like hardcoding functions in coroutine pipelines. Depending on the ordering of your pipeline, you might have to create things and then refer to them, because of the forward reference problem.
Here's my stackoverflow question for what I'm getting at:
https://stackoverflow.com/questions/74420108/whats-the-canonical-approach-to-creating-multiple-producers-and-consumers-with https://stackoverflow.com/questions/74420108/whats-the-canon...
Coordinating work between independent threads of execution is adhoc and not really well developed. 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.
Go's and Occam's CSP is pretty powerful.
I've noticed that people build turing completeness ontop of existing languages, probably due to the lack of expressivity of the original programming langauge to take turingness as an input.
- noobermin 3y agoI mean, this is an interesting thing to think about but isn't at all what the article is about...coroutines have many uses may be and are often related to async work in general but OP is using coroutines just as a pauseable function (in good old asm, you know how computers actually just work, this is just jumping into a subroutine).
- samsquire 3y agoI think the article implements stackful coroutines since the yield calls a function where control flow jumps to, it eventually returns to the instruction(s) after the yield statement. (That is, it's not a JMP)
- xigency 3y agoI’d recommend exploring some with Scheme. Somewhere between writing code in continuation-passing style, using macros, and using call-with-current-continuation it should be able to build any of these mechanisms in a clean way. Maybe there’s a prototype for a better construct there. Then it’s on other language developers to support these capabilities as well. Because looking at all the examples listed in the article, none of them seem like very idioms.
- samsquire 3y agoThanks for your reply. I have looked into shift and reset to the point I think I understand it (and callCC but it actually changes the execution context or can be seen as an AST transformation) (This wiki page helped me understand delimited continuations: https://wiki.haskell.org/Library/CC-delcont https://wiki.haskell.org/Library/CC-delcont ) I am interested in algebraic effects too, but would like to understand them from a assembly point of view and such as exceptions. My Lisp-like experience is only with Clojure, and it is delightful applying methods so trivially. I just find other people's LISP hard to read!
- 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?
- synergy20 3y agoor just use dds as ros2 does, or use something like zeromq?