xref: /netbsd-src/external/bsd/jemalloc.old/dist/src/background_thread.c (revision 8e33eff89e26cf71871ead62f0d5063e1313c33a)
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