4 ms·
I am deeply interested in the multithreading, parallelism, async and coroutine design space and I journal about it everyday in my ideas journal. I am also inter
by samsquire 3y ago
I am deeply interested in the multithreading, parallelism, async and coroutine design space and I journal about it everyday in my ideas journal. I am also interested in cancellation.
I wrote a toy very simple 1 scheduler thread:M kernel threads:N lightweight thread runtime in terrible Rust, C and Java.
Hot loops use a structure for its limit and looping variable. Then to cancel the loop, you set the looping variable to the limit from a scheduling thread, cancelling the loop. This is used for process switching and scheduling but it can also be used for cancellation.
Can create very responsive code this way, it's even possible to cancel while (true) loops by replacing them with while (!preempted) {}.
https://github.com/samsquire/preemptible-thread https://github.com/samsquire/preemptible-thread
There is potential for a race, but that can be detected and worked around.
- girvo 3y agoSilly question, because I’ve been dealing with exactly this complexity in FreeRTOS and haven’t been able to solve it, but if you’re doing while (!preempted) doesn’t that mean that the preemption can only stop the loop after the current iteration of it finishes? I’ll dig into your code, but I’ve not been able to think of a (user space) way of cancelling a blocked thread (such that the thread is properly cleaned up/scope closes and destructors are called) myself
- brabel 3y agoAs far as I know, it' basically impossible to interrupt a blocking IO call unless you're using something designed for preemptive scheduling, like epoll[1]. A Java Thread can be interrupted, but only if the runnable implementation "cooperates"... if it's blocking on a synchronous socket "read", it's going to stay blocked even after the Thread has been interrupted. In a language like Dart, where async is mostly the default, it's possible to cancel every job running on a "Zone"[2] because the mechanism used for creating a new Future is interceptable by application code, which is pretty cool. Kotlin coroutines have a mechanism for cancellation built-in[3] and it's also "cooperative", i.e. all suspend functions will cancel within the coroutine context, but a rogue coroutine that blocks on a blocking IO call will remain blocked (as I understand it - not sure if Kotlin provides ways to completely avoid blocking IO). So I guess whatever language you use, there has to be points of "yielding" control where cancellation can occur... the `while (!preempted)` check is just one of them. [1] https://man7.org/linux/man-pages/man7/epoll.7.html https://man7.org/linux/man-pages/man7/epoll.7.html [2] https://dart.dev/articles/archive/zones https://dart.dev/articles/archive/zones [3] https://kotlinlang.org/docs/cancellation-and-timeouts.html#cancellation-is-cooperative https://kotlinlang.org/docs/cancellation-and-timeouts.html#c...`
- Matthias247 3y agoYou can interrupt blocking IO calls using signals, so there is an alternative to using epoll/select for this. It can also be used to implement timeouts if another userspace thread tracks the IO operation duration and sends a signal to the IO blocked thread if the max duration has expired. I think Java thread interruption for blocking IO might be based on signals and is not using nonblocking IO under the hood. But I would need to read JDK source again to tell for sure
- girvo 3y agoYeah signals is what I’ve seen too, though I’m unsure if newlib + FreeRTOS has enough to make ‘em work! I’ll have a look though.
- samsquire 3y agoYes that's right. I think as the other commentator said that you would need to use signals to interrupt within between elements of a loop. I try not interrupt a hot for loop or while true loop to reduce performance impact of an if statement in a loop. register_loop(loops, 0, 1000000); for (; loops[0].loop_variable < loops[0].limit ; loops[0].loop_variable++) { // Hot loop here } It's difficult from another thread to know where in the loop another thread is in. Then from another thread loops[0].loop_variable = loops[0].limit
- progbits 3y agoIf the loop uses loop_variable within its body this can cause a data race. Imagine: for (...) { int x = array[loops[0].loop_variable]; array[loops[0].loop_variable] = calculation(x); } If the loop_variable = limit from another thread happens after the array read but before the write you get corrupted answer, not just incomplete one. How do you work around that? Storing a copy and only using that? Or does it not matter in your usecases?
- samsquire 3y agoI never used the loop variable inside a loop in my testing but thank you for detecting this! I would change it to this, shouldn't slow down the loop either. for (int i = 0; loops[0].loop_variable < loops[0].limit ; i = loops[0].loop_variable, loops[0].loop_variable++) { // Hot loop here }