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:
David Goldblatt 2018-03-08 16:34:17 -08:00 committed by David Goldblatt
parent e74a1a37c8
commit 982c10de35
5 changed files with 43 additions and 32 deletions

View File

@ -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

View File

@ -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);
} }
} }

View File

@ -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 *

View File

@ -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:

View File

@ -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");