3 ms·
> do something with garbage collection (but what? I’m confused about this still.) It's an almost hilarious technique. In a garbage collected, multi-threaded
by bjourne 3y ago
> do something with garbage collection (but what? I’m confused about
this still.)
It's an almost hilarious technique. In a garbage collected,
multi-threaded virtual machine the runtime may at some point decide it
is time for garbage collection. However, all threads must first be
stopped because allocating memory while the garbage collector runs
leads to badness and/or the garbage collector moves objects referenced
by threads.
There is no direct way to signal to a thread that it should stop and
having the thread poll some flag would hurt performance. Instead the
compiler inserts dummy instructions (safepoints) at the top of every loop body or
function entry point which writes to a page the runtime controls:
mov [addr], eax
Then when it's time to stop all threads, it just write protects the
page! Every thread eventually stumbles upon the bomb. Boom! SIGSEGV!
Boom! SIGSEGV! Boom! When every thread has exploded the garbage
collector can run in peace. Then the runtime removes the write
protection and resumes executing all threads. Something very similar
is used on Windows (though I think it's called events and not signals)
and sometimes you use read-protected pages rather than write-protected
ones, but the principle is the same.
- hayley-patton 3y agoYou probably want to read and not write, as writing will cause a lot of cache ping-pong and slow the mutator down when it doesn't SEGV on the dummy access <https://users.cecs.anu.edu.au/~steveb/pubs/papers/yieldpoint-ismm-2015.pdf#page=8 https://users.cecs.anu.edu.au/~steveb/pubs/papers/yieldpoint...>
- tubs 3y agoHave you got any links to how they continue the threads after the gc has run? I was looking for something similar but couldn't find much.
- themoonisachees 3y agoNaïve approach is to have each segv handler poll something to tell when GC is done, and simply return when it can. State is then restored by the kernel.
- jlokier 3y agoYou could have each thread select(), poll(), or read() a byte, from the same pipe in their SIGSEGV handler. (Those syscalls are async-signal-safe in POSIX.) When it's time to resume, the GC unprotects the page then writes a byte (or enough bytes if read() is used), which wakes all the blocked mutator threads. Those threads also need to synchronise acks with the GC. After the GC protects the guard page, the mutators don't stop mutating immediately, so they must send an ack to the GC thread from their SIGSEGV handler to say when they have stopped. A pipe can be used for this too. Use of a non-mutex-using atomic counter, if available, can speed this by ensuring the GC only wakes once. Similarly the mutator threads must send an ack after they resume before returning from their SIGSEGV handler, to ensure the next GC run does not race with a mutator thread still slowly handling the current GC run On Linux, futex or eventfd are async-signal-safe but non-portable alternatives to pipes. Futex has the advantage of not using a file descriptor and may be faster.