mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-05-30 08:37:29 +03:00
Fix FreeBSD postfork child handler never being called: FreeBSD's libthr calls _malloc_postfork in both parent and child (see freebsd-src lib/libthr/thread/thr_fork.c), but jemalloc mapped it to the parent handler only. Detect the child via getpid() and route to jemalloc_postfork_child, which resets nthreads and rebuilds the descriptor queue. Remove the child_survivor_bytes vs pre_survivor_bytes comparison: on macOS where jemalloc registers as the default zone, internal allocations during the postfork handler (pthread_mutex_init) can inflate the surviving thread's tcache. Add double-fork test to verify prefork pid is refreshed correctly when a child process forks again.
182 lines
4.5 KiB
C
182 lines
4.5 KiB
C
#include "jemalloc/internal/jemalloc_preamble.h"
|
|
#include "jemalloc/internal/jemalloc_internal_includes.h"
|
|
|
|
#include "jemalloc/internal/arenas_management.h"
|
|
#include "jemalloc/internal/ctl.h"
|
|
#include "jemalloc/internal/jemalloc_fork.h"
|
|
#include "jemalloc/internal/jemalloc_init.h"
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* The following functions are used by threading libraries for protection of
|
|
* malloc during fork().
|
|
*/
|
|
|
|
#ifdef JEMALLOC_MUTEX_INIT_CB
|
|
/*
|
|
* When JEMALLOC_MUTEX_INIT_CB is defined, pthread_atfork registration is
|
|
* skipped and the platform calls _malloc_prefork/_malloc_postfork directly.
|
|
* FreeBSD's libthr calls _malloc_postfork in both parent and child. Detect
|
|
* the child by pid change so we route to jemalloc_postfork_child, which resets
|
|
* per-arena state the parent handler does not touch (nthreads, descriptor
|
|
* queues). The check is harmless on any platform that only calls
|
|
* _malloc_postfork in the parent.
|
|
*/
|
|
static pid_t jemalloc_prefork_pid;
|
|
#endif
|
|
|
|
#ifndef JEMALLOC_MUTEX_INIT_CB
|
|
void
|
|
jemalloc_prefork(void)
|
|
#else
|
|
JEMALLOC_EXPORT void
|
|
_malloc_prefork(void)
|
|
#endif
|
|
{
|
|
tsd_t *tsd;
|
|
unsigned i, j, narenas;
|
|
arena_t *arena;
|
|
|
|
#ifdef JEMALLOC_MUTEX_INIT_CB
|
|
if (!malloc_initialized()) {
|
|
return;
|
|
}
|
|
#endif
|
|
assert(malloc_initialized());
|
|
|
|
#ifdef JEMALLOC_MUTEX_INIT_CB
|
|
jemalloc_prefork_pid = getpid();
|
|
#endif
|
|
|
|
tsd = tsd_fetch();
|
|
|
|
narenas = narenas_total_get();
|
|
|
|
witness_prefork(tsd_witness_tsdp_get(tsd));
|
|
/* Acquire all mutexes in a safe order. */
|
|
ctl_prefork(tsd_tsdn(tsd));
|
|
tcache_prefork(tsd_tsdn(tsd));
|
|
arenas_management_prefork(tsd_tsdn(tsd));
|
|
if (have_background_thread) {
|
|
background_thread_prefork0(tsd_tsdn(tsd));
|
|
}
|
|
prof_prefork0(tsd_tsdn(tsd));
|
|
if (have_background_thread) {
|
|
background_thread_prefork1(tsd_tsdn(tsd));
|
|
}
|
|
/* Break arena prefork into stages to preserve lock order. */
|
|
for (i = 0; i < 9; i++) {
|
|
for (j = 0; j < narenas; j++) {
|
|
if ((arena = arena_get(tsd_tsdn(tsd), j, false))
|
|
!= NULL) {
|
|
switch (i) {
|
|
case 0:
|
|
arena_prefork0(tsd_tsdn(tsd), arena);
|
|
break;
|
|
case 1:
|
|
arena_prefork1(tsd_tsdn(tsd), arena);
|
|
break;
|
|
case 2:
|
|
arena_prefork2(tsd_tsdn(tsd), arena);
|
|
break;
|
|
case 3:
|
|
arena_prefork3(tsd_tsdn(tsd), arena);
|
|
break;
|
|
case 4:
|
|
arena_prefork4(tsd_tsdn(tsd), arena);
|
|
break;
|
|
case 5:
|
|
arena_prefork5(tsd_tsdn(tsd), arena);
|
|
break;
|
|
case 6:
|
|
arena_prefork6(tsd_tsdn(tsd), arena);
|
|
break;
|
|
case 7:
|
|
arena_prefork7(tsd_tsdn(tsd), arena);
|
|
break;
|
|
case 8:
|
|
arena_prefork8(tsd_tsdn(tsd), arena);
|
|
break;
|
|
default:
|
|
not_reached();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
prof_prefork1(tsd_tsdn(tsd));
|
|
stats_prefork(tsd_tsdn(tsd));
|
|
}
|
|
|
|
#ifndef JEMALLOC_MUTEX_INIT_CB
|
|
void
|
|
jemalloc_postfork_parent(void)
|
|
#else
|
|
JEMALLOC_EXPORT void
|
|
_malloc_postfork(void)
|
|
#endif
|
|
{
|
|
tsd_t *tsd;
|
|
unsigned i, narenas;
|
|
|
|
#ifdef JEMALLOC_MUTEX_INIT_CB
|
|
if (!malloc_initialized()) {
|
|
return;
|
|
}
|
|
if (getpid() != jemalloc_prefork_pid) {
|
|
jemalloc_postfork_child();
|
|
return;
|
|
}
|
|
#endif
|
|
assert(malloc_initialized());
|
|
|
|
tsd = tsd_fetch();
|
|
|
|
witness_postfork_parent(tsd_witness_tsdp_get(tsd));
|
|
/* Release all mutexes, now that fork() has completed. */
|
|
stats_postfork_parent(tsd_tsdn(tsd));
|
|
for (i = 0, narenas = narenas_total_get(); i < narenas; i++) {
|
|
arena_t *arena;
|
|
|
|
if ((arena = arena_get(tsd_tsdn(tsd), i, false)) != NULL) {
|
|
arena_postfork_parent(tsd_tsdn(tsd), arena);
|
|
}
|
|
}
|
|
prof_postfork_parent(tsd_tsdn(tsd));
|
|
if (have_background_thread) {
|
|
background_thread_postfork_parent(tsd_tsdn(tsd));
|
|
}
|
|
arenas_management_postfork_parent(tsd_tsdn(tsd));
|
|
tcache_postfork_parent(tsd_tsdn(tsd));
|
|
ctl_postfork_parent(tsd_tsdn(tsd));
|
|
}
|
|
|
|
void
|
|
jemalloc_postfork_child(void) {
|
|
tsd_t *tsd;
|
|
unsigned i, narenas;
|
|
|
|
assert(malloc_initialized());
|
|
|
|
tsd = tsd_fetch();
|
|
|
|
witness_postfork_child(tsd_witness_tsdp_get(tsd));
|
|
/* Release all mutexes, now that fork() has completed. */
|
|
stats_postfork_child(tsd_tsdn(tsd));
|
|
for (i = 0, narenas = narenas_total_get(); i < narenas; i++) {
|
|
arena_t *arena;
|
|
|
|
if ((arena = arena_get(tsd_tsdn(tsd), i, false)) != NULL) {
|
|
cache_bin_array_descriptor_t *desc =
|
|
tcache_postfork_arena_descriptor(
|
|
tsd_tsdn(tsd), arena);
|
|
arena_postfork_child(tsd_tsdn(tsd), arena, desc);
|
|
}
|
|
}
|
|
prof_postfork_child(tsd_tsdn(tsd));
|
|
if (have_background_thread) {
|
|
background_thread_postfork_child(tsd_tsdn(tsd));
|
|
}
|
|
arenas_management_postfork_child(tsd_tsdn(tsd));
|
|
tcache_postfork_child(tsd_tsdn(tsd));
|
|
ctl_postfork_child(tsd_tsdn(tsd));
|
|
}
|