Blame jemalloc/src/background_thread.c

Packit Service 724aca
#define JEMALLOC_BACKGROUND_THREAD_C_
Packit Service 724aca
#include "jemalloc/internal/jemalloc_preamble.h"
Packit Service 724aca
#include "jemalloc/internal/jemalloc_internal_includes.h"
Packit Service 724aca
Packit Service 724aca
#include "jemalloc/internal/assert.h"
Packit Service 724aca
Packit Service 724aca
JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
Packit Service 724aca
Packit Service 724aca
/******************************************************************************/
Packit Service 724aca
/* Data. */
Packit Service 724aca
Packit Service 724aca
/* This option should be opt-in only. */
Packit Service 724aca
#define BACKGROUND_THREAD_DEFAULT false
Packit Service 724aca
/* Read-only after initialization. */
Packit Service 724aca
bool opt_background_thread = BACKGROUND_THREAD_DEFAULT;
Packit Service 724aca
size_t opt_max_background_threads = MAX_BACKGROUND_THREAD_LIMIT + 1;
Packit Service 724aca
Packit Service 724aca
/* Used for thread creation, termination and stats. */
Packit Service 724aca
malloc_mutex_t background_thread_lock;
Packit Service 724aca
/* Indicates global state.  Atomic because decay reads this w/o locking. */
Packit Service 724aca
atomic_b_t background_thread_enabled_state;
Packit Service 724aca
size_t n_background_threads;
Packit Service 724aca
size_t max_background_threads;
Packit Service 724aca
/* Thread info per-index. */
Packit Service 724aca
background_thread_info_t *background_thread_info;
Packit Service 724aca
Packit Service 724aca
/******************************************************************************/
Packit Service 724aca
Packit Service 724aca
#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER
Packit Service 724aca
Packit Service 724aca
static int (*pthread_create_fptr)(pthread_t *__restrict, const pthread_attr_t *,
Packit Service 724aca
    void *(*)(void *), void *__restrict);
Packit Service 724aca
Packit Service 724aca
static void
Packit Service 724aca
pthread_create_wrapper_init(void) {
Packit Service 724aca
#ifdef JEMALLOC_LAZY_LOCK
Packit Service 724aca
	if (!isthreaded) {
Packit Service 724aca
		isthreaded = true;
Packit Service 724aca
	}
Packit Service 724aca
#endif
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
int
Packit Service 724aca
pthread_create_wrapper(pthread_t *__restrict thread, const pthread_attr_t *attr,
Packit Service 724aca
    void *(*start_routine)(void *), void *__restrict arg) {
Packit Service 724aca
	pthread_create_wrapper_init();
Packit Service 724aca
Packit Service 724aca
	return pthread_create_fptr(thread, attr, start_routine, arg);
Packit Service 724aca
}
Packit Service 724aca
#endif /* JEMALLOC_PTHREAD_CREATE_WRAPPER */
Packit Service 724aca
Packit Service 724aca
#ifndef JEMALLOC_BACKGROUND_THREAD
Packit Service 724aca
#define NOT_REACHED { not_reached(); }
Packit Service 724aca
bool background_thread_create(tsd_t *tsd, unsigned arena_ind) NOT_REACHED
Packit Service 724aca
bool background_threads_enable(tsd_t *tsd) NOT_REACHED
Packit Service 724aca
bool background_threads_disable(tsd_t *tsd) NOT_REACHED
Packit Service 724aca
void background_thread_interval_check(tsdn_t *tsdn, arena_t *arena,
Packit Service 724aca
    arena_decay_t *decay, size_t npages_new) NOT_REACHED
Packit Service 724aca
void background_thread_prefork0(tsdn_t *tsdn) NOT_REACHED
Packit Service 724aca
void background_thread_prefork1(tsdn_t *tsdn) NOT_REACHED
Packit Service 724aca
void background_thread_postfork_parent(tsdn_t *tsdn) NOT_REACHED
Packit Service 724aca
void background_thread_postfork_child(tsdn_t *tsdn) NOT_REACHED
Packit Service 724aca
bool background_thread_stats_read(tsdn_t *tsdn,
Packit Service 724aca
    background_thread_stats_t *stats) NOT_REACHED
Packit Service 724aca
void background_thread_ctl_init(tsdn_t *tsdn) NOT_REACHED
Packit Service 724aca
#undef NOT_REACHED
Packit Service 724aca
#else
Packit Service 724aca
Packit Service 724aca
static bool background_thread_enabled_at_fork;
Packit Service 724aca
Packit Service 724aca
static void
Packit Service 724aca
background_thread_info_init(tsdn_t *tsdn, background_thread_info_t *info) {
Packit Service 724aca
	background_thread_wakeup_time_set(tsdn, info, 0);
Packit Service 724aca
	info->npages_to_purge_new = 0;
Packit Service 724aca
	if (config_stats) {
Packit Service 724aca
		info->tot_n_runs = 0;
Packit Service 724aca
		nstime_init(&info->tot_sleep_time, 0);
Packit Service 724aca
	}
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
static inline bool
Packit Service 724aca
set_current_thread_affinity(int cpu) {
Packit Service 724aca
#if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY)
Packit Service 724aca
	cpu_set_t cpuset;
Packit Service 724aca
	CPU_ZERO(&cpuset);
Packit Service 724aca
	CPU_SET(cpu, &cpuset);
Packit Service 724aca
	int ret = sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
Packit Service 724aca
Packit Service 724aca
	return (ret != 0);
Packit Service 724aca
#else
Packit Service 724aca
	return false;
Packit Service 724aca
#endif
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
/* Threshold for determining when to wake up the background thread. */
Packit Service 724aca
#define BACKGROUND_THREAD_NPAGES_THRESHOLD UINT64_C(1024)
Packit Service 724aca
#define BILLION UINT64_C(1000000000)
Packit Service 724aca
/* Minimal sleep interval 100 ms. */
Packit Service 724aca
#define BACKGROUND_THREAD_MIN_INTERVAL_NS (BILLION / 10)
Packit Service 724aca
Packit Service 724aca
static inline size_t
Packit Service 724aca
decay_npurge_after_interval(arena_decay_t *decay, size_t interval) {
Packit Service 724aca
	size_t i;
Packit Service 724aca
	uint64_t sum = 0;
Packit Service 724aca
	for (i = 0; i < interval; i++) {
Packit Service 724aca
		sum += decay->backlog[i] * h_steps[i];
Packit Service 724aca
	}
Packit Service 724aca
	for (; i < SMOOTHSTEP_NSTEPS; i++) {
Packit Service 724aca
		sum += decay->backlog[i] * (h_steps[i] - h_steps[i - interval]);
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	return (size_t)(sum >> SMOOTHSTEP_BFP);
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
static uint64_t
Packit Service 724aca
arena_decay_compute_purge_interval_impl(tsdn_t *tsdn, arena_decay_t *decay,
Packit Service 724aca
    extents_t *extents) {
Packit Service 724aca
	if (malloc_mutex_trylock(tsdn, &decay->mtx)) {
Packit Service 724aca
		/* Use minimal interval if decay is contended. */
Packit Service 724aca
		return BACKGROUND_THREAD_MIN_INTERVAL_NS;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	uint64_t interval;
Packit Service 724aca
	ssize_t decay_time = atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED);
Packit Service 724aca
	if (decay_time <= 0) {
Packit Service 724aca
		/* Purging is eagerly done or disabled currently. */
Packit Service 724aca
		interval = BACKGROUND_THREAD_INDEFINITE_SLEEP;
Packit Service 724aca
		goto label_done;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	uint64_t decay_interval_ns = nstime_ns(&decay->interval);
Packit Service 724aca
	assert(decay_interval_ns > 0);
Packit Service 724aca
	size_t npages = extents_npages_get(extents);
Packit Service 724aca
	if (npages == 0) {
Packit Service 724aca
		unsigned i;
Packit Service 724aca
		for (i = 0; i < SMOOTHSTEP_NSTEPS; i++) {
Packit Service 724aca
			if (decay->backlog[i] > 0) {
Packit Service 724aca
				break;
Packit Service 724aca
			}
Packit Service 724aca
		}
Packit Service 724aca
		if (i == SMOOTHSTEP_NSTEPS) {
Packit Service 724aca
			/* No dirty pages recorded.  Sleep indefinitely. */
Packit Service 724aca
			interval = BACKGROUND_THREAD_INDEFINITE_SLEEP;
Packit Service 724aca
			goto label_done;
Packit Service 724aca
		}
Packit Service 724aca
	}
Packit Service 724aca
	if (npages <= BACKGROUND_THREAD_NPAGES_THRESHOLD) {
Packit Service 724aca
		/* Use max interval. */
Packit Service 724aca
		interval = decay_interval_ns * SMOOTHSTEP_NSTEPS;
Packit Service 724aca
		goto label_done;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	size_t lb = BACKGROUND_THREAD_MIN_INTERVAL_NS / decay_interval_ns;
Packit Service 724aca
	size_t ub = SMOOTHSTEP_NSTEPS;
Packit Service 724aca
	/* Minimal 2 intervals to ensure reaching next epoch deadline. */
Packit Service 724aca
	lb = (lb < 2) ? 2 : lb;
Packit Service 724aca
	if ((decay_interval_ns * ub <= BACKGROUND_THREAD_MIN_INTERVAL_NS) ||
Packit Service 724aca
	    (lb + 2 > ub)) {
Packit Service 724aca
		interval = BACKGROUND_THREAD_MIN_INTERVAL_NS;
Packit Service 724aca
		goto label_done;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	assert(lb + 2 <= ub);
Packit Service 724aca
	size_t npurge_lb, npurge_ub;
Packit Service 724aca
	npurge_lb = decay_npurge_after_interval(decay, lb);
Packit Service 724aca
	if (npurge_lb > BACKGROUND_THREAD_NPAGES_THRESHOLD) {
Packit Service 724aca
		interval = decay_interval_ns * lb;
Packit Service 724aca
		goto label_done;
Packit Service 724aca
	}
Packit Service 724aca
	npurge_ub = decay_npurge_after_interval(decay, ub);
Packit Service 724aca
	if (npurge_ub < BACKGROUND_THREAD_NPAGES_THRESHOLD) {
Packit Service 724aca
		interval = decay_interval_ns * ub;
Packit Service 724aca
		goto label_done;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	unsigned n_search = 0;
Packit Service 724aca
	size_t target, npurge;
Packit Service 724aca
	while ((npurge_lb + BACKGROUND_THREAD_NPAGES_THRESHOLD < npurge_ub)
Packit Service 724aca
	    && (lb + 2 < ub)) {
Packit Service 724aca
		target = (lb + ub) / 2;
Packit Service 724aca
		npurge = decay_npurge_after_interval(decay, target);
Packit Service 724aca
		if (npurge > BACKGROUND_THREAD_NPAGES_THRESHOLD) {
Packit Service 724aca
			ub = target;
Packit Service 724aca
			npurge_ub = npurge;
Packit Service 724aca
		} else {
Packit Service 724aca
			lb = target;
Packit Service 724aca
			npurge_lb = npurge;
Packit Service 724aca
		}
Packit Service 724aca
		assert(n_search++ < lg_floor(SMOOTHSTEP_NSTEPS) + 1);
Packit Service 724aca
	}
Packit Service 724aca
	interval = decay_interval_ns * (ub + lb) / 2;
Packit Service 724aca
label_done:
Packit Service 724aca
	interval = (interval < BACKGROUND_THREAD_MIN_INTERVAL_NS) ?
Packit Service 724aca
	    BACKGROUND_THREAD_MIN_INTERVAL_NS : interval;
Packit Service 724aca
	malloc_mutex_unlock(tsdn, &decay->mtx);
Packit Service 724aca
Packit Service 724aca
	return interval;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
/* Compute purge interval for background threads. */
Packit Service 724aca
static uint64_t
Packit Service 724aca
arena_decay_compute_purge_interval(tsdn_t *tsdn, arena_t *arena) {
Packit Service 724aca
	uint64_t i1, i2;
Packit Service 724aca
	i1 = arena_decay_compute_purge_interval_impl(tsdn, &arena->decay_dirty,
Packit Service 724aca
	    &arena->extents_dirty);
Packit Service 724aca
	if (i1 == BACKGROUND_THREAD_MIN_INTERVAL_NS) {
Packit Service 724aca
		return i1;
Packit Service 724aca
	}
Packit Service 724aca
	i2 = arena_decay_compute_purge_interval_impl(tsdn, &arena->decay_muzzy,
Packit Service 724aca
	    &arena->extents_muzzy);
Packit Service 724aca
Packit Service 724aca
	return i1 < i2 ? i1 : i2;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
static void
Packit Service 724aca
background_thread_sleep(tsdn_t *tsdn, background_thread_info_t *info,
Packit Service 724aca
    uint64_t interval) {
Packit Service 724aca
	if (config_stats) {
Packit Service 724aca
		info->tot_n_runs++;
Packit Service 724aca
	}
Packit Service 724aca
	info->npages_to_purge_new = 0;
Packit Service 724aca
Packit Service 724aca
	struct timeval tv;
Packit Service 724aca
	/* Specific clock required by timedwait. */
Packit Service 724aca
	gettimeofday(&tv, NULL);
Packit Service 724aca
	nstime_t before_sleep;
Packit Service 724aca
	nstime_init2(&before_sleep, tv.tv_sec, tv.tv_usec * 1000);
Packit Service 724aca
Packit Service 724aca
	int ret;
Packit Service 724aca
	if (interval == BACKGROUND_THREAD_INDEFINITE_SLEEP) {
Packit Service 724aca
		assert(background_thread_indefinite_sleep(info));
Packit Service 724aca
		ret = pthread_cond_wait(&info->cond, &info->mtx.lock);
Packit Service 724aca
		assert(ret == 0);
Packit Service 724aca
	} else {
Packit Service 724aca
		assert(interval >= BACKGROUND_THREAD_MIN_INTERVAL_NS &&
Packit Service 724aca
		    interval <= BACKGROUND_THREAD_INDEFINITE_SLEEP);
Packit Service 724aca
		/* We need malloc clock (can be different from tv). */
Packit Service 724aca
		nstime_t next_wakeup;
Packit Service 724aca
		nstime_init(&next_wakeup, 0);
Packit Service 724aca
		nstime_update(&next_wakeup);
Packit Service 724aca
		nstime_iadd(&next_wakeup, interval);
Packit Service 724aca
		assert(nstime_ns(&next_wakeup) <
Packit Service 724aca
		    BACKGROUND_THREAD_INDEFINITE_SLEEP);
Packit Service 724aca
		background_thread_wakeup_time_set(tsdn, info,
Packit Service 724aca
		    nstime_ns(&next_wakeup));
Packit Service 724aca
Packit Service 724aca
		nstime_t ts_wakeup;
Packit Service 724aca
		nstime_copy(&ts_wakeup, &before_sleep);
Packit Service 724aca
		nstime_iadd(&ts_wakeup, interval);
Packit Service 724aca
		struct timespec ts;
Packit Service 724aca
		ts.tv_sec = (size_t)nstime_sec(&ts_wakeup);
Packit Service 724aca
		ts.tv_nsec = (size_t)nstime_nsec(&ts_wakeup);
Packit Service 724aca
Packit Service 724aca
		assert(!background_thread_indefinite_sleep(info));
Packit Service 724aca
		ret = pthread_cond_timedwait(&info->cond, &info->mtx.lock, &ts);
Packit Service 724aca
		assert(ret == ETIMEDOUT || ret == 0);
Packit Service 724aca
		background_thread_wakeup_time_set(tsdn, info,
Packit Service 724aca
		    BACKGROUND_THREAD_INDEFINITE_SLEEP);
Packit Service 724aca
	}
Packit Service 724aca
	if (config_stats) {
Packit Service 724aca
		gettimeofday(&tv, NULL);
Packit Service 724aca
		nstime_t after_sleep;
Packit Service 724aca
		nstime_init2(&after_sleep, tv.tv_sec, tv.tv_usec * 1000);
Packit Service 724aca
		if (nstime_compare(&after_sleep, &before_sleep) > 0) {
Packit Service 724aca
			nstime_subtract(&after_sleep, &before_sleep);
Packit Service 724aca
			nstime_add(&info->tot_sleep_time, &after_sleep);
Packit Service 724aca
		}
Packit Service 724aca
	}
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
static bool
Packit Service 724aca
background_thread_pause_check(tsdn_t *tsdn, background_thread_info_t *info) {
Packit Service 724aca
	if (unlikely(info->state == background_thread_paused)) {
Packit Service 724aca
		malloc_mutex_unlock(tsdn, &info->mtx);
Packit Service 724aca
		/* Wait on global lock to update status. */
Packit Service 724aca
		malloc_mutex_lock(tsdn, &background_thread_lock);
Packit Service 724aca
		malloc_mutex_unlock(tsdn, &background_thread_lock);
Packit Service 724aca
		malloc_mutex_lock(tsdn, &info->mtx);
Packit Service 724aca
		return true;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	return false;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
static inline void
Packit Service 724aca
background_work_sleep_once(tsdn_t *tsdn, background_thread_info_t *info, unsigned ind) {
Packit Service 724aca
	uint64_t min_interval = BACKGROUND_THREAD_INDEFINITE_SLEEP;
Packit Service 724aca
	unsigned narenas = narenas_total_get();
Packit Service 724aca
Packit Service 724aca
	for (unsigned i = ind; i < narenas; i += max_background_threads) {
Packit Service 724aca
		arena_t *arena = arena_get(tsdn, i, false);
Packit Service 724aca
		if (!arena) {
Packit Service 724aca
			continue;
Packit Service 724aca
		}
Packit Service 724aca
		arena_decay(tsdn, arena, true, false);
Packit Service 724aca
		if (min_interval == BACKGROUND_THREAD_MIN_INTERVAL_NS) {
Packit Service 724aca
			/* Min interval will be used. */
Packit Service 724aca
			continue;
Packit Service 724aca
		}
Packit Service 724aca
		uint64_t interval = arena_decay_compute_purge_interval(tsdn,
Packit Service 724aca
		    arena);
Packit Service 724aca
		assert(interval >= BACKGROUND_THREAD_MIN_INTERVAL_NS);
Packit Service 724aca
		if (min_interval > interval) {
Packit Service 724aca
			min_interval = interval;
Packit Service 724aca
		}
Packit Service 724aca
	}
Packit Service 724aca
	background_thread_sleep(tsdn, info, min_interval);
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
static bool
Packit Service 724aca
background_threads_disable_single(tsd_t *tsd, background_thread_info_t *info) {
Packit Service 724aca
	if (info == &background_thread_info[0]) {
Packit Service 724aca
		malloc_mutex_assert_owner(tsd_tsdn(tsd),
Packit Service 724aca
		    &background_thread_lock);
Packit Service 724aca
	} else {
Packit Service 724aca
		malloc_mutex_assert_not_owner(tsd_tsdn(tsd),
Packit Service 724aca
		    &background_thread_lock);
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	pre_reentrancy(tsd, NULL);
Packit Service 724aca
	malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);
Packit Service 724aca
	bool has_thread;
Packit Service 724aca
	assert(info->state != background_thread_paused);
Packit Service 724aca
	if (info->state == background_thread_started) {
Packit Service 724aca
		has_thread = true;
Packit Service 724aca
		info->state = background_thread_stopped;
Packit Service 724aca
		pthread_cond_signal(&info->cond);
Packit Service 724aca
	} else {
Packit Service 724aca
		has_thread = false;
Packit Service 724aca
	}
Packit Service 724aca
	malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);
Packit Service 724aca
Packit Service 724aca
	if (!has_thread) {
Packit Service 724aca
		post_reentrancy(tsd);
Packit Service 724aca
		return false;
Packit Service 724aca
	}
Packit Service 724aca
	void *ret;
Packit Service 724aca
	if (pthread_join(info->thread, &ret)) {
Packit Service 724aca
		post_reentrancy(tsd);
Packit Service 724aca
		return true;
Packit Service 724aca
	}
Packit Service 724aca
	assert(ret == NULL);
Packit Service 724aca
	n_background_threads--;
Packit Service 724aca
	post_reentrancy(tsd);
Packit Service 724aca
Packit Service 724aca
	return false;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
static void *background_thread_entry(void *ind_arg);
Packit Service 724aca
Packit Service 724aca
static int
Packit Service 724aca
background_thread_create_signals_masked(pthread_t *thread,
Packit Service 724aca
    const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) {
Packit Service 724aca
	/*
Packit Service 724aca
	 * Mask signals during thread creation so that the thread inherits
Packit Service 724aca
	 * an empty signal set.
Packit Service 724aca
	 */
Packit Service 724aca
	sigset_t set;
Packit Service 724aca
	sigfillset(&set);
Packit Service 724aca
	sigset_t oldset;
Packit Service 724aca
	int mask_err = pthread_sigmask(SIG_SETMASK, &set, &oldset);
Packit Service 724aca
	if (mask_err != 0) {
Packit Service 724aca
		return mask_err;
Packit Service 724aca
	}
Packit Service 724aca
	int create_err = pthread_create_wrapper(thread, attr, start_routine,
Packit Service 724aca
	    arg);
Packit Service 724aca
	/*
Packit Service 724aca
	 * Restore the signal mask.  Failure to restore the signal mask here
Packit Service 724aca
	 * changes program behavior.
Packit Service 724aca
	 */
Packit Service 724aca
	int restore_err = pthread_sigmask(SIG_SETMASK, &oldset, NULL);
Packit Service 724aca
	if (restore_err != 0) {
Packit Service 724aca
		malloc_printf("<jemalloc>: background thread creation "
Packit Service 724aca
		    "failed (%d), and signal mask restoration failed "
Packit Service 724aca
		    "(%d)\n", create_err, restore_err);
Packit Service 724aca
		if (opt_abort) {
Packit Service 724aca
			abort();
Packit Service 724aca
		}
Packit Service 724aca
	}
Packit Service 724aca
	return create_err;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
static bool
Packit Service 724aca
check_background_thread_creation(tsd_t *tsd, unsigned *n_created,
Packit Service 724aca
    bool *created_threads) {
Packit Service 724aca
	bool ret = false;
Packit Service 724aca
	if (likely(*n_created == n_background_threads)) {
Packit Service 724aca
		return ret;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	tsdn_t *tsdn = tsd_tsdn(tsd);
Packit Service 724aca
	malloc_mutex_unlock(tsdn, &background_thread_info[0].mtx);
Packit Service 724aca
	for (unsigned i = 1; i < max_background_threads; i++) {
Packit Service 724aca
		if (created_threads[i]) {
Packit Service 724aca
			continue;
Packit Service 724aca
		}
Packit Service 724aca
		background_thread_info_t *info = &background_thread_info[i];
Packit Service 724aca
		malloc_mutex_lock(tsdn, &info->mtx);
Packit Service 724aca
		/*
Packit Service 724aca
		 * In case of the background_thread_paused state because of
Packit Service 724aca
		 * arena reset, delay the creation.
Packit Service 724aca
		 */
Packit Service 724aca
		bool create = (info->state == background_thread_started);
Packit Service 724aca
		malloc_mutex_unlock(tsdn, &info->mtx);
Packit Service 724aca
		if (!create) {
Packit Service 724aca
			continue;
Packit Service 724aca
		}
Packit Service 724aca
Packit Service 724aca
		pre_reentrancy(tsd, NULL);
Packit Service 724aca
		int err = background_thread_create_signals_masked(&info->thread,
Packit Service 724aca
		    NULL, background_thread_entry, (void *)(uintptr_t)i);
Packit Service 724aca
		post_reentrancy(tsd);
Packit Service 724aca
Packit Service 724aca
		if (err == 0) {
Packit Service 724aca
			(*n_created)++;
Packit Service 724aca
			created_threads[i] = true;
Packit Service 724aca
		} else {
Packit Service 724aca
			malloc_printf("<jemalloc>: background thread "
Packit Service 724aca
			    "creation failed (%d)\n", err);
Packit Service 724aca
			if (opt_abort) {
Packit Service 724aca
				abort();
Packit Service 724aca
			}
Packit Service 724aca
		}
Packit Service 724aca
		/* Return to restart the loop since we unlocked. */
Packit Service 724aca
		ret = true;
Packit Service 724aca
		break;
Packit Service 724aca
	}
Packit Service 724aca
	malloc_mutex_lock(tsdn, &background_thread_info[0].mtx);
Packit Service 724aca
Packit Service 724aca
	return ret;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
static void
Packit Service 724aca
background_thread0_work(tsd_t *tsd) {
Packit Service 724aca
	/* Thread0 is also responsible for launching / terminating threads. */
Packit Service 724aca
	VARIABLE_ARRAY(bool, created_threads, max_background_threads);
Packit Service 724aca
	unsigned i;
Packit Service 724aca
	for (i = 1; i < max_background_threads; i++) {
Packit Service 724aca
		created_threads[i] = false;
Packit Service 724aca
	}
Packit Service 724aca
	/* Start working, and create more threads when asked. */
Packit Service 724aca
	unsigned n_created = 1;
Packit Service 724aca
	while (background_thread_info[0].state != background_thread_stopped) {
Packit Service 724aca
		if (background_thread_pause_check(tsd_tsdn(tsd),
Packit Service 724aca
		    &background_thread_info[0])) {
Packit Service 724aca
			continue;
Packit Service 724aca
		}
Packit Service 724aca
		if (check_background_thread_creation(tsd, &n_created,
Packit Service 724aca
		    (bool *)&created_threads)) {
Packit Service 724aca
			continue;
Packit Service 724aca
		}
Packit Service 724aca
		background_work_sleep_once(tsd_tsdn(tsd),
Packit Service 724aca
		    &background_thread_info[0], 0);
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	/*
Packit Service 724aca
	 * Shut down other threads at exit.  Note that the ctl thread is holding
Packit Service 724aca
	 * the global background_thread mutex (and is waiting) for us.
Packit Service 724aca
	 */
Packit Service 724aca
	assert(!background_thread_enabled());
Packit Service 724aca
	for (i = 1; i < max_background_threads; i++) {
Packit Service 724aca
		background_thread_info_t *info = &background_thread_info[i];
Packit Service 724aca
		assert(info->state != background_thread_paused);
Packit Service 724aca
		if (created_threads[i]) {
Packit Service 724aca
			background_threads_disable_single(tsd, info);
Packit Service 724aca
		} else {
Packit Service 724aca
			malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);
Packit Service 724aca
			if (info->state != background_thread_stopped) {
Packit Service 724aca
				/* The thread was not created. */
Packit Service 724aca
				assert(info->state ==
Packit Service 724aca
				    background_thread_started);
Packit Service 724aca
				n_background_threads--;
Packit Service 724aca
				info->state = background_thread_stopped;
Packit Service 724aca
			}
Packit Service 724aca
			malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);
Packit Service 724aca
		}
Packit Service 724aca
	}
Packit Service 724aca
	background_thread_info[0].state = background_thread_stopped;
Packit Service 724aca
	assert(n_background_threads == 1);
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
static void
Packit Service 724aca
background_work(tsd_t *tsd, unsigned ind) {
Packit Service 724aca
	background_thread_info_t *info = &background_thread_info[ind];
Packit Service 724aca
Packit Service 724aca
	malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);
Packit Service 724aca
	background_thread_wakeup_time_set(tsd_tsdn(tsd), info,
Packit Service 724aca
	    BACKGROUND_THREAD_INDEFINITE_SLEEP);
Packit Service 724aca
	if (ind == 0) {
Packit Service 724aca
		background_thread0_work(tsd);
Packit Service 724aca
	} else {
Packit Service 724aca
		while (info->state != background_thread_stopped) {
Packit Service 724aca
			if (background_thread_pause_check(tsd_tsdn(tsd),
Packit Service 724aca
			    info)) {
Packit Service 724aca
				continue;
Packit Service 724aca
			}
Packit Service 724aca
			background_work_sleep_once(tsd_tsdn(tsd), info, ind);
Packit Service 724aca
		}
Packit Service 724aca
	}
Packit Service 724aca
	assert(info->state == background_thread_stopped);
Packit Service 724aca
	background_thread_wakeup_time_set(tsd_tsdn(tsd), info, 0);
Packit Service 724aca
	malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
static void *
Packit Service 724aca
background_thread_entry(void *ind_arg) {
Packit Service 724aca
	unsigned thread_ind = (unsigned)(uintptr_t)ind_arg;
Packit Service 724aca
	assert(thread_ind < max_background_threads);
Packit Service 724aca
#ifdef JEMALLOC_HAVE_PTHREAD_SETNAME_NP
Packit Service 724aca
	pthread_setname_np(pthread_self(), "jemalloc_bg_thd");
Packit Service 724aca
#elif defined(__FreeBSD__)
Packit Service 724aca
	pthread_set_name_np(pthread_self(), "jemalloc_bg_thd");
Packit Service 724aca
#endif
Packit Service 724aca
	if (opt_percpu_arena != percpu_arena_disabled) {
Packit Service 724aca
		set_current_thread_affinity((int)thread_ind);
Packit Service 724aca
	}
Packit Service 724aca
	/*
Packit Service 724aca
	 * Start periodic background work.  We use internal tsd which avoids
Packit Service 724aca
	 * side effects, for example triggering new arena creation (which in
Packit Service 724aca
	 * turn triggers another background thread creation).
Packit Service 724aca
	 */
Packit Service 724aca
	background_work(tsd_internal_fetch(), thread_ind);
Packit Service 724aca
	assert(pthread_equal(pthread_self(),
Packit Service 724aca
	    background_thread_info[thread_ind].thread));
Packit Service 724aca
Packit Service 724aca
	return NULL;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
static void
Packit Service 724aca
background_thread_init(tsd_t *tsd, background_thread_info_t *info) {
Packit Service 724aca
	malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);
Packit Service 724aca
	info->state = background_thread_started;
Packit Service 724aca
	background_thread_info_init(tsd_tsdn(tsd), info);
Packit Service 724aca
	n_background_threads++;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
static bool
Packit Service 724aca
background_thread_create_locked(tsd_t *tsd, unsigned arena_ind) {
Packit Service 724aca
	assert(have_background_thread);
Packit Service 724aca
	malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);
Packit Service 724aca
Packit Service 724aca
	/* We create at most NCPUs threads. */
Packit Service 724aca
	size_t thread_ind = arena_ind % max_background_threads;
Packit Service 724aca
	background_thread_info_t *info = &background_thread_info[thread_ind];
Packit Service 724aca
Packit Service 724aca
	bool need_new_thread;
Packit Service 724aca
	malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);
Packit Service 724aca
	need_new_thread = background_thread_enabled() &&
Packit Service 724aca
	    (info->state == background_thread_stopped);
Packit Service 724aca
	if (need_new_thread) {
Packit Service 724aca
		background_thread_init(tsd, info);
Packit Service 724aca
	}
Packit Service 724aca
	malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);
Packit Service 724aca
	if (!need_new_thread) {
Packit Service 724aca
		return false;
Packit Service 724aca
	}
Packit Service 724aca
	if (arena_ind != 0) {
Packit Service 724aca
		/* Threads are created asynchronously by Thread 0. */
Packit Service 724aca
		background_thread_info_t *t0 = &background_thread_info[0];
Packit Service 724aca
		malloc_mutex_lock(tsd_tsdn(tsd), &t0->mtx);
Packit Service 724aca
		assert(t0->state == background_thread_started);
Packit Service 724aca
		pthread_cond_signal(&t0->cond);
Packit Service 724aca
		malloc_mutex_unlock(tsd_tsdn(tsd), &t0->mtx);
Packit Service 724aca
Packit Service 724aca
		return false;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	pre_reentrancy(tsd, NULL);
Packit Service 724aca
	/*
Packit Service 724aca
	 * To avoid complications (besides reentrancy), create internal
Packit Service 724aca
	 * background threads with the underlying pthread_create.
Packit Service 724aca
	 */
Packit Service 724aca
	int err = background_thread_create_signals_masked(&info->thread, NULL,
Packit Service 724aca
	    background_thread_entry, (void *)thread_ind);
Packit Service 724aca
	post_reentrancy(tsd);
Packit Service 724aca
Packit Service 724aca
	if (err != 0) {
Packit Service 724aca
		malloc_printf("<jemalloc>: arena 0 background thread creation "
Packit Service 724aca
		    "failed (%d)\n", err);
Packit Service 724aca
		malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);
Packit Service 724aca
		info->state = background_thread_stopped;
Packit Service 724aca
		n_background_threads--;
Packit Service 724aca
		malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);
Packit Service 724aca
Packit Service 724aca
		return true;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	return false;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
/* Create a new background thread if needed. */
Packit Service 724aca
bool
Packit Service 724aca
background_thread_create(tsd_t *tsd, unsigned arena_ind) {
Packit Service 724aca
	assert(have_background_thread);
Packit Service 724aca
Packit Service 724aca
	bool ret;
Packit Service 724aca
	malloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock);
Packit Service 724aca
	ret = background_thread_create_locked(tsd, arena_ind);
Packit Service 724aca
	malloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock);
Packit Service 724aca
Packit Service 724aca
	return ret;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
bool
Packit Service 724aca
background_threads_enable(tsd_t *tsd) {
Packit Service 724aca
	assert(n_background_threads == 0);
Packit Service 724aca
	assert(background_thread_enabled());
Packit Service 724aca
	malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);
Packit Service 724aca
Packit Service 724aca
	VARIABLE_ARRAY(bool, marked, max_background_threads);
Packit Service 724aca
	unsigned i, nmarked;
Packit Service 724aca
	for (i = 0; i < max_background_threads; i++) {
Packit Service 724aca
		marked[i] = false;
Packit Service 724aca
	}
Packit Service 724aca
	nmarked = 0;
Packit Service 724aca
	/* Thread 0 is required and created at the end. */
Packit Service 724aca
	marked[0] = true;
Packit Service 724aca
	/* Mark the threads we need to create for thread 0. */
Packit Service 724aca
	unsigned n = narenas_total_get();
Packit Service 724aca
	for (i = 1; i < n; i++) {
Packit Service 724aca
		if (marked[i % max_background_threads] ||
Packit Service 724aca
		    arena_get(tsd_tsdn(tsd), i, false) == NULL) {
Packit Service 724aca
			continue;
Packit Service 724aca
		}
Packit Service 724aca
		background_thread_info_t *info = &background_thread_info[
Packit Service 724aca
		    i % max_background_threads];
Packit Service 724aca
		malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);
Packit Service 724aca
		assert(info->state == background_thread_stopped);
Packit Service 724aca
		background_thread_init(tsd, info);
Packit Service 724aca
		malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);
Packit Service 724aca
		marked[i % max_background_threads] = true;
Packit Service 724aca
		if (++nmarked == max_background_threads) {
Packit Service 724aca
			break;
Packit Service 724aca
		}
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	return background_thread_create_locked(tsd, 0);
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
bool
Packit Service 724aca
background_threads_disable(tsd_t *tsd) {
Packit Service 724aca
	assert(!background_thread_enabled());
Packit Service 724aca
	malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);
Packit Service 724aca
Packit Service 724aca
	/* Thread 0 will be responsible for terminating other threads. */
Packit Service 724aca
	if (background_threads_disable_single(tsd,
Packit Service 724aca
	    &background_thread_info[0])) {
Packit Service 724aca
		return true;
Packit Service 724aca
	}
Packit Service 724aca
	assert(n_background_threads == 0);
Packit Service 724aca
Packit Service 724aca
	return false;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
/* Check if we need to signal the background thread early. */
Packit Service 724aca
void
Packit Service 724aca
background_thread_interval_check(tsdn_t *tsdn, arena_t *arena,
Packit Service 724aca
    arena_decay_t *decay, size_t npages_new) {
Packit Service 724aca
	background_thread_info_t *info = arena_background_thread_info_get(
Packit Service 724aca
	    arena);
Packit Service 724aca
	if (malloc_mutex_trylock(tsdn, &info->mtx)) {
Packit Service 724aca
		/*
Packit Service 724aca
		 * Background thread may hold the mutex for a long period of
Packit Service 724aca
		 * time.  We'd like to avoid the variance on application
Packit Service 724aca
		 * threads.  So keep this non-blocking, and leave the work to a
Packit Service 724aca
		 * future epoch.
Packit Service 724aca
		 */
Packit Service 724aca
		return;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	if (info->state != background_thread_started) {
Packit Service 724aca
		goto label_done;
Packit Service 724aca
	}
Packit Service 724aca
	if (malloc_mutex_trylock(tsdn, &decay->mtx)) {
Packit Service 724aca
		goto label_done;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	ssize_t decay_time = atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED);
Packit Service 724aca
	if (decay_time <= 0) {
Packit Service 724aca
		/* Purging is eagerly done or disabled currently. */
Packit Service 724aca
		goto label_done_unlock2;
Packit Service 724aca
	}
Packit Service 724aca
	uint64_t decay_interval_ns = nstime_ns(&decay->interval);
Packit Service 724aca
	assert(decay_interval_ns > 0);
Packit Service 724aca
Packit Service 724aca
	nstime_t diff;
Packit Service 724aca
	nstime_init(&diff, background_thread_wakeup_time_get(info));
Packit Service 724aca
	if (nstime_compare(&diff, &decay->epoch) <= 0) {
Packit Service 724aca
		goto label_done_unlock2;
Packit Service 724aca
	}
Packit Service 724aca
	nstime_subtract(&diff, &decay->epoch);
Packit Service 724aca
	if (nstime_ns(&diff) < BACKGROUND_THREAD_MIN_INTERVAL_NS) {
Packit Service 724aca
		goto label_done_unlock2;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	if (npages_new > 0) {
Packit Service 724aca
		size_t n_epoch = (size_t)(nstime_ns(&diff) / decay_interval_ns);
Packit Service 724aca
		/*
Packit Service 724aca
		 * Compute how many new pages we would need to purge by the next
Packit Service 724aca
		 * wakeup, which is used to determine if we should signal the
Packit Service 724aca
		 * background thread.
Packit Service 724aca
		 */
Packit Service 724aca
		uint64_t npurge_new;
Packit Service 724aca
		if (n_epoch >= SMOOTHSTEP_NSTEPS) {
Packit Service 724aca
			npurge_new = npages_new;
Packit Service 724aca
		} else {
Packit Service 724aca
			uint64_t h_steps_max = h_steps[SMOOTHSTEP_NSTEPS - 1];
Packit Service 724aca
			assert(h_steps_max >=
Packit Service 724aca
			    h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]);
Packit Service 724aca
			npurge_new = npages_new * (h_steps_max -
Packit Service 724aca
			    h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]);
Packit Service 724aca
			npurge_new >>= SMOOTHSTEP_BFP;
Packit Service 724aca
		}
Packit Service 724aca
		info->npages_to_purge_new += npurge_new;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	bool should_signal;
Packit Service 724aca
	if (info->npages_to_purge_new > BACKGROUND_THREAD_NPAGES_THRESHOLD) {
Packit Service 724aca
		should_signal = true;
Packit Service 724aca
	} else if (unlikely(background_thread_indefinite_sleep(info)) &&
Packit Service 724aca
	    (extents_npages_get(&arena->extents_dirty) > 0 ||
Packit Service 724aca
	    extents_npages_get(&arena->extents_muzzy) > 0 ||
Packit Service 724aca
	    info->npages_to_purge_new > 0)) {
Packit Service 724aca
		should_signal = true;
Packit Service 724aca
	} else {
Packit Service 724aca
		should_signal = false;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	if (should_signal) {
Packit Service 724aca
		info->npages_to_purge_new = 0;
Packit Service 724aca
		pthread_cond_signal(&info->cond);
Packit Service 724aca
	}
Packit Service 724aca
label_done_unlock2:
Packit Service 724aca
	malloc_mutex_unlock(tsdn, &decay->mtx);
Packit Service 724aca
label_done:
Packit Service 724aca
	malloc_mutex_unlock(tsdn, &info->mtx);
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
void
Packit Service 724aca
background_thread_prefork0(tsdn_t *tsdn) {
Packit Service 724aca
	malloc_mutex_prefork(tsdn, &background_thread_lock);
Packit Service 724aca
	background_thread_enabled_at_fork = background_thread_enabled();
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
void
Packit Service 724aca
background_thread_prefork1(tsdn_t *tsdn) {
Packit Service 724aca
	for (unsigned i = 0; i < max_background_threads; i++) {
Packit Service 724aca
		malloc_mutex_prefork(tsdn, &background_thread_info[i].mtx);
Packit Service 724aca
	}
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
void
Packit Service 724aca
background_thread_postfork_parent(tsdn_t *tsdn) {
Packit Service 724aca
	for (unsigned i = 0; i < max_background_threads; i++) {
Packit Service 724aca
		malloc_mutex_postfork_parent(tsdn,
Packit Service 724aca
		    &background_thread_info[i].mtx);
Packit Service 724aca
	}
Packit Service 724aca
	malloc_mutex_postfork_parent(tsdn, &background_thread_lock);
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
void
Packit Service 724aca
background_thread_postfork_child(tsdn_t *tsdn) {
Packit Service 724aca
	for (unsigned i = 0; i < max_background_threads; i++) {
Packit Service 724aca
		malloc_mutex_postfork_child(tsdn,
Packit Service 724aca
		    &background_thread_info[i].mtx);
Packit Service 724aca
	}
Packit Service 724aca
	malloc_mutex_postfork_child(tsdn, &background_thread_lock);
Packit Service 724aca
	if (!background_thread_enabled_at_fork) {
Packit Service 724aca
		return;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	/* Clear background_thread state (reset to disabled for child). */
Packit Service 724aca
	malloc_mutex_lock(tsdn, &background_thread_lock);
Packit Service 724aca
	n_background_threads = 0;
Packit Service 724aca
	background_thread_enabled_set(tsdn, false);
Packit Service 724aca
	for (unsigned i = 0; i < max_background_threads; i++) {
Packit Service 724aca
		background_thread_info_t *info = &background_thread_info[i];
Packit Service 724aca
		malloc_mutex_lock(tsdn, &info->mtx);
Packit Service 724aca
		info->state = background_thread_stopped;
Packit Service 724aca
		int ret = pthread_cond_init(&info->cond, NULL);
Packit Service 724aca
		assert(ret == 0);
Packit Service 724aca
		background_thread_info_init(tsdn, info);
Packit Service 724aca
		malloc_mutex_unlock(tsdn, &info->mtx);
Packit Service 724aca
	}
Packit Service 724aca
	malloc_mutex_unlock(tsdn, &background_thread_lock);
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
bool
Packit Service 724aca
background_thread_stats_read(tsdn_t *tsdn, background_thread_stats_t *stats) {
Packit Service 724aca
	assert(config_stats);
Packit Service 724aca
	malloc_mutex_lock(tsdn, &background_thread_lock);
Packit Service 724aca
	if (!background_thread_enabled()) {
Packit Service 724aca
		malloc_mutex_unlock(tsdn, &background_thread_lock);
Packit Service 724aca
		return true;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	stats->num_threads = n_background_threads;
Packit Service 724aca
	uint64_t num_runs = 0;
Packit Service 724aca
	nstime_init(&stats->run_interval, 0);
Packit Service 724aca
	for (unsigned i = 0; i < max_background_threads; i++) {
Packit Service 724aca
		background_thread_info_t *info = &background_thread_info[i];
Packit Service 724aca
		if (malloc_mutex_trylock(tsdn, &info->mtx)) {
Packit Service 724aca
			/*
Packit Service 724aca
			 * Each background thread run may take a long time;
Packit Service 724aca
			 * avoid waiting on the stats if the thread is active.
Packit Service 724aca
			 */
Packit Service 724aca
			continue;
Packit Service 724aca
		}
Packit Service 724aca
		if (info->state != background_thread_stopped) {
Packit Service 724aca
			num_runs += info->tot_n_runs;
Packit Service 724aca
			nstime_add(&stats->run_interval, &info->tot_sleep_time);
Packit Service 724aca
		}
Packit Service 724aca
		malloc_mutex_unlock(tsdn, &info->mtx);
Packit Service 724aca
	}
Packit Service 724aca
	stats->num_runs = num_runs;
Packit Service 724aca
	if (num_runs > 0) {
Packit Service 724aca
		nstime_idivide(&stats->run_interval, num_runs);
Packit Service 724aca
	}
Packit Service 724aca
	malloc_mutex_unlock(tsdn, &background_thread_lock);
Packit Service 724aca
Packit Service 724aca
	return false;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
#undef BACKGROUND_THREAD_NPAGES_THRESHOLD
Packit Service 724aca
#undef BILLION
Packit Service 724aca
#undef BACKGROUND_THREAD_MIN_INTERVAL_NS
Packit Service 724aca
Packit Service 724aca
#ifdef JEMALLOC_HAVE_DLSYM
Packit Service 724aca
#include <dlfcn.h>
Packit Service 724aca
#endif
Packit Service 724aca
Packit Service 724aca
static bool
Packit Service 724aca
pthread_create_fptr_init(void) {
Packit Service 724aca
	if (pthread_create_fptr != NULL) {
Packit Service 724aca
		return false;
Packit Service 724aca
	}
Packit Service 724aca
	/*
Packit Service 724aca
	 * Try the next symbol first, because 1) when use lazy_lock we have a
Packit Service 724aca
	 * wrapper for pthread_create; and 2) application may define its own
Packit Service 724aca
	 * wrapper as well (and can call malloc within the wrapper).
Packit Service 724aca
	 */
Packit Service 724aca
#ifdef JEMALLOC_HAVE_DLSYM
Packit Service 724aca
	pthread_create_fptr = dlsym(RTLD_NEXT, "pthread_create");
Packit Service 724aca
#else
Packit Service 724aca
	pthread_create_fptr = NULL;
Packit Service 724aca
#endif
Packit Service 724aca
	if (pthread_create_fptr == NULL) {
Packit Service 724aca
		if (config_lazy_lock) {
Packit Service 724aca
			malloc_write("<jemalloc>: Error in dlsym(RTLD_NEXT, "
Packit Service 724aca
			    "\"pthread_create\")\n");
Packit Service 724aca
			abort();
Packit Service 724aca
		} else {
Packit Service 724aca
			/* Fall back to the default symbol. */
Packit Service 724aca
			pthread_create_fptr = pthread_create;
Packit Service 724aca
		}
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	return false;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
/*
Packit Service 724aca
 * When lazy lock is enabled, we need to make sure setting isthreaded before
Packit Service 724aca
 * taking any background_thread locks.  This is called early in ctl (instead of
Packit Service 724aca
 * wait for the pthread_create calls to trigger) because the mutex is required
Packit Service 724aca
 * before creating background threads.
Packit Service 724aca
 */
Packit Service 724aca
void
Packit Service 724aca
background_thread_ctl_init(tsdn_t *tsdn) {
Packit Service 724aca
	malloc_mutex_assert_not_owner(tsdn, &background_thread_lock);
Packit Service 724aca
#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER
Packit Service 724aca
	pthread_create_fptr_init();
Packit Service 724aca
	pthread_create_wrapper_init();
Packit Service 724aca
#endif
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
#endif /* defined(JEMALLOC_BACKGROUND_THREAD) */
Packit Service 724aca
Packit Service 724aca
bool
Packit Service 724aca
background_thread_boot0(void) {
Packit Service 724aca
	if (!have_background_thread && opt_background_thread) {
Packit Service 724aca
		malloc_printf("<jemalloc>: option background_thread currently "
Packit Service 724aca
		    "supports pthread only\n");
Packit Service 724aca
		return true;
Packit Service 724aca
	}
Packit Service 724aca
#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER
Packit Service 724aca
	if ((config_lazy_lock || opt_background_thread) &&
Packit Service 724aca
	    pthread_create_fptr_init()) {
Packit Service 724aca
		return true;
Packit Service 724aca
	}
Packit Service 724aca
#endif
Packit Service 724aca
	return false;
Packit Service 724aca
}
Packit Service 724aca
Packit Service 724aca
bool
Packit Service 724aca
background_thread_boot1(tsdn_t *tsdn) {
Packit Service 724aca
#ifdef JEMALLOC_BACKGROUND_THREAD
Packit Service 724aca
	assert(have_background_thread);
Packit Service 724aca
	assert(narenas_total_get() > 0);
Packit Service 724aca
Packit Service 724aca
	if (opt_max_background_threads > MAX_BACKGROUND_THREAD_LIMIT) {
Packit Service 724aca
		opt_max_background_threads = DEFAULT_NUM_BACKGROUND_THREAD;
Packit Service 724aca
	}
Packit Service 724aca
	max_background_threads = opt_max_background_threads;
Packit Service 724aca
Packit Service 724aca
	background_thread_enabled_set(tsdn, opt_background_thread);
Packit Service 724aca
	if (malloc_mutex_init(&background_thread_lock,
Packit Service 724aca
	    "background_thread_global",
Packit Service 724aca
	    WITNESS_RANK_BACKGROUND_THREAD_GLOBAL,
Packit Service 724aca
	    malloc_mutex_rank_exclusive)) {
Packit Service 724aca
		return true;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	background_thread_info = (background_thread_info_t *)base_alloc(tsdn,
Packit Service 724aca
	    b0get(), opt_max_background_threads *
Packit Service 724aca
	    sizeof(background_thread_info_t), CACHELINE);
Packit Service 724aca
	if (background_thread_info == NULL) {
Packit Service 724aca
		return true;
Packit Service 724aca
	}
Packit Service 724aca
Packit Service 724aca
	for (unsigned i = 0; i < max_background_threads; i++) {
Packit Service 724aca
		background_thread_info_t *info = &background_thread_info[i];
Packit Service 724aca
		/* Thread mutex is rank_inclusive because of thread0. */
Packit Service 724aca
		if (malloc_mutex_init(&info->mtx, "background_thread",
Packit Service 724aca
		    WITNESS_RANK_BACKGROUND_THREAD,
Packit Service 724aca
		    malloc_mutex_address_ordered)) {
Packit Service 724aca
			return true;
Packit Service 724aca
		}
Packit Service 724aca
		if (pthread_cond_init(&info->cond, NULL)) {
Packit Service 724aca
			return true;
Packit Service 724aca
		}
Packit Service 724aca
		malloc_mutex_lock(tsdn, &info->mtx);
Packit Service 724aca
		info->state = background_thread_stopped;
Packit Service 724aca
		background_thread_info_init(tsdn, info);
Packit Service 724aca
		malloc_mutex_unlock(tsdn, &info->mtx);
Packit Service 724aca
	}
Packit Service 724aca
#endif
Packit Service 724aca
Packit Service 724aca
	return false;
Packit Service 724aca
}