Home | History | Annotate | Download | only in internal
      1 #ifndef JEMALLOC_INTERNAL_MUTEX_H
      2 #define JEMALLOC_INTERNAL_MUTEX_H
      3 
      4 #include "jemalloc/internal/atomic.h"
      5 #include "jemalloc/internal/mutex_prof.h"
      6 #include "jemalloc/internal/tsd.h"
      7 #include "jemalloc/internal/witness.h"
      8 
      9 typedef enum {
     10 	/* Can only acquire one mutex of a given witness rank at a time. */
     11 	malloc_mutex_rank_exclusive,
     12 	/*
     13 	 * Can acquire multiple mutexes of the same witness rank, but in
     14 	 * address-ascending order only.
     15 	 */
     16 	malloc_mutex_address_ordered
     17 } malloc_mutex_lock_order_t;
     18 
     19 typedef struct malloc_mutex_s malloc_mutex_t;
     20 struct malloc_mutex_s {
     21 	union {
     22 		struct {
     23 			/*
     24 			 * prof_data is defined first to reduce cacheline
     25 			 * bouncing: the data is not touched by the mutex holder
     26 			 * during unlocking, while might be modified by
     27 			 * contenders.  Having it before the mutex itself could
     28 			 * avoid prefetching a modified cacheline (for the
     29 			 * unlocking thread).
     30 			 */
     31 			mutex_prof_data_t	prof_data;
     32 #ifdef _WIN32
     33 #  if _WIN32_WINNT >= 0x0600
     34 			SRWLOCK         	lock;
     35 #  else
     36 			CRITICAL_SECTION	lock;
     37 #  endif
     38 #elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
     39 			os_unfair_lock		lock;
     40 #elif (defined(JEMALLOC_OSSPIN))
     41 			OSSpinLock		lock;
     42 #elif (defined(JEMALLOC_MUTEX_INIT_CB))
     43 			pthread_mutex_t		lock;
     44 			malloc_mutex_t		*postponed_next;
     45 #else
     46 			pthread_mutex_t		lock;
     47 #endif
     48 		};
     49 		/*
     50 		 * We only touch witness when configured w/ debug.  However we
     51 		 * keep the field in a union when !debug so that we don't have
     52 		 * to pollute the code base with #ifdefs, while avoid paying the
     53 		 * memory cost.
     54 		 */
     55 #if !defined(JEMALLOC_DEBUG)
     56 		witness_t			witness;
     57 		malloc_mutex_lock_order_t	lock_order;
     58 #endif
     59 	};
     60 
     61 #if defined(JEMALLOC_DEBUG)
     62 	witness_t			witness;
     63 	malloc_mutex_lock_order_t	lock_order;
     64 #endif
     65 };
     66 
     67 /*
     68  * Based on benchmark results, a fixed spin with this amount of retries works
     69  * well for our critical sections.
     70  */
     71 #define MALLOC_MUTEX_MAX_SPIN 250
     72 
     73 #ifdef _WIN32
     74 #  if _WIN32_WINNT >= 0x0600
     75 #    define MALLOC_MUTEX_LOCK(m)    AcquireSRWLockExclusive(&(m)->lock)
     76 #    define MALLOC_MUTEX_UNLOCK(m)  ReleaseSRWLockExclusive(&(m)->lock)
     77 #    define MALLOC_MUTEX_TRYLOCK(m) (!TryAcquireSRWLockExclusive(&(m)->lock))
     78 #  else
     79 #    define MALLOC_MUTEX_LOCK(m)    EnterCriticalSection(&(m)->lock)
     80 #    define MALLOC_MUTEX_UNLOCK(m)  LeaveCriticalSection(&(m)->lock)
     81 #    define MALLOC_MUTEX_TRYLOCK(m) (!TryEnterCriticalSection(&(m)->lock))
     82 #  endif
     83 #elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
     84 #    define MALLOC_MUTEX_LOCK(m)    os_unfair_lock_lock(&(m)->lock)
     85 #    define MALLOC_MUTEX_UNLOCK(m)  os_unfair_lock_unlock(&(m)->lock)
     86 #    define MALLOC_MUTEX_TRYLOCK(m) (!os_unfair_lock_trylock(&(m)->lock))
     87 #elif (defined(JEMALLOC_OSSPIN))
     88 #    define MALLOC_MUTEX_LOCK(m)    OSSpinLockLock(&(m)->lock)
     89 #    define MALLOC_MUTEX_UNLOCK(m)  OSSpinLockUnlock(&(m)->lock)
     90 #    define MALLOC_MUTEX_TRYLOCK(m) (!OSSpinLockTry(&(m)->lock))
     91 #else
     92 #    define MALLOC_MUTEX_LOCK(m)    pthread_mutex_lock(&(m)->lock)
     93 #    define MALLOC_MUTEX_UNLOCK(m)  pthread_mutex_unlock(&(m)->lock)
     94 #    define MALLOC_MUTEX_TRYLOCK(m) (pthread_mutex_trylock(&(m)->lock) != 0)
     95 #endif
     96 
     97 #define LOCK_PROF_DATA_INITIALIZER					\
     98     {NSTIME_ZERO_INITIALIZER, NSTIME_ZERO_INITIALIZER, 0, 0, 0,		\
     99 	    ATOMIC_INIT(0), 0, NULL, 0}
    100 
    101 #ifdef _WIN32
    102 #  define MALLOC_MUTEX_INITIALIZER
    103 #elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
    104 #  define MALLOC_MUTEX_INITIALIZER					\
    105      {{{LOCK_PROF_DATA_INITIALIZER, OS_UNFAIR_LOCK_INIT}},		\
    106       WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT)}
    107 #elif (defined(JEMALLOC_OSSPIN))
    108 #  define MALLOC_MUTEX_INITIALIZER					\
    109      {{{LOCK_PROF_DATA_INITIALIZER, 0}},				\
    110       WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT)}
    111 #elif (defined(JEMALLOC_MUTEX_INIT_CB))
    112 #  define MALLOC_MUTEX_INITIALIZER					\
    113      {{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, NULL}},	\
    114       WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT)}
    115 #else
    116 #    define MALLOC_MUTEX_TYPE PTHREAD_MUTEX_DEFAULT
    117 #    define MALLOC_MUTEX_INITIALIZER					\
    118        {{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER}},	\
    119         WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT)}
    120 #endif
    121 
    122 #ifdef JEMALLOC_LAZY_LOCK
    123 extern bool isthreaded;
    124 #else
    125 #  undef isthreaded /* Undo private_namespace.h definition. */
    126 #  define isthreaded true
    127 #endif
    128 
    129 bool malloc_mutex_init(malloc_mutex_t *mutex, const char *name,
    130     witness_rank_t rank, malloc_mutex_lock_order_t lock_order);
    131 void malloc_mutex_prefork(tsdn_t *tsdn, malloc_mutex_t *mutex);
    132 void malloc_mutex_postfork_parent(tsdn_t *tsdn, malloc_mutex_t *mutex);
    133 void malloc_mutex_postfork_child(tsdn_t *tsdn, malloc_mutex_t *mutex);
    134 bool malloc_mutex_boot(void);
    135 void malloc_mutex_prof_data_reset(tsdn_t *tsdn, malloc_mutex_t *mutex);
    136 
    137 void malloc_mutex_lock_slow(malloc_mutex_t *mutex);
    138 
    139 static inline void
    140 malloc_mutex_lock_final(malloc_mutex_t *mutex) {
    141 	MALLOC_MUTEX_LOCK(mutex);
    142 }
    143 
    144 static inline bool
    145 malloc_mutex_trylock_final(malloc_mutex_t *mutex) {
    146 	return MALLOC_MUTEX_TRYLOCK(mutex);
    147 }
    148 
    149 static inline void
    150 mutex_owner_stats_update(tsdn_t *tsdn, malloc_mutex_t *mutex) {
    151 	if (config_stats) {
    152 		mutex_prof_data_t *data = &mutex->prof_data;
    153 		data->n_lock_ops++;
    154 		if (data->prev_owner != tsdn) {
    155 			data->prev_owner = tsdn;
    156 			data->n_owner_switches++;
    157 		}
    158 	}
    159 }
    160 
    161 /* Trylock: return false if the lock is successfully acquired. */
    162 static inline bool
    163 malloc_mutex_trylock(tsdn_t *tsdn, malloc_mutex_t *mutex) {
    164 	witness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
    165 	if (isthreaded) {
    166 		if (malloc_mutex_trylock_final(mutex)) {
    167 			return true;
    168 		}
    169 		mutex_owner_stats_update(tsdn, mutex);
    170 	}
    171 	witness_lock(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
    172 
    173 	return false;
    174 }
    175 
    176 /* Aggregate lock prof data. */
    177 static inline void
    178 malloc_mutex_prof_merge(mutex_prof_data_t *sum, mutex_prof_data_t *data) {
    179 	nstime_add(&sum->tot_wait_time, &data->tot_wait_time);
    180 	if (nstime_compare(&sum->max_wait_time, &data->max_wait_time) < 0) {
    181 		nstime_copy(&sum->max_wait_time, &data->max_wait_time);
    182 	}
    183 
    184 	sum->n_wait_times += data->n_wait_times;
    185 	sum->n_spin_acquired += data->n_spin_acquired;
    186 
    187 	if (sum->max_n_thds < data->max_n_thds) {
    188 		sum->max_n_thds = data->max_n_thds;
    189 	}
    190 	uint32_t cur_n_waiting_thds = atomic_load_u32(&sum->n_waiting_thds,
    191 	    ATOMIC_RELAXED);
    192 	uint32_t new_n_waiting_thds = cur_n_waiting_thds + atomic_load_u32(
    193 	    &data->n_waiting_thds, ATOMIC_RELAXED);
    194 	atomic_store_u32(&sum->n_waiting_thds, new_n_waiting_thds,
    195 	    ATOMIC_RELAXED);
    196 	sum->n_owner_switches += data->n_owner_switches;
    197 	sum->n_lock_ops += data->n_lock_ops;
    198 }
    199 
    200 static inline void
    201 malloc_mutex_lock(tsdn_t *tsdn, malloc_mutex_t *mutex) {
    202 	witness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
    203 	if (isthreaded) {
    204 		if (malloc_mutex_trylock_final(mutex)) {
    205 			malloc_mutex_lock_slow(mutex);
    206 		}
    207 		mutex_owner_stats_update(tsdn, mutex);
    208 	}
    209 	witness_lock(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
    210 }
    211 
    212 static inline void
    213 malloc_mutex_unlock(tsdn_t *tsdn, malloc_mutex_t *mutex) {
    214 	witness_unlock(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
    215 	if (isthreaded) {
    216 		MALLOC_MUTEX_UNLOCK(mutex);
    217 	}
    218 }
    219 
    220 static inline void
    221 malloc_mutex_assert_owner(tsdn_t *tsdn, malloc_mutex_t *mutex) {
    222 	witness_assert_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
    223 }
    224 
    225 static inline void
    226 malloc_mutex_assert_not_owner(tsdn_t *tsdn, malloc_mutex_t *mutex) {
    227 	witness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
    228 }
    229 
    230 /* Copy the prof data from mutex for processing. */
    231 static inline void
    232 malloc_mutex_prof_read(tsdn_t *tsdn, mutex_prof_data_t *data,
    233     malloc_mutex_t *mutex) {
    234 	mutex_prof_data_t *source = &mutex->prof_data;
    235 	/* Can only read holding the mutex. */
    236 	malloc_mutex_assert_owner(tsdn, mutex);
    237 
    238 	/*
    239 	 * Not *really* allowed (we shouldn't be doing non-atomic loads of
    240 	 * atomic data), but the mutex protection makes this safe, and writing
    241 	 * a member-for-member copy is tedious for this situation.
    242 	 */
    243 	*data = *source;
    244 	/* n_wait_thds is not reported (modified w/o locking). */
    245 	atomic_store_u32(&data->n_waiting_thds, 0, ATOMIC_RELAXED);
    246 }
    247 
    248 #endif /* JEMALLOC_INTERNAL_MUTEX_H */
    249