TSD: Make all state access happen through a function.
Shortly, tsd state will be atomic and have some complicated enough logic down the state-setting path that we should be aware of it.
This commit is contained in:
parent
e74a1a37c8
commit
982c10de35
@ -66,6 +66,8 @@ JEMALLOC_GENERATE_INT_ATOMICS(size_t, zu, LG_SIZEOF_PTR)
|
|||||||
|
|
||||||
JEMALLOC_GENERATE_INT_ATOMICS(ssize_t, zd, LG_SIZEOF_PTR)
|
JEMALLOC_GENERATE_INT_ATOMICS(ssize_t, zd, LG_SIZEOF_PTR)
|
||||||
|
|
||||||
|
JEMALLOC_GENERATE_INT_ATOMICS(uint8_t, u8, 0)
|
||||||
|
|
||||||
JEMALLOC_GENERATE_INT_ATOMICS(uint32_t, u32, 2)
|
JEMALLOC_GENERATE_INT_ATOMICS(uint32_t, u32, 2)
|
||||||
|
|
||||||
#ifdef JEMALLOC_ATOMIC_U64
|
#ifdef JEMALLOC_ATOMIC_U64
|
||||||
|
@ -156,7 +156,7 @@ pre_reentrancy(tsd_t *tsd, arena_t *arena) {
|
|||||||
if (fast) {
|
if (fast) {
|
||||||
/* Prepare slow path for reentrancy. */
|
/* Prepare slow path for reentrancy. */
|
||||||
tsd_slow_update(tsd);
|
tsd_slow_update(tsd);
|
||||||
assert(tsd->state == tsd_state_nominal_slow);
|
assert(tsd_state_get(tsd) == tsd_state_nominal_slow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,9 +107,6 @@ enum {
|
|||||||
tsd_state_uninitialized = 5
|
tsd_state_uninitialized = 5
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Manually limit tsd_state_t to a single byte. */
|
|
||||||
typedef uint8_t tsd_state_t;
|
|
||||||
|
|
||||||
/* The actual tsd. */
|
/* The actual tsd. */
|
||||||
struct tsd_s {
|
struct tsd_s {
|
||||||
/*
|
/*
|
||||||
@ -117,13 +114,25 @@ struct tsd_s {
|
|||||||
* module. Access any thread-local state through the getters and
|
* module. Access any thread-local state through the getters and
|
||||||
* setters below.
|
* setters below.
|
||||||
*/
|
*/
|
||||||
tsd_state_t state;
|
|
||||||
|
/* We manually limit the state to just a single byte. */
|
||||||
|
uint8_t state;
|
||||||
#define O(n, t, nt) \
|
#define O(n, t, nt) \
|
||||||
t use_a_getter_or_setter_instead_##n;
|
t use_a_getter_or_setter_instead_##n;
|
||||||
MALLOC_TSD
|
MALLOC_TSD
|
||||||
#undef O
|
#undef O
|
||||||
};
|
};
|
||||||
|
|
||||||
|
JEMALLOC_ALWAYS_INLINE uint8_t
|
||||||
|
tsd_state_get(tsd_t *tsd) {
|
||||||
|
return tsd->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
JEMALLOC_ALWAYS_INLINE void
|
||||||
|
tsd_state_set(tsd_t *tsd, uint8_t state) {
|
||||||
|
tsd->state = state;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wrapper around tsd_t that makes it possible to avoid implicit conversion
|
* Wrapper around tsd_t that makes it possible to avoid implicit conversion
|
||||||
* between tsd_t and tsdn_t, where tsdn_t is "nullable" and has to be
|
* between tsd_t and tsdn_t, where tsdn_t is "nullable" and has to be
|
||||||
@ -191,10 +200,10 @@ MALLOC_TSD
|
|||||||
#define O(n, t, nt) \
|
#define O(n, t, nt) \
|
||||||
JEMALLOC_ALWAYS_INLINE t * \
|
JEMALLOC_ALWAYS_INLINE t * \
|
||||||
tsd_##n##p_get(tsd_t *tsd) { \
|
tsd_##n##p_get(tsd_t *tsd) { \
|
||||||
assert(tsd->state == tsd_state_nominal || \
|
assert(tsd_state_get(tsd) == tsd_state_nominal || \
|
||||||
tsd->state == tsd_state_nominal_slow || \
|
tsd_state_get(tsd) == tsd_state_nominal_slow || \
|
||||||
tsd->state == tsd_state_reincarnated || \
|
tsd_state_get(tsd) == tsd_state_reincarnated || \
|
||||||
tsd->state == tsd_state_minimal_initialized); \
|
tsd_state_get(tsd) == tsd_state_minimal_initialized); \
|
||||||
return tsd_##n##p_get_unsafe(tsd); \
|
return tsd_##n##p_get_unsafe(tsd); \
|
||||||
}
|
}
|
||||||
MALLOC_TSD
|
MALLOC_TSD
|
||||||
@ -229,8 +238,8 @@ MALLOC_TSD
|
|||||||
#define O(n, t, nt) \
|
#define O(n, t, nt) \
|
||||||
JEMALLOC_ALWAYS_INLINE void \
|
JEMALLOC_ALWAYS_INLINE void \
|
||||||
tsd_##n##_set(tsd_t *tsd, t val) { \
|
tsd_##n##_set(tsd_t *tsd, t val) { \
|
||||||
assert(tsd->state != tsd_state_reincarnated && \
|
assert(tsd_state_get(tsd) != tsd_state_reincarnated && \
|
||||||
tsd->state != tsd_state_minimal_initialized); \
|
tsd_state_get(tsd) != tsd_state_minimal_initialized); \
|
||||||
*tsd_##n##p_get(tsd) = val; \
|
*tsd_##n##p_get(tsd) = val; \
|
||||||
}
|
}
|
||||||
MALLOC_TSD
|
MALLOC_TSD
|
||||||
@ -244,7 +253,7 @@ tsd_assert_fast(tsd_t *tsd) {
|
|||||||
|
|
||||||
JEMALLOC_ALWAYS_INLINE bool
|
JEMALLOC_ALWAYS_INLINE bool
|
||||||
tsd_fast(tsd_t *tsd) {
|
tsd_fast(tsd_t *tsd) {
|
||||||
bool fast = (tsd->state == tsd_state_nominal);
|
bool fast = (tsd_state_get(tsd) == tsd_state_nominal);
|
||||||
if (fast) {
|
if (fast) {
|
||||||
tsd_assert_fast(tsd);
|
tsd_assert_fast(tsd);
|
||||||
}
|
}
|
||||||
@ -261,7 +270,7 @@ tsd_fetch_impl(bool init, bool minimal) {
|
|||||||
}
|
}
|
||||||
assert(tsd != NULL);
|
assert(tsd != NULL);
|
||||||
|
|
||||||
if (unlikely(tsd->state != tsd_state_nominal)) {
|
if (unlikely(tsd_state_get(tsd) != tsd_state_nominal)) {
|
||||||
return tsd_fetch_slow(tsd, minimal);
|
return tsd_fetch_slow(tsd, minimal);
|
||||||
}
|
}
|
||||||
assert(tsd_fast(tsd));
|
assert(tsd_fast(tsd));
|
||||||
@ -281,7 +290,7 @@ JEMALLOC_ALWAYS_INLINE tsd_t *
|
|||||||
tsd_internal_fetch(void) {
|
tsd_internal_fetch(void) {
|
||||||
tsd_t *tsd = tsd_fetch_min();
|
tsd_t *tsd = tsd_fetch_min();
|
||||||
/* Use reincarnated state to prevent full initialization. */
|
/* Use reincarnated state to prevent full initialization. */
|
||||||
tsd->state = tsd_state_reincarnated;
|
tsd_state_set(tsd, tsd_state_reincarnated);
|
||||||
|
|
||||||
return tsd;
|
return tsd;
|
||||||
}
|
}
|
||||||
@ -293,7 +302,7 @@ tsd_fetch(void) {
|
|||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
tsd_nominal(tsd_t *tsd) {
|
tsd_nominal(tsd_t *tsd) {
|
||||||
return (tsd->state <= tsd_state_nominal_max);
|
return (tsd_state_get(tsd) <= tsd_state_nominal_max);
|
||||||
}
|
}
|
||||||
|
|
||||||
JEMALLOC_ALWAYS_INLINE tsdn_t *
|
JEMALLOC_ALWAYS_INLINE tsdn_t *
|
||||||
|
30
src/tsd.c
30
src/tsd.c
@ -56,9 +56,9 @@ tsd_slow_update(tsd_t *tsd) {
|
|||||||
if (tsd_nominal(tsd)) {
|
if (tsd_nominal(tsd)) {
|
||||||
if (malloc_slow || !tsd_tcache_enabled_get(tsd) ||
|
if (malloc_slow || !tsd_tcache_enabled_get(tsd) ||
|
||||||
tsd_reentrancy_level_get(tsd) > 0) {
|
tsd_reentrancy_level_get(tsd) > 0) {
|
||||||
tsd->state = tsd_state_nominal_slow;
|
tsd_state_set(tsd, tsd_state_nominal_slow);
|
||||||
} else {
|
} else {
|
||||||
tsd->state = tsd_state_nominal;
|
tsd_state_set(tsd, tsd_state_nominal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,8 +97,8 @@ assert_tsd_data_cleanup_done(tsd_t *tsd) {
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
tsd_data_init_nocleanup(tsd_t *tsd) {
|
tsd_data_init_nocleanup(tsd_t *tsd) {
|
||||||
assert(tsd->state == tsd_state_reincarnated ||
|
assert(tsd_state_get(tsd) == tsd_state_reincarnated ||
|
||||||
tsd->state == tsd_state_minimal_initialized);
|
tsd_state_get(tsd) == tsd_state_minimal_initialized);
|
||||||
/*
|
/*
|
||||||
* During reincarnation, there is no guarantee that the cleanup function
|
* During reincarnation, there is no guarantee that the cleanup function
|
||||||
* will be called (deallocation may happen after all tsd destructors).
|
* will be called (deallocation may happen after all tsd destructors).
|
||||||
@ -117,27 +117,27 @@ tsd_t *
|
|||||||
tsd_fetch_slow(tsd_t *tsd, bool minimal) {
|
tsd_fetch_slow(tsd_t *tsd, bool minimal) {
|
||||||
assert(!tsd_fast(tsd));
|
assert(!tsd_fast(tsd));
|
||||||
|
|
||||||
if (tsd->state == tsd_state_nominal_slow) {
|
if (tsd_state_get(tsd) == tsd_state_nominal_slow) {
|
||||||
/* On slow path but no work needed. */
|
/* On slow path but no work needed. */
|
||||||
assert(malloc_slow || !tsd_tcache_enabled_get(tsd) ||
|
assert(malloc_slow || !tsd_tcache_enabled_get(tsd) ||
|
||||||
tsd_reentrancy_level_get(tsd) > 0 ||
|
tsd_reentrancy_level_get(tsd) > 0 ||
|
||||||
*tsd_arenas_tdata_bypassp_get(tsd));
|
*tsd_arenas_tdata_bypassp_get(tsd));
|
||||||
} else if (tsd->state == tsd_state_uninitialized) {
|
} else if (tsd_state_get(tsd) == tsd_state_uninitialized) {
|
||||||
if (!minimal) {
|
if (!minimal) {
|
||||||
tsd->state = tsd_state_nominal;
|
tsd_state_set(tsd, tsd_state_nominal);
|
||||||
tsd_slow_update(tsd);
|
tsd_slow_update(tsd);
|
||||||
/* Trigger cleanup handler registration. */
|
/* Trigger cleanup handler registration. */
|
||||||
tsd_set(tsd);
|
tsd_set(tsd);
|
||||||
tsd_data_init(tsd);
|
tsd_data_init(tsd);
|
||||||
} else {
|
} else {
|
||||||
tsd->state = tsd_state_minimal_initialized;
|
tsd_state_set(tsd, tsd_state_minimal_initialized);
|
||||||
tsd_set(tsd);
|
tsd_set(tsd);
|
||||||
tsd_data_init_nocleanup(tsd);
|
tsd_data_init_nocleanup(tsd);
|
||||||
}
|
}
|
||||||
} else if (tsd->state == tsd_state_minimal_initialized) {
|
} else if (tsd_state_get(tsd) == tsd_state_minimal_initialized) {
|
||||||
if (!minimal) {
|
if (!minimal) {
|
||||||
/* Switch to fully initialized. */
|
/* Switch to fully initialized. */
|
||||||
tsd->state = tsd_state_nominal;
|
tsd_state_set(tsd, tsd_state_nominal);
|
||||||
assert(*tsd_reentrancy_levelp_get(tsd) >= 1);
|
assert(*tsd_reentrancy_levelp_get(tsd) >= 1);
|
||||||
(*tsd_reentrancy_levelp_get(tsd))--;
|
(*tsd_reentrancy_levelp_get(tsd))--;
|
||||||
tsd_slow_update(tsd);
|
tsd_slow_update(tsd);
|
||||||
@ -145,12 +145,12 @@ tsd_fetch_slow(tsd_t *tsd, bool minimal) {
|
|||||||
} else {
|
} else {
|
||||||
assert_tsd_data_cleanup_done(tsd);
|
assert_tsd_data_cleanup_done(tsd);
|
||||||
}
|
}
|
||||||
} else if (tsd->state == tsd_state_purgatory) {
|
} else if (tsd_state_get(tsd) == tsd_state_purgatory) {
|
||||||
tsd->state = tsd_state_reincarnated;
|
tsd_state_set(tsd, tsd_state_reincarnated);
|
||||||
tsd_set(tsd);
|
tsd_set(tsd);
|
||||||
tsd_data_init_nocleanup(tsd);
|
tsd_data_init_nocleanup(tsd);
|
||||||
} else {
|
} else {
|
||||||
assert(tsd->state == tsd_state_reincarnated);
|
assert(tsd_state_get(tsd) == tsd_state_reincarnated);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tsd;
|
return tsd;
|
||||||
@ -214,7 +214,7 @@ void
|
|||||||
tsd_cleanup(void *arg) {
|
tsd_cleanup(void *arg) {
|
||||||
tsd_t *tsd = (tsd_t *)arg;
|
tsd_t *tsd = (tsd_t *)arg;
|
||||||
|
|
||||||
switch (tsd->state) {
|
switch (tsd_state_get(tsd)) {
|
||||||
case tsd_state_uninitialized:
|
case tsd_state_uninitialized:
|
||||||
/* Do nothing. */
|
/* Do nothing. */
|
||||||
break;
|
break;
|
||||||
@ -232,7 +232,7 @@ tsd_cleanup(void *arg) {
|
|||||||
case tsd_state_nominal:
|
case tsd_state_nominal:
|
||||||
case tsd_state_nominal_slow:
|
case tsd_state_nominal_slow:
|
||||||
tsd_do_data_cleanup(tsd);
|
tsd_do_data_cleanup(tsd);
|
||||||
tsd->state = tsd_state_purgatory;
|
tsd_state_set(tsd, tsd_state_purgatory);
|
||||||
tsd_set(tsd);
|
tsd_set(tsd);
|
||||||
break;
|
break;
|
||||||
case tsd_state_purgatory:
|
case tsd_state_purgatory:
|
||||||
|
@ -98,11 +98,11 @@ thd_start_reincarnated(void *arg) {
|
|||||||
tsd_cleanup((void *)tsd);
|
tsd_cleanup((void *)tsd);
|
||||||
assert_ptr_null(*tsd_arenap_get_unsafe(tsd),
|
assert_ptr_null(*tsd_arenap_get_unsafe(tsd),
|
||||||
"TSD arena should have been cleared.");
|
"TSD arena should have been cleared.");
|
||||||
assert_u_eq(tsd->state, tsd_state_purgatory,
|
assert_u_eq(tsd_state_get(tsd), tsd_state_purgatory,
|
||||||
"TSD state should be purgatory\n");
|
"TSD state should be purgatory\n");
|
||||||
|
|
||||||
free(p);
|
free(p);
|
||||||
assert_u_eq(tsd->state, tsd_state_reincarnated,
|
assert_u_eq(tsd_state_get(tsd), tsd_state_reincarnated,
|
||||||
"TSD state should be reincarnated\n");
|
"TSD state should be reincarnated\n");
|
||||||
p = mallocx(1, MALLOCX_TCACHE_NONE);
|
p = mallocx(1, MALLOCX_TCACHE_NONE);
|
||||||
assert_ptr_not_null(p, "Unexpected malloc() failure");
|
assert_ptr_not_null(p, "Unexpected malloc() failure");
|
||||||
|
Loading…
Reference in New Issue
Block a user