The following are references about (non-robust) futexes:
The following are references about robust futexes on the side of the Linux kernel:
- set_robust_list(2)
- A description of what robust futexes are
- The robust futex ABI
- Robust futexes - a new approach
There aren't many references on the internet about the use of robust
futexes (specifically robust_list
). The only implementation I found is from
glibc. The following is a summary of the information contained in
the glibc-2.36
source code.
-
struct robust_list_head
is an userspace per-thread data structure used to keep track of futexes that the thread has acquired. It is registered with the Linux kernel viaset_robust_list
so that when the thread exits (either normally or abnormally), the kernel can iterate through the list and release the futexes that the thread has acquired.struct robust_list_head
is defined inlinux/futex.h
as part of the Linux syscall ABI. -
In glibc, the
struct robust_list_head
is stored in therobust_list
field ofstruct pthread
, which is the struct pointed to bypthread_t
. -
When a process first starts,
robust_head
is initialized and registered with the kernel withset_robust_list
system call. The relevant code is in__tls_init_tp
:#0 __tls_init_tp () at ../sysdeps/nptl/dl-tls_init_tp.c:106 #1 0x00007ffff7fe3e28 in init_tls (naudit=naudit@entry=0) at ./elf/rtld.c:821 #2 0x00007ffff7fe766c in dl_main (phdr=<optimized out>, phnum=<optimized out>, user_entry=<optimized out>, auxv=<optimized out>) at ./elf/rtld.c:2045 #3 0x00007ffff7fe285c in _dl_sysdep_start (start_argptr=start_argptr@entry=0x7fffffffdd90, dl_main=dl_main@entry=0x7ffff7fe4900 <dl_main>) at ../elf/dl-sysdep.c:256 #4 0x00007ffff7fe45b8 in _dl_start_final (arg=0x7fffffffdd90) at ./elf/rtld.c:507 #5 _dl_start (arg=0x7fffffffdd90) at ./elf/rtld.c:596 #6 0x00007ffff7fe32b8 in _start () from /lib64/ld-linux-x86-64.so.2
-
When a new thread is created, tht content of the
robust_list
is initialized inallocate_stack
:#0 allocate_stack (stacksize=<synthetic pointer>, stack=<synthetic pointer>, pdp=<synthetic pointer>, attr=0x7fffffffdb50) at ./nptl/allocatestack.c:555 #1 __pthread_create_2_1 (newthread=<optimized out>, attr=<optimized out>, start_routine=<optimized out>, arg=<optimized out>) at ./nptl/pthread_create.c:647
Then
set_robust_list
is called instart_thread
,#0 start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:400 #1 create_thread (pd=pd@entry=0x7ffff7d7e640, attr=attr@entry=0x7fffffffdb50, stopped_start=stopped_start@entry=0x7fffffffdb4e, stackaddr=stackaddr@entry=0x7ffff757e000, stacksize=8388352, thread_ran=thread_ran@entry=0x7fffffffdb4f) at ./nptl/pthread_create.c:285 #2 0x00007ffff7e17280 in __pthread_create_2_1 (newthread=<optimized out>, attr=<optimized out>, start_routine=<optimized out>, arg=<optimized out>) at ./nptl/pthread_create.c:828
-
When a mutex is acquired, it needs to be added to the
robust_list
of the current thread so the kernel can see it when the thread exits. Similarly, when a mutex is released, it should be removed. The relevant code is in nptl/descr.h -
In glibc, the code for locking/unlocking robust mutex is in pthread_mutex_lock.c and pthread_mutex_unlock.c