1*8e33eff8Schristos #define JEMALLOC_BACKGROUND_THREAD_C_ 2*8e33eff8Schristos #include "jemalloc/internal/jemalloc_preamble.h" 3*8e33eff8Schristos #include "jemalloc/internal/jemalloc_internal_includes.h" 4*8e33eff8Schristos 5*8e33eff8Schristos #include "jemalloc/internal/assert.h" 6*8e33eff8Schristos 7*8e33eff8Schristos /******************************************************************************/ 8*8e33eff8Schristos /* Data. */ 9*8e33eff8Schristos 10*8e33eff8Schristos /* This option should be opt-in only. */ 11*8e33eff8Schristos #define BACKGROUND_THREAD_DEFAULT false 12*8e33eff8Schristos /* Read-only after initialization. */ 13*8e33eff8Schristos bool opt_background_thread = BACKGROUND_THREAD_DEFAULT; 14*8e33eff8Schristos size_t opt_max_background_threads = MAX_BACKGROUND_THREAD_LIMIT; 15*8e33eff8Schristos 16*8e33eff8Schristos /* Used for thread creation, termination and stats. */ 17*8e33eff8Schristos malloc_mutex_t background_thread_lock; 18*8e33eff8Schristos /* Indicates global state. Atomic because decay reads this w/o locking. */ 19*8e33eff8Schristos atomic_b_t background_thread_enabled_state; 20*8e33eff8Schristos size_t n_background_threads; 21*8e33eff8Schristos size_t max_background_threads; 22*8e33eff8Schristos /* Thread info per-index. */ 23*8e33eff8Schristos background_thread_info_t *background_thread_info; 24*8e33eff8Schristos 25*8e33eff8Schristos /* False if no necessary runtime support. */ 26*8e33eff8Schristos bool can_enable_background_thread; 27*8e33eff8Schristos 28*8e33eff8Schristos /******************************************************************************/ 29*8e33eff8Schristos 30*8e33eff8Schristos #ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER 31*8e33eff8Schristos #include <dlfcn.h> 32*8e33eff8Schristos 33*8e33eff8Schristos static int (*pthread_create_fptr)(pthread_t *__restrict, const pthread_attr_t *, 34*8e33eff8Schristos void *(*)(void *), void *__restrict); 35*8e33eff8Schristos 36*8e33eff8Schristos static void 37*8e33eff8Schristos pthread_create_wrapper_init(void) { 38*8e33eff8Schristos #ifdef JEMALLOC_LAZY_LOCK 39*8e33eff8Schristos if (!isthreaded) { 40*8e33eff8Schristos isthreaded = true; 41*8e33eff8Schristos } 42*8e33eff8Schristos #endif 43*8e33eff8Schristos } 44*8e33eff8Schristos 45*8e33eff8Schristos int 46*8e33eff8Schristos pthread_create_wrapper(pthread_t *__restrict thread, const pthread_attr_t *attr, 47*8e33eff8Schristos void *(*start_routine)(void *), void *__restrict arg) { 48*8e33eff8Schristos pthread_create_wrapper_init(); 49*8e33eff8Schristos 50*8e33eff8Schristos return pthread_create_fptr(thread, attr, start_routine, arg); 51*8e33eff8Schristos } 52*8e33eff8Schristos #endif /* JEMALLOC_PTHREAD_CREATE_WRAPPER */ 53*8e33eff8Schristos 54*8e33eff8Schristos #ifndef JEMALLOC_BACKGROUND_THREAD 55*8e33eff8Schristos #define NOT_REACHED { not_reached(); } 56*8e33eff8Schristos bool background_thread_create(tsd_t *tsd, unsigned arena_ind) NOT_REACHED 57*8e33eff8Schristos bool background_threads_enable(tsd_t *tsd) NOT_REACHED 58*8e33eff8Schristos bool background_threads_disable(tsd_t *tsd) NOT_REACHED 59*8e33eff8Schristos void background_thread_interval_check(tsdn_t *tsdn, arena_t *arena, 60*8e33eff8Schristos arena_decay_t *decay, size_t npages_new) NOT_REACHED 61*8e33eff8Schristos void background_thread_prefork0(tsdn_t *tsdn) NOT_REACHED 62*8e33eff8Schristos void background_thread_prefork1(tsdn_t *tsdn) NOT_REACHED 63*8e33eff8Schristos void background_thread_postfork_parent(tsdn_t *tsdn) NOT_REACHED 64*8e33eff8Schristos void background_thread_postfork_child(tsdn_t *tsdn) NOT_REACHED 65*8e33eff8Schristos bool background_thread_stats_read(tsdn_t *tsdn, 66*8e33eff8Schristos background_thread_stats_t *stats) NOT_REACHED 67*8e33eff8Schristos void background_thread_ctl_init(tsdn_t *tsdn) NOT_REACHED 68*8e33eff8Schristos #undef NOT_REACHED 69*8e33eff8Schristos #else 70*8e33eff8Schristos 71*8e33eff8Schristos static bool background_thread_enabled_at_fork; 72*8e33eff8Schristos 73*8e33eff8Schristos static void 74*8e33eff8Schristos background_thread_info_init(tsdn_t *tsdn, background_thread_info_t *info) { 75*8e33eff8Schristos background_thread_wakeup_time_set(tsdn, info, 0); 76*8e33eff8Schristos info->npages_to_purge_new = 0; 77*8e33eff8Schristos if (config_stats) { 78*8e33eff8Schristos info->tot_n_runs = 0; 79*8e33eff8Schristos nstime_init(&info->tot_sleep_time, 0); 80*8e33eff8Schristos } 81*8e33eff8Schristos } 82*8e33eff8Schristos 83*8e33eff8Schristos static inline bool 84*8e33eff8Schristos set_current_thread_affinity(UNUSED int cpu) { 85*8e33eff8Schristos #if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY) 86*8e33eff8Schristos cpu_set_t cpuset; 87*8e33eff8Schristos CPU_ZERO(&cpuset); 88*8e33eff8Schristos CPU_SET(cpu, &cpuset); 89*8e33eff8Schristos int ret = sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); 90*8e33eff8Schristos 91*8e33eff8Schristos return (ret != 0); 92*8e33eff8Schristos #else 93*8e33eff8Schristos return false; 94*8e33eff8Schristos #endif 95*8e33eff8Schristos } 96*8e33eff8Schristos 97*8e33eff8Schristos /* Threshold for determining when to wake up the background thread. */ 98*8e33eff8Schristos #define BACKGROUND_THREAD_NPAGES_THRESHOLD UINT64_C(1024) 99*8e33eff8Schristos #define BILLION UINT64_C(1000000000) 100*8e33eff8Schristos /* Minimal sleep interval 100 ms. */ 101*8e33eff8Schristos #define BACKGROUND_THREAD_MIN_INTERVAL_NS (BILLION / 10) 102*8e33eff8Schristos 103*8e33eff8Schristos static inline size_t 104*8e33eff8Schristos decay_npurge_after_interval(arena_decay_t *decay, size_t interval) { 105*8e33eff8Schristos size_t i; 106*8e33eff8Schristos uint64_t sum = 0; 107*8e33eff8Schristos for (i = 0; i < interval; i++) { 108*8e33eff8Schristos sum += decay->backlog[i] * h_steps[i]; 109*8e33eff8Schristos } 110*8e33eff8Schristos for (; i < SMOOTHSTEP_NSTEPS; i++) { 111*8e33eff8Schristos sum += decay->backlog[i] * (h_steps[i] - h_steps[i - interval]); 112*8e33eff8Schristos } 113*8e33eff8Schristos 114*8e33eff8Schristos return (size_t)(sum >> SMOOTHSTEP_BFP); 115*8e33eff8Schristos } 116*8e33eff8Schristos 117*8e33eff8Schristos static uint64_t 118*8e33eff8Schristos arena_decay_compute_purge_interval_impl(tsdn_t *tsdn, arena_decay_t *decay, 119*8e33eff8Schristos extents_t *extents) { 120*8e33eff8Schristos if (malloc_mutex_trylock(tsdn, &decay->mtx)) { 121*8e33eff8Schristos /* Use minimal interval if decay is contended. */ 122*8e33eff8Schristos return BACKGROUND_THREAD_MIN_INTERVAL_NS; 123*8e33eff8Schristos } 124*8e33eff8Schristos 125*8e33eff8Schristos uint64_t interval; 126*8e33eff8Schristos ssize_t decay_time = atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED); 127*8e33eff8Schristos if (decay_time <= 0) { 128*8e33eff8Schristos /* Purging is eagerly done or disabled currently. */ 129*8e33eff8Schristos interval = BACKGROUND_THREAD_INDEFINITE_SLEEP; 130*8e33eff8Schristos goto label_done; 131*8e33eff8Schristos } 132*8e33eff8Schristos 133*8e33eff8Schristos uint64_t decay_interval_ns = nstime_ns(&decay->interval); 134*8e33eff8Schristos assert(decay_interval_ns > 0); 135*8e33eff8Schristos size_t npages = extents_npages_get(extents); 136*8e33eff8Schristos if (npages == 0) { 137*8e33eff8Schristos unsigned i; 138*8e33eff8Schristos for (i = 0; i < SMOOTHSTEP_NSTEPS; i++) { 139*8e33eff8Schristos if (decay->backlog[i] > 0) { 140*8e33eff8Schristos break; 141*8e33eff8Schristos } 142*8e33eff8Schristos } 143*8e33eff8Schristos if (i == SMOOTHSTEP_NSTEPS) { 144*8e33eff8Schristos /* No dirty pages recorded. Sleep indefinitely. */ 145*8e33eff8Schristos interval = BACKGROUND_THREAD_INDEFINITE_SLEEP; 146*8e33eff8Schristos goto label_done; 147*8e33eff8Schristos } 148*8e33eff8Schristos } 149*8e33eff8Schristos if (npages <= BACKGROUND_THREAD_NPAGES_THRESHOLD) { 150*8e33eff8Schristos /* Use max interval. */ 151*8e33eff8Schristos interval = decay_interval_ns * SMOOTHSTEP_NSTEPS; 152*8e33eff8Schristos goto label_done; 153*8e33eff8Schristos } 154*8e33eff8Schristos 155*8e33eff8Schristos size_t lb = BACKGROUND_THREAD_MIN_INTERVAL_NS / decay_interval_ns; 156*8e33eff8Schristos size_t ub = SMOOTHSTEP_NSTEPS; 157*8e33eff8Schristos /* Minimal 2 intervals to ensure reaching next epoch deadline. */ 158*8e33eff8Schristos lb = (lb < 2) ? 2 : lb; 159*8e33eff8Schristos if ((decay_interval_ns * ub <= BACKGROUND_THREAD_MIN_INTERVAL_NS) || 160*8e33eff8Schristos (lb + 2 > ub)) { 161*8e33eff8Schristos interval = BACKGROUND_THREAD_MIN_INTERVAL_NS; 162*8e33eff8Schristos goto label_done; 163*8e33eff8Schristos } 164*8e33eff8Schristos 165*8e33eff8Schristos assert(lb + 2 <= ub); 166*8e33eff8Schristos size_t npurge_lb, npurge_ub; 167*8e33eff8Schristos npurge_lb = decay_npurge_after_interval(decay, lb); 168*8e33eff8Schristos if (npurge_lb > BACKGROUND_THREAD_NPAGES_THRESHOLD) { 169*8e33eff8Schristos interval = decay_interval_ns * lb; 170*8e33eff8Schristos goto label_done; 171*8e33eff8Schristos } 172*8e33eff8Schristos npurge_ub = decay_npurge_after_interval(decay, ub); 173*8e33eff8Schristos if (npurge_ub < BACKGROUND_THREAD_NPAGES_THRESHOLD) { 174*8e33eff8Schristos interval = decay_interval_ns * ub; 175*8e33eff8Schristos goto label_done; 176*8e33eff8Schristos } 177*8e33eff8Schristos 178*8e33eff8Schristos unsigned n_search = 0; 179*8e33eff8Schristos size_t target, npurge; 180*8e33eff8Schristos while ((npurge_lb + BACKGROUND_THREAD_NPAGES_THRESHOLD < npurge_ub) 181*8e33eff8Schristos && (lb + 2 < ub)) { 182*8e33eff8Schristos target = (lb + ub) / 2; 183*8e33eff8Schristos npurge = decay_npurge_after_interval(decay, target); 184*8e33eff8Schristos if (npurge > BACKGROUND_THREAD_NPAGES_THRESHOLD) { 185*8e33eff8Schristos ub = target; 186*8e33eff8Schristos npurge_ub = npurge; 187*8e33eff8Schristos } else { 188*8e33eff8Schristos lb = target; 189*8e33eff8Schristos npurge_lb = npurge; 190*8e33eff8Schristos } 191*8e33eff8Schristos assert(n_search++ < lg_floor(SMOOTHSTEP_NSTEPS) + 1); 192*8e33eff8Schristos } 193*8e33eff8Schristos interval = decay_interval_ns * (ub + lb) / 2; 194*8e33eff8Schristos label_done: 195*8e33eff8Schristos interval = (interval < BACKGROUND_THREAD_MIN_INTERVAL_NS) ? 196*8e33eff8Schristos BACKGROUND_THREAD_MIN_INTERVAL_NS : interval; 197*8e33eff8Schristos malloc_mutex_unlock(tsdn, &decay->mtx); 198*8e33eff8Schristos 199*8e33eff8Schristos return interval; 200*8e33eff8Schristos } 201*8e33eff8Schristos 202*8e33eff8Schristos /* Compute purge interval for background threads. */ 203*8e33eff8Schristos static uint64_t 204*8e33eff8Schristos arena_decay_compute_purge_interval(tsdn_t *tsdn, arena_t *arena) { 205*8e33eff8Schristos uint64_t i1, i2; 206*8e33eff8Schristos i1 = arena_decay_compute_purge_interval_impl(tsdn, &arena->decay_dirty, 207*8e33eff8Schristos &arena->extents_dirty); 208*8e33eff8Schristos if (i1 == BACKGROUND_THREAD_MIN_INTERVAL_NS) { 209*8e33eff8Schristos return i1; 210*8e33eff8Schristos } 211*8e33eff8Schristos i2 = arena_decay_compute_purge_interval_impl(tsdn, &arena->decay_muzzy, 212*8e33eff8Schristos &arena->extents_muzzy); 213*8e33eff8Schristos 214*8e33eff8Schristos return i1 < i2 ? i1 : i2; 215*8e33eff8Schristos } 216*8e33eff8Schristos 217*8e33eff8Schristos static void 218*8e33eff8Schristos background_thread_sleep(tsdn_t *tsdn, background_thread_info_t *info, 219*8e33eff8Schristos uint64_t interval) { 220*8e33eff8Schristos if (config_stats) { 221*8e33eff8Schristos info->tot_n_runs++; 222*8e33eff8Schristos } 223*8e33eff8Schristos info->npages_to_purge_new = 0; 224*8e33eff8Schristos 225*8e33eff8Schristos struct timeval tv; 226*8e33eff8Schristos /* Specific clock required by timedwait. */ 227*8e33eff8Schristos gettimeofday(&tv, NULL); 228*8e33eff8Schristos nstime_t before_sleep; 229*8e33eff8Schristos nstime_init2(&before_sleep, tv.tv_sec, tv.tv_usec * 1000); 230*8e33eff8Schristos 231*8e33eff8Schristos int ret; 232*8e33eff8Schristos if (interval == BACKGROUND_THREAD_INDEFINITE_SLEEP) { 233*8e33eff8Schristos assert(background_thread_indefinite_sleep(info)); 234*8e33eff8Schristos ret = pthread_cond_wait(&info->cond, &info->mtx.lock); 235*8e33eff8Schristos assert(ret == 0); 236*8e33eff8Schristos } else { 237*8e33eff8Schristos assert(interval >= BACKGROUND_THREAD_MIN_INTERVAL_NS && 238*8e33eff8Schristos interval <= BACKGROUND_THREAD_INDEFINITE_SLEEP); 239*8e33eff8Schristos /* We need malloc clock (can be different from tv). */ 240*8e33eff8Schristos nstime_t next_wakeup; 241*8e33eff8Schristos nstime_init(&next_wakeup, 0); 242*8e33eff8Schristos nstime_update(&next_wakeup); 243*8e33eff8Schristos nstime_iadd(&next_wakeup, interval); 244*8e33eff8Schristos assert(nstime_ns(&next_wakeup) < 245*8e33eff8Schristos BACKGROUND_THREAD_INDEFINITE_SLEEP); 246*8e33eff8Schristos background_thread_wakeup_time_set(tsdn, info, 247*8e33eff8Schristos nstime_ns(&next_wakeup)); 248*8e33eff8Schristos 249*8e33eff8Schristos nstime_t ts_wakeup; 250*8e33eff8Schristos nstime_copy(&ts_wakeup, &before_sleep); 251*8e33eff8Schristos nstime_iadd(&ts_wakeup, interval); 252*8e33eff8Schristos struct timespec ts; 253*8e33eff8Schristos ts.tv_sec = (size_t)nstime_sec(&ts_wakeup); 254*8e33eff8Schristos ts.tv_nsec = (size_t)nstime_nsec(&ts_wakeup); 255*8e33eff8Schristos 256*8e33eff8Schristos assert(!background_thread_indefinite_sleep(info)); 257*8e33eff8Schristos ret = pthread_cond_timedwait(&info->cond, &info->mtx.lock, &ts); 258*8e33eff8Schristos assert(ret == ETIMEDOUT || ret == 0); 259*8e33eff8Schristos background_thread_wakeup_time_set(tsdn, info, 260*8e33eff8Schristos BACKGROUND_THREAD_INDEFINITE_SLEEP); 261*8e33eff8Schristos } 262*8e33eff8Schristos if (config_stats) { 263*8e33eff8Schristos gettimeofday(&tv, NULL); 264*8e33eff8Schristos nstime_t after_sleep; 265*8e33eff8Schristos nstime_init2(&after_sleep, tv.tv_sec, tv.tv_usec * 1000); 266*8e33eff8Schristos if (nstime_compare(&after_sleep, &before_sleep) > 0) { 267*8e33eff8Schristos nstime_subtract(&after_sleep, &before_sleep); 268*8e33eff8Schristos nstime_add(&info->tot_sleep_time, &after_sleep); 269*8e33eff8Schristos } 270*8e33eff8Schristos } 271*8e33eff8Schristos } 272*8e33eff8Schristos 273*8e33eff8Schristos static bool 274*8e33eff8Schristos background_thread_pause_check(tsdn_t *tsdn, background_thread_info_t *info) { 275*8e33eff8Schristos if (unlikely(info->state == background_thread_paused)) { 276*8e33eff8Schristos malloc_mutex_unlock(tsdn, &info->mtx); 277*8e33eff8Schristos /* Wait on global lock to update status. */ 278*8e33eff8Schristos malloc_mutex_lock(tsdn, &background_thread_lock); 279*8e33eff8Schristos malloc_mutex_unlock(tsdn, &background_thread_lock); 280*8e33eff8Schristos malloc_mutex_lock(tsdn, &info->mtx); 281*8e33eff8Schristos return true; 282*8e33eff8Schristos } 283*8e33eff8Schristos 284*8e33eff8Schristos return false; 285*8e33eff8Schristos } 286*8e33eff8Schristos 287*8e33eff8Schristos static inline void 288*8e33eff8Schristos background_work_sleep_once(tsdn_t *tsdn, background_thread_info_t *info, unsigned ind) { 289*8e33eff8Schristos uint64_t min_interval = BACKGROUND_THREAD_INDEFINITE_SLEEP; 290*8e33eff8Schristos unsigned narenas = narenas_total_get(); 291*8e33eff8Schristos 292*8e33eff8Schristos for (unsigned i = ind; i < narenas; i += max_background_threads) { 293*8e33eff8Schristos arena_t *arena = arena_get(tsdn, i, false); 294*8e33eff8Schristos if (!arena) { 295*8e33eff8Schristos continue; 296*8e33eff8Schristos } 297*8e33eff8Schristos arena_decay(tsdn, arena, true, false); 298*8e33eff8Schristos if (min_interval == BACKGROUND_THREAD_MIN_INTERVAL_NS) { 299*8e33eff8Schristos /* Min interval will be used. */ 300*8e33eff8Schristos continue; 301*8e33eff8Schristos } 302*8e33eff8Schristos uint64_t interval = arena_decay_compute_purge_interval(tsdn, 303*8e33eff8Schristos arena); 304*8e33eff8Schristos assert(interval >= BACKGROUND_THREAD_MIN_INTERVAL_NS); 305*8e33eff8Schristos if (min_interval > interval) { 306*8e33eff8Schristos min_interval = interval; 307*8e33eff8Schristos } 308*8e33eff8Schristos } 309*8e33eff8Schristos background_thread_sleep(tsdn, info, min_interval); 310*8e33eff8Schristos } 311*8e33eff8Schristos 312*8e33eff8Schristos static bool 313*8e33eff8Schristos background_threads_disable_single(tsd_t *tsd, background_thread_info_t *info) { 314*8e33eff8Schristos if (info == &background_thread_info[0]) { 315*8e33eff8Schristos malloc_mutex_assert_owner(tsd_tsdn(tsd), 316*8e33eff8Schristos &background_thread_lock); 317*8e33eff8Schristos } else { 318*8e33eff8Schristos malloc_mutex_assert_not_owner(tsd_tsdn(tsd), 319*8e33eff8Schristos &background_thread_lock); 320*8e33eff8Schristos } 321*8e33eff8Schristos 322*8e33eff8Schristos pre_reentrancy(tsd, NULL); 323*8e33eff8Schristos malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); 324*8e33eff8Schristos bool has_thread; 325*8e33eff8Schristos assert(info->state != background_thread_paused); 326*8e33eff8Schristos if (info->state == background_thread_started) { 327*8e33eff8Schristos has_thread = true; 328*8e33eff8Schristos info->state = background_thread_stopped; 329*8e33eff8Schristos pthread_cond_signal(&info->cond); 330*8e33eff8Schristos } else { 331*8e33eff8Schristos has_thread = false; 332*8e33eff8Schristos } 333*8e33eff8Schristos malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); 334*8e33eff8Schristos 335*8e33eff8Schristos if (!has_thread) { 336*8e33eff8Schristos post_reentrancy(tsd); 337*8e33eff8Schristos return false; 338*8e33eff8Schristos } 339*8e33eff8Schristos void *ret; 340*8e33eff8Schristos if (pthread_join(info->thread, &ret)) { 341*8e33eff8Schristos post_reentrancy(tsd); 342*8e33eff8Schristos return true; 343*8e33eff8Schristos } 344*8e33eff8Schristos assert(ret == NULL); 345*8e33eff8Schristos n_background_threads--; 346*8e33eff8Schristos post_reentrancy(tsd); 347*8e33eff8Schristos 348*8e33eff8Schristos return false; 349*8e33eff8Schristos } 350*8e33eff8Schristos 351*8e33eff8Schristos static void *background_thread_entry(void *ind_arg); 352*8e33eff8Schristos 353*8e33eff8Schristos static int 354*8e33eff8Schristos background_thread_create_signals_masked(pthread_t *thread, 355*8e33eff8Schristos const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { 356*8e33eff8Schristos /* 357*8e33eff8Schristos * Mask signals during thread creation so that the thread inherits 358*8e33eff8Schristos * an empty signal set. 359*8e33eff8Schristos */ 360*8e33eff8Schristos sigset_t set; 361*8e33eff8Schristos sigfillset(&set); 362*8e33eff8Schristos sigset_t oldset; 363*8e33eff8Schristos int mask_err = pthread_sigmask(SIG_SETMASK, &set, &oldset); 364*8e33eff8Schristos if (mask_err != 0) { 365*8e33eff8Schristos return mask_err; 366*8e33eff8Schristos } 367*8e33eff8Schristos int create_err = pthread_create_wrapper(thread, attr, start_routine, 368*8e33eff8Schristos arg); 369*8e33eff8Schristos /* 370*8e33eff8Schristos * Restore the signal mask. Failure to restore the signal mask here 371*8e33eff8Schristos * changes program behavior. 372*8e33eff8Schristos */ 373*8e33eff8Schristos int restore_err = pthread_sigmask(SIG_SETMASK, &oldset, NULL); 374*8e33eff8Schristos if (restore_err != 0) { 375*8e33eff8Schristos malloc_printf("<jemalloc>: background thread creation " 376*8e33eff8Schristos "failed (%d), and signal mask restoration failed " 377*8e33eff8Schristos "(%d)\n", create_err, restore_err); 378*8e33eff8Schristos if (opt_abort) { 379*8e33eff8Schristos abort(); 380*8e33eff8Schristos } 381*8e33eff8Schristos } 382*8e33eff8Schristos return create_err; 383*8e33eff8Schristos } 384*8e33eff8Schristos 385*8e33eff8Schristos static bool 386*8e33eff8Schristos check_background_thread_creation(tsd_t *tsd, unsigned *n_created, 387*8e33eff8Schristos bool *created_threads) { 388*8e33eff8Schristos bool ret = false; 389*8e33eff8Schristos if (likely(*n_created == n_background_threads)) { 390*8e33eff8Schristos return ret; 391*8e33eff8Schristos } 392*8e33eff8Schristos 393*8e33eff8Schristos tsdn_t *tsdn = tsd_tsdn(tsd); 394*8e33eff8Schristos malloc_mutex_unlock(tsdn, &background_thread_info[0].mtx); 395*8e33eff8Schristos for (unsigned i = 1; i < max_background_threads; i++) { 396*8e33eff8Schristos if (created_threads[i]) { 397*8e33eff8Schristos continue; 398*8e33eff8Schristos } 399*8e33eff8Schristos background_thread_info_t *info = &background_thread_info[i]; 400*8e33eff8Schristos malloc_mutex_lock(tsdn, &info->mtx); 401*8e33eff8Schristos /* 402*8e33eff8Schristos * In case of the background_thread_paused state because of 403*8e33eff8Schristos * arena reset, delay the creation. 404*8e33eff8Schristos */ 405*8e33eff8Schristos bool create = (info->state == background_thread_started); 406*8e33eff8Schristos malloc_mutex_unlock(tsdn, &info->mtx); 407*8e33eff8Schristos if (!create) { 408*8e33eff8Schristos continue; 409*8e33eff8Schristos } 410*8e33eff8Schristos 411*8e33eff8Schristos pre_reentrancy(tsd, NULL); 412*8e33eff8Schristos int err = background_thread_create_signals_masked(&info->thread, 413*8e33eff8Schristos NULL, background_thread_entry, (void *)(uintptr_t)i); 414*8e33eff8Schristos post_reentrancy(tsd); 415*8e33eff8Schristos 416*8e33eff8Schristos if (err == 0) { 417*8e33eff8Schristos (*n_created)++; 418*8e33eff8Schristos created_threads[i] = true; 419*8e33eff8Schristos } else { 420*8e33eff8Schristos malloc_printf("<jemalloc>: background thread " 421*8e33eff8Schristos "creation failed (%d)\n", err); 422*8e33eff8Schristos if (opt_abort) { 423*8e33eff8Schristos abort(); 424*8e33eff8Schristos } 425*8e33eff8Schristos } 426*8e33eff8Schristos /* Return to restart the loop since we unlocked. */ 427*8e33eff8Schristos ret = true; 428*8e33eff8Schristos break; 429*8e33eff8Schristos } 430*8e33eff8Schristos malloc_mutex_lock(tsdn, &background_thread_info[0].mtx); 431*8e33eff8Schristos 432*8e33eff8Schristos return ret; 433*8e33eff8Schristos } 434*8e33eff8Schristos 435*8e33eff8Schristos static void 436*8e33eff8Schristos background_thread0_work(tsd_t *tsd) { 437*8e33eff8Schristos /* Thread0 is also responsible for launching / terminating threads. */ 438*8e33eff8Schristos VARIABLE_ARRAY(bool, created_threads, max_background_threads); 439*8e33eff8Schristos unsigned i; 440*8e33eff8Schristos for (i = 1; i < max_background_threads; i++) { 441*8e33eff8Schristos created_threads[i] = false; 442*8e33eff8Schristos } 443*8e33eff8Schristos /* Start working, and create more threads when asked. */ 444*8e33eff8Schristos unsigned n_created = 1; 445*8e33eff8Schristos while (background_thread_info[0].state != background_thread_stopped) { 446*8e33eff8Schristos if (background_thread_pause_check(tsd_tsdn(tsd), 447*8e33eff8Schristos &background_thread_info[0])) { 448*8e33eff8Schristos continue; 449*8e33eff8Schristos } 450*8e33eff8Schristos if (check_background_thread_creation(tsd, &n_created, 451*8e33eff8Schristos (bool *)&created_threads)) { 452*8e33eff8Schristos continue; 453*8e33eff8Schristos } 454*8e33eff8Schristos background_work_sleep_once(tsd_tsdn(tsd), 455*8e33eff8Schristos &background_thread_info[0], 0); 456*8e33eff8Schristos } 457*8e33eff8Schristos 458*8e33eff8Schristos /* 459*8e33eff8Schristos * Shut down other threads at exit. Note that the ctl thread is holding 460*8e33eff8Schristos * the global background_thread mutex (and is waiting) for us. 461*8e33eff8Schristos */ 462*8e33eff8Schristos assert(!background_thread_enabled()); 463*8e33eff8Schristos for (i = 1; i < max_background_threads; i++) { 464*8e33eff8Schristos background_thread_info_t *info = &background_thread_info[i]; 465*8e33eff8Schristos assert(info->state != background_thread_paused); 466*8e33eff8Schristos if (created_threads[i]) { 467*8e33eff8Schristos background_threads_disable_single(tsd, info); 468*8e33eff8Schristos } else { 469*8e33eff8Schristos malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); 470*8e33eff8Schristos if (info->state != background_thread_stopped) { 471*8e33eff8Schristos /* The thread was not created. */ 472*8e33eff8Schristos assert(info->state == 473*8e33eff8Schristos background_thread_started); 474*8e33eff8Schristos n_background_threads--; 475*8e33eff8Schristos info->state = background_thread_stopped; 476*8e33eff8Schristos } 477*8e33eff8Schristos malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); 478*8e33eff8Schristos } 479*8e33eff8Schristos } 480*8e33eff8Schristos background_thread_info[0].state = background_thread_stopped; 481*8e33eff8Schristos assert(n_background_threads == 1); 482*8e33eff8Schristos } 483*8e33eff8Schristos 484*8e33eff8Schristos static void 485*8e33eff8Schristos background_work(tsd_t *tsd, unsigned ind) { 486*8e33eff8Schristos background_thread_info_t *info = &background_thread_info[ind]; 487*8e33eff8Schristos 488*8e33eff8Schristos malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); 489*8e33eff8Schristos background_thread_wakeup_time_set(tsd_tsdn(tsd), info, 490*8e33eff8Schristos BACKGROUND_THREAD_INDEFINITE_SLEEP); 491*8e33eff8Schristos if (ind == 0) { 492*8e33eff8Schristos background_thread0_work(tsd); 493*8e33eff8Schristos } else { 494*8e33eff8Schristos while (info->state != background_thread_stopped) { 495*8e33eff8Schristos if (background_thread_pause_check(tsd_tsdn(tsd), 496*8e33eff8Schristos info)) { 497*8e33eff8Schristos continue; 498*8e33eff8Schristos } 499*8e33eff8Schristos background_work_sleep_once(tsd_tsdn(tsd), info, ind); 500*8e33eff8Schristos } 501*8e33eff8Schristos } 502*8e33eff8Schristos assert(info->state == background_thread_stopped); 503*8e33eff8Schristos background_thread_wakeup_time_set(tsd_tsdn(tsd), info, 0); 504*8e33eff8Schristos malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); 505*8e33eff8Schristos } 506*8e33eff8Schristos 507*8e33eff8Schristos static void * 508*8e33eff8Schristos background_thread_entry(void *ind_arg) { 509*8e33eff8Schristos unsigned thread_ind = (unsigned)(uintptr_t)ind_arg; 510*8e33eff8Schristos assert(thread_ind < max_background_threads); 511*8e33eff8Schristos #ifdef JEMALLOC_HAVE_PTHREAD_SETNAME_NP 512*8e33eff8Schristos pthread_setname_np(pthread_self(), "jemalloc_bg_thd"); 513*8e33eff8Schristos #endif 514*8e33eff8Schristos if (opt_percpu_arena != percpu_arena_disabled) { 515*8e33eff8Schristos set_current_thread_affinity((int)thread_ind); 516*8e33eff8Schristos } 517*8e33eff8Schristos /* 518*8e33eff8Schristos * Start periodic background work. We use internal tsd which avoids 519*8e33eff8Schristos * side effects, for example triggering new arena creation (which in 520*8e33eff8Schristos * turn triggers another background thread creation). 521*8e33eff8Schristos */ 522*8e33eff8Schristos background_work(tsd_internal_fetch(), thread_ind); 523*8e33eff8Schristos assert(pthread_equal(pthread_self(), 524*8e33eff8Schristos background_thread_info[thread_ind].thread)); 525*8e33eff8Schristos 526*8e33eff8Schristos return NULL; 527*8e33eff8Schristos } 528*8e33eff8Schristos 529*8e33eff8Schristos static void 530*8e33eff8Schristos background_thread_init(tsd_t *tsd, background_thread_info_t *info) { 531*8e33eff8Schristos malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock); 532*8e33eff8Schristos info->state = background_thread_started; 533*8e33eff8Schristos background_thread_info_init(tsd_tsdn(tsd), info); 534*8e33eff8Schristos n_background_threads++; 535*8e33eff8Schristos } 536*8e33eff8Schristos 537*8e33eff8Schristos /* Create a new background thread if needed. */ 538*8e33eff8Schristos bool 539*8e33eff8Schristos background_thread_create(tsd_t *tsd, unsigned arena_ind) { 540*8e33eff8Schristos assert(have_background_thread); 541*8e33eff8Schristos malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock); 542*8e33eff8Schristos 543*8e33eff8Schristos /* We create at most NCPUs threads. */ 544*8e33eff8Schristos size_t thread_ind = arena_ind % max_background_threads; 545*8e33eff8Schristos background_thread_info_t *info = &background_thread_info[thread_ind]; 546*8e33eff8Schristos 547*8e33eff8Schristos bool need_new_thread; 548*8e33eff8Schristos malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); 549*8e33eff8Schristos need_new_thread = background_thread_enabled() && 550*8e33eff8Schristos (info->state == background_thread_stopped); 551*8e33eff8Schristos if (need_new_thread) { 552*8e33eff8Schristos background_thread_init(tsd, info); 553*8e33eff8Schristos } 554*8e33eff8Schristos malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); 555*8e33eff8Schristos if (!need_new_thread) { 556*8e33eff8Schristos return false; 557*8e33eff8Schristos } 558*8e33eff8Schristos if (arena_ind != 0) { 559*8e33eff8Schristos /* Threads are created asynchronously by Thread 0. */ 560*8e33eff8Schristos background_thread_info_t *t0 = &background_thread_info[0]; 561*8e33eff8Schristos malloc_mutex_lock(tsd_tsdn(tsd), &t0->mtx); 562*8e33eff8Schristos assert(t0->state == background_thread_started); 563*8e33eff8Schristos pthread_cond_signal(&t0->cond); 564*8e33eff8Schristos malloc_mutex_unlock(tsd_tsdn(tsd), &t0->mtx); 565*8e33eff8Schristos 566*8e33eff8Schristos return false; 567*8e33eff8Schristos } 568*8e33eff8Schristos 569*8e33eff8Schristos pre_reentrancy(tsd, NULL); 570*8e33eff8Schristos /* 571*8e33eff8Schristos * To avoid complications (besides reentrancy), create internal 572*8e33eff8Schristos * background threads with the underlying pthread_create. 573*8e33eff8Schristos */ 574*8e33eff8Schristos int err = background_thread_create_signals_masked(&info->thread, NULL, 575*8e33eff8Schristos background_thread_entry, (void *)thread_ind); 576*8e33eff8Schristos post_reentrancy(tsd); 577*8e33eff8Schristos 578*8e33eff8Schristos if (err != 0) { 579*8e33eff8Schristos malloc_printf("<jemalloc>: arena 0 background thread creation " 580*8e33eff8Schristos "failed (%d)\n", err); 581*8e33eff8Schristos malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); 582*8e33eff8Schristos info->state = background_thread_stopped; 583*8e33eff8Schristos n_background_threads--; 584*8e33eff8Schristos malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); 585*8e33eff8Schristos 586*8e33eff8Schristos return true; 587*8e33eff8Schristos } 588*8e33eff8Schristos 589*8e33eff8Schristos return false; 590*8e33eff8Schristos } 591*8e33eff8Schristos 592*8e33eff8Schristos bool 593*8e33eff8Schristos background_threads_enable(tsd_t *tsd) { 594*8e33eff8Schristos assert(n_background_threads == 0); 595*8e33eff8Schristos assert(background_thread_enabled()); 596*8e33eff8Schristos malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock); 597*8e33eff8Schristos 598*8e33eff8Schristos VARIABLE_ARRAY(bool, marked, max_background_threads); 599*8e33eff8Schristos unsigned i, nmarked; 600*8e33eff8Schristos for (i = 0; i < max_background_threads; i++) { 601*8e33eff8Schristos marked[i] = false; 602*8e33eff8Schristos } 603*8e33eff8Schristos nmarked = 0; 604*8e33eff8Schristos /* Thread 0 is required and created at the end. */ 605*8e33eff8Schristos marked[0] = true; 606*8e33eff8Schristos /* Mark the threads we need to create for thread 0. */ 607*8e33eff8Schristos unsigned n = narenas_total_get(); 608*8e33eff8Schristos for (i = 1; i < n; i++) { 609*8e33eff8Schristos if (marked[i % max_background_threads] || 610*8e33eff8Schristos arena_get(tsd_tsdn(tsd), i, false) == NULL) { 611*8e33eff8Schristos continue; 612*8e33eff8Schristos } 613*8e33eff8Schristos background_thread_info_t *info = &background_thread_info[ 614*8e33eff8Schristos i % max_background_threads]; 615*8e33eff8Schristos malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); 616*8e33eff8Schristos assert(info->state == background_thread_stopped); 617*8e33eff8Schristos background_thread_init(tsd, info); 618*8e33eff8Schristos malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); 619*8e33eff8Schristos marked[i % max_background_threads] = true; 620*8e33eff8Schristos if (++nmarked == max_background_threads) { 621*8e33eff8Schristos break; 622*8e33eff8Schristos } 623*8e33eff8Schristos } 624*8e33eff8Schristos 625*8e33eff8Schristos return background_thread_create(tsd, 0); 626*8e33eff8Schristos } 627*8e33eff8Schristos 628*8e33eff8Schristos bool 629*8e33eff8Schristos background_threads_disable(tsd_t *tsd) { 630*8e33eff8Schristos assert(!background_thread_enabled()); 631*8e33eff8Schristos malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock); 632*8e33eff8Schristos 633*8e33eff8Schristos /* Thread 0 will be responsible for terminating other threads. */ 634*8e33eff8Schristos if (background_threads_disable_single(tsd, 635*8e33eff8Schristos &background_thread_info[0])) { 636*8e33eff8Schristos return true; 637*8e33eff8Schristos } 638*8e33eff8Schristos assert(n_background_threads == 0); 639*8e33eff8Schristos 640*8e33eff8Schristos return false; 641*8e33eff8Schristos } 642*8e33eff8Schristos 643*8e33eff8Schristos /* Check if we need to signal the background thread early. */ 644*8e33eff8Schristos void 645*8e33eff8Schristos background_thread_interval_check(tsdn_t *tsdn, arena_t *arena, 646*8e33eff8Schristos arena_decay_t *decay, size_t npages_new) { 647*8e33eff8Schristos background_thread_info_t *info = arena_background_thread_info_get( 648*8e33eff8Schristos arena); 649*8e33eff8Schristos if (malloc_mutex_trylock(tsdn, &info->mtx)) { 650*8e33eff8Schristos /* 651*8e33eff8Schristos * Background thread may hold the mutex for a long period of 652*8e33eff8Schristos * time. We'd like to avoid the variance on application 653*8e33eff8Schristos * threads. So keep this non-blocking, and leave the work to a 654*8e33eff8Schristos * future epoch. 655*8e33eff8Schristos */ 656*8e33eff8Schristos return; 657*8e33eff8Schristos } 658*8e33eff8Schristos 659*8e33eff8Schristos if (info->state != background_thread_started) { 660*8e33eff8Schristos goto label_done; 661*8e33eff8Schristos } 662*8e33eff8Schristos if (malloc_mutex_trylock(tsdn, &decay->mtx)) { 663*8e33eff8Schristos goto label_done; 664*8e33eff8Schristos } 665*8e33eff8Schristos 666*8e33eff8Schristos ssize_t decay_time = atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED); 667*8e33eff8Schristos if (decay_time <= 0) { 668*8e33eff8Schristos /* Purging is eagerly done or disabled currently. */ 669*8e33eff8Schristos goto label_done_unlock2; 670*8e33eff8Schristos } 671*8e33eff8Schristos uint64_t decay_interval_ns = nstime_ns(&decay->interval); 672*8e33eff8Schristos assert(decay_interval_ns > 0); 673*8e33eff8Schristos 674*8e33eff8Schristos nstime_t diff; 675*8e33eff8Schristos nstime_init(&diff, background_thread_wakeup_time_get(info)); 676*8e33eff8Schristos if (nstime_compare(&diff, &decay->epoch) <= 0) { 677*8e33eff8Schristos goto label_done_unlock2; 678*8e33eff8Schristos } 679*8e33eff8Schristos nstime_subtract(&diff, &decay->epoch); 680*8e33eff8Schristos if (nstime_ns(&diff) < BACKGROUND_THREAD_MIN_INTERVAL_NS) { 681*8e33eff8Schristos goto label_done_unlock2; 682*8e33eff8Schristos } 683*8e33eff8Schristos 684*8e33eff8Schristos if (npages_new > 0) { 685*8e33eff8Schristos size_t n_epoch = (size_t)(nstime_ns(&diff) / decay_interval_ns); 686*8e33eff8Schristos /* 687*8e33eff8Schristos * Compute how many new pages we would need to purge by the next 688*8e33eff8Schristos * wakeup, which is used to determine if we should signal the 689*8e33eff8Schristos * background thread. 690*8e33eff8Schristos */ 691*8e33eff8Schristos uint64_t npurge_new; 692*8e33eff8Schristos if (n_epoch >= SMOOTHSTEP_NSTEPS) { 693*8e33eff8Schristos npurge_new = npages_new; 694*8e33eff8Schristos } else { 695*8e33eff8Schristos uint64_t h_steps_max = h_steps[SMOOTHSTEP_NSTEPS - 1]; 696*8e33eff8Schristos assert(h_steps_max >= 697*8e33eff8Schristos h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]); 698*8e33eff8Schristos npurge_new = npages_new * (h_steps_max - 699*8e33eff8Schristos h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]); 700*8e33eff8Schristos npurge_new >>= SMOOTHSTEP_BFP; 701*8e33eff8Schristos } 702*8e33eff8Schristos info->npages_to_purge_new += npurge_new; 703*8e33eff8Schristos } 704*8e33eff8Schristos 705*8e33eff8Schristos bool should_signal; 706*8e33eff8Schristos if (info->npages_to_purge_new > BACKGROUND_THREAD_NPAGES_THRESHOLD) { 707*8e33eff8Schristos should_signal = true; 708*8e33eff8Schristos } else if (unlikely(background_thread_indefinite_sleep(info)) && 709*8e33eff8Schristos (extents_npages_get(&arena->extents_dirty) > 0 || 710*8e33eff8Schristos extents_npages_get(&arena->extents_muzzy) > 0 || 711*8e33eff8Schristos info->npages_to_purge_new > 0)) { 712*8e33eff8Schristos should_signal = true; 713*8e33eff8Schristos } else { 714*8e33eff8Schristos should_signal = false; 715*8e33eff8Schristos } 716*8e33eff8Schristos 717*8e33eff8Schristos if (should_signal) { 718*8e33eff8Schristos info->npages_to_purge_new = 0; 719*8e33eff8Schristos pthread_cond_signal(&info->cond); 720*8e33eff8Schristos } 721*8e33eff8Schristos label_done_unlock2: 722*8e33eff8Schristos malloc_mutex_unlock(tsdn, &decay->mtx); 723*8e33eff8Schristos label_done: 724*8e33eff8Schristos malloc_mutex_unlock(tsdn, &info->mtx); 725*8e33eff8Schristos } 726*8e33eff8Schristos 727*8e33eff8Schristos void 728*8e33eff8Schristos background_thread_prefork0(tsdn_t *tsdn) { 729*8e33eff8Schristos malloc_mutex_prefork(tsdn, &background_thread_lock); 730*8e33eff8Schristos background_thread_enabled_at_fork = background_thread_enabled(); 731*8e33eff8Schristos } 732*8e33eff8Schristos 733*8e33eff8Schristos void 734*8e33eff8Schristos background_thread_prefork1(tsdn_t *tsdn) { 735*8e33eff8Schristos for (unsigned i = 0; i < max_background_threads; i++) { 736*8e33eff8Schristos malloc_mutex_prefork(tsdn, &background_thread_info[i].mtx); 737*8e33eff8Schristos } 738*8e33eff8Schristos } 739*8e33eff8Schristos 740*8e33eff8Schristos void 741*8e33eff8Schristos background_thread_postfork_parent(tsdn_t *tsdn) { 742*8e33eff8Schristos for (unsigned i = 0; i < max_background_threads; i++) { 743*8e33eff8Schristos malloc_mutex_postfork_parent(tsdn, 744*8e33eff8Schristos &background_thread_info[i].mtx); 745*8e33eff8Schristos } 746*8e33eff8Schristos malloc_mutex_postfork_parent(tsdn, &background_thread_lock); 747*8e33eff8Schristos } 748*8e33eff8Schristos 749*8e33eff8Schristos void 750*8e33eff8Schristos background_thread_postfork_child(tsdn_t *tsdn) { 751*8e33eff8Schristos for (unsigned i = 0; i < max_background_threads; i++) { 752*8e33eff8Schristos malloc_mutex_postfork_child(tsdn, 753*8e33eff8Schristos &background_thread_info[i].mtx); 754*8e33eff8Schristos } 755*8e33eff8Schristos malloc_mutex_postfork_child(tsdn, &background_thread_lock); 756*8e33eff8Schristos if (!background_thread_enabled_at_fork) { 757*8e33eff8Schristos return; 758*8e33eff8Schristos } 759*8e33eff8Schristos 760*8e33eff8Schristos /* Clear background_thread state (reset to disabled for child). */ 761*8e33eff8Schristos malloc_mutex_lock(tsdn, &background_thread_lock); 762*8e33eff8Schristos n_background_threads = 0; 763*8e33eff8Schristos background_thread_enabled_set(tsdn, false); 764*8e33eff8Schristos for (unsigned i = 0; i < max_background_threads; i++) { 765*8e33eff8Schristos background_thread_info_t *info = &background_thread_info[i]; 766*8e33eff8Schristos malloc_mutex_lock(tsdn, &info->mtx); 767*8e33eff8Schristos info->state = background_thread_stopped; 768*8e33eff8Schristos int ret = pthread_cond_init(&info->cond, NULL); 769*8e33eff8Schristos assert(ret == 0); 770*8e33eff8Schristos background_thread_info_init(tsdn, info); 771*8e33eff8Schristos malloc_mutex_unlock(tsdn, &info->mtx); 772*8e33eff8Schristos } 773*8e33eff8Schristos malloc_mutex_unlock(tsdn, &background_thread_lock); 774*8e33eff8Schristos } 775*8e33eff8Schristos 776*8e33eff8Schristos bool 777*8e33eff8Schristos background_thread_stats_read(tsdn_t *tsdn, background_thread_stats_t *stats) { 778*8e33eff8Schristos assert(config_stats); 779*8e33eff8Schristos malloc_mutex_lock(tsdn, &background_thread_lock); 780*8e33eff8Schristos if (!background_thread_enabled()) { 781*8e33eff8Schristos malloc_mutex_unlock(tsdn, &background_thread_lock); 782*8e33eff8Schristos return true; 783*8e33eff8Schristos } 784*8e33eff8Schristos 785*8e33eff8Schristos stats->num_threads = n_background_threads; 786*8e33eff8Schristos uint64_t num_runs = 0; 787*8e33eff8Schristos nstime_init(&stats->run_interval, 0); 788*8e33eff8Schristos for (unsigned i = 0; i < max_background_threads; i++) { 789*8e33eff8Schristos background_thread_info_t *info = &background_thread_info[i]; 790*8e33eff8Schristos malloc_mutex_lock(tsdn, &info->mtx); 791*8e33eff8Schristos if (info->state != background_thread_stopped) { 792*8e33eff8Schristos num_runs += info->tot_n_runs; 793*8e33eff8Schristos nstime_add(&stats->run_interval, &info->tot_sleep_time); 794*8e33eff8Schristos } 795*8e33eff8Schristos malloc_mutex_unlock(tsdn, &info->mtx); 796*8e33eff8Schristos } 797*8e33eff8Schristos stats->num_runs = num_runs; 798*8e33eff8Schristos if (num_runs > 0) { 799*8e33eff8Schristos nstime_idivide(&stats->run_interval, num_runs); 800*8e33eff8Schristos } 801*8e33eff8Schristos malloc_mutex_unlock(tsdn, &background_thread_lock); 802*8e33eff8Schristos 803*8e33eff8Schristos return false; 804*8e33eff8Schristos } 805*8e33eff8Schristos 806*8e33eff8Schristos #undef BACKGROUND_THREAD_NPAGES_THRESHOLD 807*8e33eff8Schristos #undef BILLION 808*8e33eff8Schristos #undef BACKGROUND_THREAD_MIN_INTERVAL_NS 809*8e33eff8Schristos 810*8e33eff8Schristos static bool 811*8e33eff8Schristos pthread_create_fptr_init(void) { 812*8e33eff8Schristos if (pthread_create_fptr != NULL) { 813*8e33eff8Schristos return false; 814*8e33eff8Schristos } 815*8e33eff8Schristos pthread_create_fptr = dlsym(RTLD_NEXT, "pthread_create"); 816*8e33eff8Schristos if (pthread_create_fptr == NULL) { 817*8e33eff8Schristos can_enable_background_thread = false; 818*8e33eff8Schristos if (config_lazy_lock || opt_background_thread) { 819*8e33eff8Schristos malloc_write("<jemalloc>: Error in dlsym(RTLD_NEXT, " 820*8e33eff8Schristos "\"pthread_create\")\n"); 821*8e33eff8Schristos abort(); 822*8e33eff8Schristos } 823*8e33eff8Schristos } else { 824*8e33eff8Schristos can_enable_background_thread = true; 825*8e33eff8Schristos } 826*8e33eff8Schristos 827*8e33eff8Schristos return false; 828*8e33eff8Schristos } 829*8e33eff8Schristos 830*8e33eff8Schristos /* 831*8e33eff8Schristos * When lazy lock is enabled, we need to make sure setting isthreaded before 832*8e33eff8Schristos * taking any background_thread locks. This is called early in ctl (instead of 833*8e33eff8Schristos * wait for the pthread_create calls to trigger) because the mutex is required 834*8e33eff8Schristos * before creating background threads. 835*8e33eff8Schristos */ 836*8e33eff8Schristos void 837*8e33eff8Schristos background_thread_ctl_init(tsdn_t *tsdn) { 838*8e33eff8Schristos malloc_mutex_assert_not_owner(tsdn, &background_thread_lock); 839*8e33eff8Schristos #ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER 840*8e33eff8Schristos pthread_create_fptr_init(); 841*8e33eff8Schristos pthread_create_wrapper_init(); 842*8e33eff8Schristos #endif 843*8e33eff8Schristos } 844*8e33eff8Schristos 845*8e33eff8Schristos #endif /* defined(JEMALLOC_BACKGROUND_THREAD) */ 846*8e33eff8Schristos 847*8e33eff8Schristos bool 848*8e33eff8Schristos background_thread_boot0(void) { 849*8e33eff8Schristos if (!have_background_thread && opt_background_thread) { 850*8e33eff8Schristos malloc_printf("<jemalloc>: option background_thread currently " 851*8e33eff8Schristos "supports pthread only\n"); 852*8e33eff8Schristos return true; 853*8e33eff8Schristos } 854*8e33eff8Schristos #ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER 855*8e33eff8Schristos if ((config_lazy_lock || opt_background_thread) && 856*8e33eff8Schristos pthread_create_fptr_init()) { 857*8e33eff8Schristos return true; 858*8e33eff8Schristos } 859*8e33eff8Schristos #endif 860*8e33eff8Schristos return false; 861*8e33eff8Schristos } 862*8e33eff8Schristos 863*8e33eff8Schristos bool 864*8e33eff8Schristos background_thread_boot1(tsdn_t *tsdn) { 865*8e33eff8Schristos #ifdef JEMALLOC_BACKGROUND_THREAD 866*8e33eff8Schristos assert(have_background_thread); 867*8e33eff8Schristos assert(narenas_total_get() > 0); 868*8e33eff8Schristos 869*8e33eff8Schristos if (opt_max_background_threads == MAX_BACKGROUND_THREAD_LIMIT && 870*8e33eff8Schristos ncpus < MAX_BACKGROUND_THREAD_LIMIT) { 871*8e33eff8Schristos opt_max_background_threads = ncpus; 872*8e33eff8Schristos } 873*8e33eff8Schristos max_background_threads = opt_max_background_threads; 874*8e33eff8Schristos 875*8e33eff8Schristos background_thread_enabled_set(tsdn, opt_background_thread); 876*8e33eff8Schristos if (malloc_mutex_init(&background_thread_lock, 877*8e33eff8Schristos "background_thread_global", 878*8e33eff8Schristos WITNESS_RANK_BACKGROUND_THREAD_GLOBAL, 879*8e33eff8Schristos malloc_mutex_rank_exclusive)) { 880*8e33eff8Schristos return true; 881*8e33eff8Schristos } 882*8e33eff8Schristos 883*8e33eff8Schristos background_thread_info = (background_thread_info_t *)base_alloc(tsdn, 884*8e33eff8Schristos b0get(), opt_max_background_threads * 885*8e33eff8Schristos sizeof(background_thread_info_t), CACHELINE); 886*8e33eff8Schristos if (background_thread_info == NULL) { 887*8e33eff8Schristos return true; 888*8e33eff8Schristos } 889*8e33eff8Schristos 890*8e33eff8Schristos for (unsigned i = 0; i < max_background_threads; i++) { 891*8e33eff8Schristos background_thread_info_t *info = &background_thread_info[i]; 892*8e33eff8Schristos /* Thread mutex is rank_inclusive because of thread0. */ 893*8e33eff8Schristos if (malloc_mutex_init(&info->mtx, "background_thread", 894*8e33eff8Schristos WITNESS_RANK_BACKGROUND_THREAD, 895*8e33eff8Schristos malloc_mutex_address_ordered)) { 896*8e33eff8Schristos return true; 897*8e33eff8Schristos } 898*8e33eff8Schristos if (pthread_cond_init(&info->cond, NULL)) { 899*8e33eff8Schristos return true; 900*8e33eff8Schristos } 901*8e33eff8Schristos malloc_mutex_lock(tsdn, &info->mtx); 902*8e33eff8Schristos info->state = background_thread_stopped; 903*8e33eff8Schristos background_thread_info_init(tsdn, info); 904*8e33eff8Schristos malloc_mutex_unlock(tsdn, &info->mtx); 905*8e33eff8Schristos } 906*8e33eff8Schristos #endif 907*8e33eff8Schristos 908*8e33eff8Schristos return false; 909*8e33eff8Schristos } 910