> - 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
Re:Re: 100k Threads? (2002)
by semiquaver on Saturday September 23, 2023 @09:16AM
(#37622964)
The docs there hint at how it’s likely done in go: the runtime uses a signal handler to preempt code that hasn’t yielded the scheduler recently enough.
Interesting! My info is way out of date then. Thanks for the link.
IIRC the Erlang VM does the second (schedule on function call). Since Erlang is a functional language and loops are done via recursion, this works out fine.
Yes, on every function call, also some internal functions implemented in native code are instrumented with those checks. On average thread is switched out after about 2000 "reductions" as they are called. Also there's an optimization, where if you send something to another thread and you are waiting for reply, VM switches instantly to the other thread, which makes some message sending equivalent to a simple function call.
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.