9. Threads¶
9.1. Thread safety¶
The MPS is designed to run in an environment with multiple threads all calling into the MPS. Some code is known to operate with exclusive access to the data it manipulates (for example, allocation via allocation points, in the common case where the buffer does not need to be refilled, and location dependencies), so this code is safe. For the rest of the code, shared data structures are locked by the use of a single lock per arena. This lock is claimed on entry to the MPS and released on exit from it. So there is at most a single thread (per arena) running “inside” the MPS at a time.
9.2. Thread registration¶
In order to scan a thread’s registers for references (which happens at
each flip), the MPS needs to be able to suspend that thread,
and in order to gain exclusive atomic access to memory in order to
scan it, the MPS needs to be able to suspend all threads that might
access that memory. This means that threads must be registered with
the MPS by calling mps_thread_reg()
(and thread roots created;
see Thread roots).
A thread must be registered with an arena if:
its control stack and registers form a root (this is enforced by
mps_root_create_thread()
); orit reads or writes from a location in an automatically managed pool in the arena.
However, some automatically managed pool classes may be more liberal than this. See the documentation for the pool class.
9.3. Signal and exception handling issues¶
Warning
On Linux and FreeBSD, the MPS suspends and resumes threads by
sending them signals. There’s a shortage of available signals that
aren’t already dedicated to other purposes (for example, ValGrind
uses SIGUSR1
and SIGUSR2
), so the MPS uses SIGXCPU
and
SIGXFSZ
. This means that programs must not mask or handle
either of these signals.
If your program needs to mask or handle either of these signals, then you can configure the MPS to use another pair of signals of your choosing, by defining these preprocessor constants:
-
CONFIG_PTHREADEXT_SIGSUSPEND
¶ If this preprocessor constant is defined, its definition names the signal used to suspend a thread. For example:
cc -DCONFIG_PTHREADEXT_SIGSUSPEND=SIGUSR1 -c mps.c
-
CONFIG_PTHREADEXT_SIGRESUME
¶ If this preprocessor constant is defined, its definition names the signal used to resume a thread. For example:
cc -DCONFIG_PTHREADEXT_SIGSUSPEND=SIGUSR2 -c mps.c
Warning
The MPS uses barriers (1) to protect memory from the client program and handles the signals that result from barrier hits.
On Linux and FreeBSD, your program must not mask or handle
SIGSEGV
.On Windows, you must not install a first-chance exception handler.
On macOS, you must not install a thread-local Mach exception handler for
EXC_BAD_ACCESS
exceptions.
All of these things are, in fact, possible, but your program must co-operate with the MPS. At present, there’s no documented mechanism for co-operating: if you are in this situation, please contact us.
9.4. Fork safety¶
On Linux, FreeBSD and macOS, the MPS makes a best-effort attempt to
continue running in the child process after a call to fork()
,
even if the client program was running multiple
threads at the point where the call is made to fork()
.
Warning
POSIX offers little or nothing in the way of guarantees about the situation of a child process running after a multi-threaded parent forked. The specification says:
A process shall be created with a single thread. If a multi-threaded process calls
fork()
, the new process shall contain a replica of the calling thread and its entire address space, possibly including the states of mutexes and other resources. Consequently, to avoid errors, the child process may only execute async-signal-safe operations until such time as one of theexec()
functions is called.
Note
Although only one thread is created in the child process, any
threads in the parent process that were registered with the MPS by
calling mps_thread_reg()
must still be deregistered, by
calling mps_thread_dereg()
, before the arena is destroyed.
9.5. Thread interface¶
-
mps_thr_t
¶ The type of registered thread descriptions.
In a multi-threaded environment where incremental garbage collection is used, threads must be registered with the MPS by calling
mps_thread_reg()
so that the MPS can suspend them as necessary in order to have exclusive access to their state.Even in a single-threaded environment it may be necessary to register a thread with the MPS so that its control stack and registers can be registered as a root by calling
mps_root_create_thread()
.
-
mps_res_t
mps_thread_reg
(mps_thr_t *thr_o, mps_arena_t arena)¶ Register the current thread with an arena.
thr_o
points to a location that will hold the address of the registered thread description, if successful.arena
is the arena.Returns
MPS_RES_OK
if successful, or another result code if not.A thread must be registered with an arena if it ever uses a pointer to a location in an automatically managed pool belonging to that arena.
Note
It is recommended that all threads be registered with all arenas.
It is an error if a thread terminates while it is registered. The client program must call
mps_thread_dereg()
first.
-
void
mps_thread_dereg
(mps_thr_t thr)¶ Deregister a thread.
thr
is the description of the thread.After calling this function, the thread whose registration with an arena was recorded in
thr
must not read or write from a location in an automatically managed pool belonging to that arena.Note
Some pool classes may be more liberal about what a thread may do after it has been deregistered. See the documentation for the pool class.
Note
It is recommended that threads be deregistered only when they are just about to exit.