I was reading source code of glibc.
In function void *__libc_malloc(size_t bytes):
void *__libc_malloc(size_t bytes) { mstate ar_ptr; void *victim; _Static_assert(PTRDIFF_MAX <= SIZE_MAX / 2, "PTRDIFF_MAX is not more than half of SIZE_MAX"); if (!__malloc_initialized) ptmalloc_init(); ... }
It shows that if the first thread was created, it calls ptmalloc_init(), and links thread_arena with main_arena, and sets __malloc_initialized to true.
On the other hand, the second thread was blocked by the following code in ptmalloc_init():
static void ptmalloc_init(void) { if (__malloc_initialized) return; __malloc_initialized = true; thread_arena = &main_arena; malloc_init_state(&main_arena); ...
Thus the thread_arena of the second thread is NULL, and it has to mmap()
additional arena.
My question is:
It seems possible to cause race condition because there’s no any lock with __malloc_initialized, and thread_arenas of the first thread and second thread may both link with main_arena, why not use lock to protect __malloc_initialized?
Advertisement
Answer
It seems possible to cause race condition because there’s no any lock with
__malloc_initialized
It is impossible1 for a program to create a second running thread without having called an allocation routine (and therefore ptmalloc_init
) while it was still single-threaded.
Because of that, ptmalloc_init
can assume that it runs while there is only a single thread.
1Why is it impossible? Because creating a thread itself calls calloc
.
For example, in this program:
#include <pthread.h> void *fn(void *p) { return p; } int main() { pthread_t tid; pthread_create(&tid, NULL, fn, NULL); pthread_join(tid, NULL); return 0; }
ptmalloc_init
is called here (only a single thread exists at that point):
Breakpoint 2, ptmalloc_init () at /usr/src/debug/glibc-2.34-42.fc35.x86_64/malloc/arena.c:283 283 if (__malloc_initialized) (gdb) bt #0 ptmalloc_init () at /usr/src/debug/glibc-2.34-42.fc35.x86_64/malloc/arena.c:283 #1 __libc_calloc (n=17, elem_size=16) at malloc.c:3526 #2 0x00007ffff7fdd6c3 in calloc (b=16, a=17) at ../include/rtld-malloc.h:44 #3 allocate_dtv (result=result@entry=0x7ffff7dae640) at ../elf/dl-tls.c:375 #4 0x00007ffff7fde0e2 in __GI__dl_allocate_tls (mem=mem@entry=0x7ffff7dae640) at ../elf/dl-tls.c:634 #5 0x00007ffff7e514e5 in allocate_stack (stacksize=<synthetic pointer>, stack=<synthetic pointer>, pdp=<synthetic pointer>, attr=0x7fffffffde30) at /usr/src/debug/glibc-2.34-42.fc35.x86_64/nptl/allocatestack.c:429 #6 __pthread_create_2_1 (newthread=0x7fffffffdf58, attr=0x0, start_routine=0x401136 <fn>, arg=0x0) at pthread_create.c:648 #7 0x0000000000401167 in main () at p.c:7