xref: /freebsd-src/contrib/jemalloc/src/mutex.c (revision c5ad81420c495d1d5de04209b0ec4fcb435c322c)
1a4bd5210SJason Evans #define JEMALLOC_MUTEX_C_
2b7eaed25SJason Evans #include "jemalloc/internal/jemalloc_preamble.h"
3b7eaed25SJason Evans #include "jemalloc/internal/jemalloc_internal_includes.h"
4a4bd5210SJason Evans 
5b7eaed25SJason Evans #include "jemalloc/internal/assert.h"
6b7eaed25SJason Evans #include "jemalloc/internal/malloc_io.h"
70ef50b4eSJason Evans #include "jemalloc/internal/spin.h"
8a4bd5210SJason Evans 
9e722f8f8SJason Evans #ifndef _CRT_SPINCOUNT
10e722f8f8SJason Evans #define _CRT_SPINCOUNT 4000
11e722f8f8SJason Evans #endif
12e722f8f8SJason Evans 
13a4bd5210SJason Evans /******************************************************************************/
14a4bd5210SJason Evans /* Data. */
15a4bd5210SJason Evans 
16a4bd5210SJason Evans #ifdef JEMALLOC_LAZY_LOCK
17a4bd5210SJason Evans bool isthreaded = false;
18a4bd5210SJason Evans #endif
19a4bd5210SJason Evans #ifdef JEMALLOC_MUTEX_INIT_CB
20a4bd5210SJason Evans static bool		postpone_init = true;
21a4bd5210SJason Evans static malloc_mutex_t	*postponed_mutexes = NULL;
22a4bd5210SJason Evans #endif
23a4bd5210SJason Evans 
24a4bd5210SJason Evans /******************************************************************************/
25a4bd5210SJason Evans /*
26a4bd5210SJason Evans  * We intercept pthread_create() calls in order to toggle isthreaded if the
27a4bd5210SJason Evans  * process goes multi-threaded.
28a4bd5210SJason Evans  */
29a4bd5210SJason Evans 
30e722f8f8SJason Evans #if defined(JEMALLOC_LAZY_LOCK) && !defined(_WIN32)
31e722f8f8SJason Evans JEMALLOC_EXPORT int
pthread_create(pthread_t * __restrict thread,const pthread_attr_t * __restrict attr,void * (* start_routine)(void *),void * __restrict arg)32a4bd5210SJason Evans pthread_create(pthread_t *__restrict thread,
33a4bd5210SJason Evans     const pthread_attr_t *__restrict attr, void *(*start_routine)(void *),
34b7eaed25SJason Evans     void *__restrict arg) {
35b7eaed25SJason Evans 	return pthread_create_wrapper(thread, attr, start_routine, arg);
36a4bd5210SJason Evans }
37a4bd5210SJason Evans #endif
38a4bd5210SJason Evans 
39a4bd5210SJason Evans /******************************************************************************/
40a4bd5210SJason Evans 
41a4bd5210SJason Evans #ifdef JEMALLOC_MUTEX_INIT_CB
4282872ac0SJason Evans JEMALLOC_EXPORT int	_pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex,
43a4bd5210SJason Evans     void *(calloc_cb)(size_t, size_t));
44a4bd5210SJason Evans 
458495e8b1SKonstantin Belousov #pragma weak _pthread_mutex_init_calloc_cb
46a4bd5210SJason Evans int
_pthread_mutex_init_calloc_cb(pthread_mutex_t * mutex,void * (calloc_cb)(size_t,size_t))478495e8b1SKonstantin Belousov _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex,
48a4bd5210SJason Evans     void *(calloc_cb)(size_t, size_t))
49a4bd5210SJason Evans {
50a4bd5210SJason Evans 
518495e8b1SKonstantin Belousov 	return (((int (*)(pthread_mutex_t *, void *(*)(size_t, size_t)))
52d0e79aa3SJason Evans 	    __libc_interposing[INTERPOS__pthread_mutex_init_calloc_cb])(mutex,
53d0e79aa3SJason Evans 	    calloc_cb));
54a4bd5210SJason Evans }
55a4bd5210SJason Evans #endif
56a4bd5210SJason Evans 
57b7eaed25SJason Evans void
malloc_mutex_lock_slow(malloc_mutex_t * mutex)58b7eaed25SJason Evans malloc_mutex_lock_slow(malloc_mutex_t *mutex) {
59b7eaed25SJason Evans 	mutex_prof_data_t *data = &mutex->prof_data;
60*c5ad8142SEric van Gyzen 	nstime_t before = NSTIME_ZERO_INITIALIZER;
61e722f8f8SJason Evans 
62b7eaed25SJason Evans 	if (ncpus == 1) {
63b7eaed25SJason Evans 		goto label_spin_done;
64b7eaed25SJason Evans 	}
65b7eaed25SJason Evans 
66b7eaed25SJason Evans 	int cnt = 0, max_cnt = MALLOC_MUTEX_MAX_SPIN;
67b7eaed25SJason Evans 	do {
680ef50b4eSJason Evans 		spin_cpu_spinwait();
69*c5ad8142SEric van Gyzen 		if (!atomic_load_b(&mutex->locked, ATOMIC_RELAXED)
70*c5ad8142SEric van Gyzen                     && !malloc_mutex_trylock_final(mutex)) {
71b7eaed25SJason Evans 			data->n_spin_acquired++;
72b7eaed25SJason Evans 			return;
73b7eaed25SJason Evans 		}
74b7eaed25SJason Evans 	} while (cnt++ < max_cnt);
75b7eaed25SJason Evans 
76b7eaed25SJason Evans 	if (!config_stats) {
77b7eaed25SJason Evans 		/* Only spin is useful when stats is off. */
78b7eaed25SJason Evans 		malloc_mutex_lock_final(mutex);
79b7eaed25SJason Evans 		return;
80b7eaed25SJason Evans 	}
81b7eaed25SJason Evans label_spin_done:
82b7eaed25SJason Evans 	nstime_update(&before);
83b7eaed25SJason Evans 	/* Copy before to after to avoid clock skews. */
84b7eaed25SJason Evans 	nstime_t after;
85b7eaed25SJason Evans 	nstime_copy(&after, &before);
86b7eaed25SJason Evans 	uint32_t n_thds = atomic_fetch_add_u32(&data->n_waiting_thds, 1,
87b7eaed25SJason Evans 	    ATOMIC_RELAXED) + 1;
88b7eaed25SJason Evans 	/* One last try as above two calls may take quite some cycles. */
89b7eaed25SJason Evans 	if (!malloc_mutex_trylock_final(mutex)) {
90b7eaed25SJason Evans 		atomic_fetch_sub_u32(&data->n_waiting_thds, 1, ATOMIC_RELAXED);
91b7eaed25SJason Evans 		data->n_spin_acquired++;
92b7eaed25SJason Evans 		return;
93b7eaed25SJason Evans 	}
94b7eaed25SJason Evans 
95b7eaed25SJason Evans 	/* True slow path. */
96b7eaed25SJason Evans 	malloc_mutex_lock_final(mutex);
97b7eaed25SJason Evans 	/* Update more slow-path only counters. */
98b7eaed25SJason Evans 	atomic_fetch_sub_u32(&data->n_waiting_thds, 1, ATOMIC_RELAXED);
99b7eaed25SJason Evans 	nstime_update(&after);
100b7eaed25SJason Evans 
101b7eaed25SJason Evans 	nstime_t delta;
102b7eaed25SJason Evans 	nstime_copy(&delta, &after);
103b7eaed25SJason Evans 	nstime_subtract(&delta, &before);
104b7eaed25SJason Evans 
105b7eaed25SJason Evans 	data->n_wait_times++;
106b7eaed25SJason Evans 	nstime_add(&data->tot_wait_time, &delta);
107b7eaed25SJason Evans 	if (nstime_compare(&data->max_wait_time, &delta) < 0) {
108b7eaed25SJason Evans 		nstime_copy(&data->max_wait_time, &delta);
109b7eaed25SJason Evans 	}
110b7eaed25SJason Evans 	if (n_thds > data->max_n_thds) {
111b7eaed25SJason Evans 		data->max_n_thds = n_thds;
112b7eaed25SJason Evans 	}
113b7eaed25SJason Evans }
114b7eaed25SJason Evans 
115b7eaed25SJason Evans static void
mutex_prof_data_init(mutex_prof_data_t * data)116b7eaed25SJason Evans mutex_prof_data_init(mutex_prof_data_t *data) {
117b7eaed25SJason Evans 	memset(data, 0, sizeof(mutex_prof_data_t));
118b7eaed25SJason Evans 	nstime_init(&data->max_wait_time, 0);
119b7eaed25SJason Evans 	nstime_init(&data->tot_wait_time, 0);
120b7eaed25SJason Evans 	data->prev_owner = NULL;
121b7eaed25SJason Evans }
122b7eaed25SJason Evans 
123b7eaed25SJason Evans void
malloc_mutex_prof_data_reset(tsdn_t * tsdn,malloc_mutex_t * mutex)124b7eaed25SJason Evans malloc_mutex_prof_data_reset(tsdn_t *tsdn, malloc_mutex_t *mutex) {
125b7eaed25SJason Evans 	malloc_mutex_assert_owner(tsdn, mutex);
126b7eaed25SJason Evans 	mutex_prof_data_init(&mutex->prof_data);
127b7eaed25SJason Evans }
128b7eaed25SJason Evans 
129b7eaed25SJason Evans static int
mutex_addr_comp(const witness_t * witness1,void * mutex1,const witness_t * witness2,void * mutex2)130b7eaed25SJason Evans mutex_addr_comp(const witness_t *witness1, void *mutex1,
131b7eaed25SJason Evans     const witness_t *witness2, void *mutex2) {
132b7eaed25SJason Evans 	assert(mutex1 != NULL);
133b7eaed25SJason Evans 	assert(mutex2 != NULL);
134b7eaed25SJason Evans 	uintptr_t mu1int = (uintptr_t)mutex1;
135b7eaed25SJason Evans 	uintptr_t mu2int = (uintptr_t)mutex2;
136b7eaed25SJason Evans 	if (mu1int < mu2int) {
137b7eaed25SJason Evans 		return -1;
138b7eaed25SJason Evans 	} else if (mu1int == mu2int) {
139b7eaed25SJason Evans 		return 0;
140b7eaed25SJason Evans 	} else {
141b7eaed25SJason Evans 		return 1;
142b7eaed25SJason Evans 	}
143b7eaed25SJason Evans }
144b7eaed25SJason Evans 
145b7eaed25SJason Evans bool
malloc_mutex_first_thread(void)146b7eaed25SJason Evans malloc_mutex_first_thread(void) {
147b7eaed25SJason Evans 
148b7eaed25SJason Evans #ifndef JEMALLOC_MUTEX_INIT_CB
149b7eaed25SJason Evans 	return (malloc_mutex_first_thread());
150b7eaed25SJason Evans #else
151b7eaed25SJason Evans 	return (false);
152b7eaed25SJason Evans #endif
153b7eaed25SJason Evans }
154b7eaed25SJason Evans 
155b7eaed25SJason Evans bool
malloc_mutex_init(malloc_mutex_t * mutex,const char * name,witness_rank_t rank,malloc_mutex_lock_order_t lock_order)156b7eaed25SJason Evans malloc_mutex_init(malloc_mutex_t *mutex, const char *name,
157b7eaed25SJason Evans     witness_rank_t rank, malloc_mutex_lock_order_t lock_order) {
158b7eaed25SJason Evans 	mutex_prof_data_init(&mutex->prof_data);
159e722f8f8SJason Evans #ifdef _WIN32
160d0e79aa3SJason Evans #  if _WIN32_WINNT >= 0x0600
161d0e79aa3SJason Evans 	InitializeSRWLock(&mutex->lock);
162d0e79aa3SJason Evans #  else
163e722f8f8SJason Evans 	if (!InitializeCriticalSectionAndSpinCount(&mutex->lock,
164b7eaed25SJason Evans 	    _CRT_SPINCOUNT)) {
165b7eaed25SJason Evans 		return true;
166b7eaed25SJason Evans 	}
167d0e79aa3SJason Evans #  endif
168bde95144SJason Evans #elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
169bde95144SJason Evans        mutex->lock = OS_UNFAIR_LOCK_INIT;
170a4bd5210SJason Evans #elif (defined(JEMALLOC_MUTEX_INIT_CB))
171a4bd5210SJason Evans 	if (postpone_init) {
172a4bd5210SJason Evans 		mutex->postponed_next = postponed_mutexes;
173a4bd5210SJason Evans 		postponed_mutexes = mutex;
174a4bd5210SJason Evans 	} else {
175d0e79aa3SJason Evans 		if (_pthread_mutex_init_calloc_cb(&mutex->lock,
176b7eaed25SJason Evans 		    bootstrap_calloc) != 0) {
177b7eaed25SJason Evans 			return true;
178b7eaed25SJason Evans 		}
179a4bd5210SJason Evans 	}
180a4bd5210SJason Evans #else
181a4bd5210SJason Evans 	pthread_mutexattr_t attr;
182a4bd5210SJason Evans 
183b7eaed25SJason Evans 	if (pthread_mutexattr_init(&attr) != 0) {
184b7eaed25SJason Evans 		return true;
185b7eaed25SJason Evans 	}
186a4bd5210SJason Evans 	pthread_mutexattr_settype(&attr, MALLOC_MUTEX_TYPE);
187a4bd5210SJason Evans 	if (pthread_mutex_init(&mutex->lock, &attr) != 0) {
188a4bd5210SJason Evans 		pthread_mutexattr_destroy(&attr);
189b7eaed25SJason Evans 		return true;
190a4bd5210SJason Evans 	}
191a4bd5210SJason Evans 	pthread_mutexattr_destroy(&attr);
192a4bd5210SJason Evans #endif
193b7eaed25SJason Evans 	if (config_debug) {
194b7eaed25SJason Evans 		mutex->lock_order = lock_order;
195b7eaed25SJason Evans 		if (lock_order == malloc_mutex_address_ordered) {
196b7eaed25SJason Evans 			witness_init(&mutex->witness, name, rank,
1970ef50b4eSJason Evans 			    mutex_addr_comp, mutex);
198b7eaed25SJason Evans 		} else {
199b7eaed25SJason Evans 			witness_init(&mutex->witness, name, rank, NULL, NULL);
200b7eaed25SJason Evans 		}
201b7eaed25SJason Evans 	}
202b7eaed25SJason Evans 	return false;
203a4bd5210SJason Evans }
204a4bd5210SJason Evans 
205a4bd5210SJason Evans void
malloc_mutex_prefork(tsdn_t * tsdn,malloc_mutex_t * mutex)206b7eaed25SJason Evans malloc_mutex_prefork(tsdn_t *tsdn, malloc_mutex_t *mutex) {
2071f0a49e8SJason Evans 	malloc_mutex_lock(tsdn, mutex);
208a4bd5210SJason Evans }
209a4bd5210SJason Evans 
210a4bd5210SJason Evans void
malloc_mutex_postfork_parent(tsdn_t * tsdn,malloc_mutex_t * mutex)211b7eaed25SJason Evans malloc_mutex_postfork_parent(tsdn_t *tsdn, malloc_mutex_t *mutex) {
2121f0a49e8SJason Evans 	malloc_mutex_unlock(tsdn, mutex);
213a4bd5210SJason Evans }
214a4bd5210SJason Evans 
215a4bd5210SJason Evans void
malloc_mutex_postfork_child(tsdn_t * tsdn,malloc_mutex_t * mutex)216b7eaed25SJason Evans malloc_mutex_postfork_child(tsdn_t *tsdn, malloc_mutex_t *mutex) {
217a4bd5210SJason Evans #ifdef JEMALLOC_MUTEX_INIT_CB
2181f0a49e8SJason Evans 	malloc_mutex_unlock(tsdn, mutex);
219a4bd5210SJason Evans #else
2201f0a49e8SJason Evans 	if (malloc_mutex_init(mutex, mutex->witness.name,
221b7eaed25SJason Evans 	    mutex->witness.rank, mutex->lock_order)) {
222a4bd5210SJason Evans 		malloc_printf("<jemalloc>: Error re-initializing mutex in "
223a4bd5210SJason Evans 		    "child\n");
224b7eaed25SJason Evans 		if (opt_abort) {
225a4bd5210SJason Evans 			abort();
226a4bd5210SJason Evans 		}
227b7eaed25SJason Evans 	}
228a4bd5210SJason Evans #endif
229a4bd5210SJason Evans }
230a4bd5210SJason Evans 
231a4bd5210SJason Evans bool
malloc_mutex_boot(void)232b7eaed25SJason Evans malloc_mutex_boot(void) {
233a4bd5210SJason Evans #ifdef JEMALLOC_MUTEX_INIT_CB
234a4bd5210SJason Evans 	postpone_init = false;
235a4bd5210SJason Evans 	while (postponed_mutexes != NULL) {
236a4bd5210SJason Evans 		if (_pthread_mutex_init_calloc_cb(&postponed_mutexes->lock,
237b7eaed25SJason Evans 		    bootstrap_calloc) != 0) {
238b7eaed25SJason Evans 			return true;
239b7eaed25SJason Evans 		}
240a4bd5210SJason Evans 		postponed_mutexes = postponed_mutexes->postponed_next;
241a4bd5210SJason Evans 	}
242a4bd5210SJason Evans #endif
243b7eaed25SJason Evans 	return false;
2448495e8b1SKonstantin Belousov }
245