4 ms·
Virtual threads, or green threads use a co-operative threading/coroutine model on top of the preemptive threads provided by the OS. In addition to Java virtual
by exDM69 3y ago
Virtual threads, or green threads use a co-operative threading/coroutine model on top of the preemptive threads provided by the OS.
In addition to Java virtual threads, similar systems exist in Go goroutines, Rust async and Haskell green threads for example.
There are upsides and downsides to cooperative userspace threads. An erratic or malicious green thread can hog all the CPU time and starve other threads for example.
An operating system can not trust the userspace to behave, starvation due to misbehaving thread is unacceptable.
So the OS can not implement green threads alone, it needs the userspace for the co-operative switching.
- yetihehe 3y agoIn erlang you have preemptive green threads. They don't block* and having a million on one medium server is not unheard of. * I managed to block a whole erlang VM one day, spoke with Joe Armstrong on chat, but they won't fix this, as it's pretty niche use case.
- darraghenright 3y agoI'm probably not the only one curious to hear more about this :)
- yetihehe 3y agoMake a ordered_bag ets table and try to insert several thousand records in one ets call. It will block whole VM (spinning one core at 100%, doesn't matter how many you have) for several seconds. It does this because ordered bag needs to find each key in table as it inserts it, resulting in n^2 complexity and this needs to be done atomically. So for 1 000 keys you have 1 000 000 comparisons during a VM-wide lock. Solution - don't insert so many records in one call into ets table.
- ninkendo 3y agoI’m more curious to understand how userspace threads can be preemptive. :) I can think of a few ways: - The VM just doesn’t JIT and can decide to stop executing a thread by just not interpreting the next piece of bytecode and switching to another green thread instead (this would be pretty slow due to the lack of JIT) - The VM JITs, but inserts a preamble before every function call saying “Before executing this function, should I switch to another green thread first?”, and thus, so long as you call functions frequently enough, you “preempt” yourself. This is how Go does it, and it’s a well known thing in Go that if you never call a function for a while (like just doing a really huge for loop), the current goroutine doesn’t yield execution and hogs the whole OS thread.
- messe 3y ago> - The VM JITs, but inserts a preamble before every function call saying “Before executing this function, should I switch to another green thread first?”, and thus, so long as you call functions frequently enough, you “preempt” yourself. This is how Go does it, and it’s a well known thing in Go that if you never call a function for a while (like just doing a really huge for loop), the current goroutine doesn’t yield execution and hogs the whole OS thread. I'm not sure of the implementation details, but this hasn't been true for a while in Go. As of Go 1.14, goroutines are asynchronously preemptible, so loops without function calls no longer deadlock the scheduler or GC: https://go.dev/doc/go1.14#runtime https://go.dev/doc/go1.14#runtime
- semiquaver 3y agoThe docs there hint at how it’s done in go and how it could be done in erlang: the runtime monitors how long a given goroutine has been running without yielding the scheduler, and uses a signal handler to interrupt code that has exceeded a 10ms quota of continuous usage. https://medium.com/a-journey-with-go/go-asynchronous-preemption-b5194227371c https://medium.com/a-journey-with-go/go-asynchronous-preempt...
- ninkendo 3y agoInteresting! My info is way out of date then. Thanks for the link.