diff --git a/include/jemalloc/internal/tsd_inlines.h b/include/jemalloc/internal/tsd_inlines.h index 7d57b7dd..7c3fba5f 100644 --- a/include/jemalloc/internal/tsd_inlines.h +++ b/include/jemalloc/internal/tsd_inlines.h @@ -74,7 +74,8 @@ tsd_##n##_get(tsd_t *tsd) { \ } \ JEMALLOC_ALWAYS_INLINE void \ tsd_##n##_set(tsd_t *tsd, t n) { \ - assert(tsd->state == tsd_state_nominal); \ + assert(tsd->state == tsd_state_nominal || \ + tsd->state == tsd_state_reincarnated); \ tsd->n = n; \ } #define MALLOC_TSD_getset_no(n, t) diff --git a/src/jemalloc.c b/src/jemalloc.c index ab047c24..7c8fe9c9 100644 --- a/src/jemalloc.c +++ b/src/jemalloc.c @@ -422,13 +422,7 @@ arena_init(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) { static void arena_bind(tsd_t *tsd, unsigned ind, bool internal) { - arena_t *arena; - - if (!tsd_nominal(tsd)) { - return; - } - - arena = arena_get(tsd_tsdn(tsd), ind, false); + arena_t *arena = arena_get(tsd_tsdn(tsd), ind, false); arena_nthreads_inc(arena, internal); if (internal) { @@ -455,6 +449,7 @@ arena_unbind(tsd_t *tsd, unsigned ind, bool internal) { arena = arena_get(tsd_tsdn(tsd), ind, false); arena_nthreads_dec(arena, internal); + if (internal) { tsd_iarena_set(tsd, NULL); } else { diff --git a/src/tsd.c b/src/tsd.c index 970d5baa..6b68c001 100644 --- a/src/tsd.c +++ b/src/tsd.c @@ -86,6 +86,12 @@ tsd_cleanup(void *arg) { /* Do nothing. */ break; case tsd_state_nominal: + case tsd_state_reincarnated: + /* + * Reincarnated means another destructor deallocated memory + * after this destructor was called. Reset state to + * tsd_state_purgatory and request another callback. + */ #define MALLOC_TSD_cleanup_yes(n, t) \ n##_cleanup(tsd); #define MALLOC_TSD_cleanup_no(n, t) @@ -106,15 +112,6 @@ MALLOC_TSD * nothing, and do not request another callback. */ break; - case tsd_state_reincarnated: - /* - * Another destructor deallocated memory after this destructor - * was called. Reset state to tsd_state_purgatory and request - * another callback. - */ - tsd->state = tsd_state_purgatory; - tsd_set(tsd); - break; default: not_reached(); } diff --git a/test/unit/tsd.c b/test/unit/tsd.c index ae47d23e..e033bb76 100644 --- a/test/unit/tsd.c +++ b/test/unit/tsd.c @@ -90,6 +90,44 @@ TEST_BEGIN(test_tsd_sub_thread) { } TEST_END +static void * +thd_start_reincarnated(void *arg) { + tsd_t *tsd = tsd_fetch(); + assert(tsd); + + void *p = malloc(1); + assert_ptr_not_null(p, "Unexpected malloc() failure"); + + /* Manually trigger reincarnation. */ + assert_ptr_not_null(tsd->arena, "Should have tsd arena set."); + tsd_cleanup((void *)tsd); + assert_ptr_null(tsd->arena, "TSD arena should have been cleared."); + assert_u_eq(tsd->state, tsd_state_purgatory, + "TSD state should be purgatory\n"); + + free(p); + assert_u_eq(tsd->state, tsd_state_reincarnated, + "TSD state should be reincarnated\n"); + p = mallocx(1, MALLOCX_TCACHE_NONE); + assert_ptr_not_null(p, "Unexpected malloc() failure"); + assert_ptr_not_null(tsd->arena, + "Should have tsd arena set after reincarnation."); + + free(p); + tsd_cleanup((void *)tsd); + assert_ptr_null(tsd->arena, + "TSD arena should have been cleared after 2nd cleanup."); + + return NULL; +} + +TEST_BEGIN(test_tsd_reincarnation) { + thd_t thd; + thd_create(&thd, thd_start_reincarnated, NULL); + thd_join(thd, NULL); +} +TEST_END + int main(void) { /* Core tsd bootstrapping must happen prior to data_tsd_boot(). */ @@ -101,5 +139,6 @@ main(void) { return test( test_tsd_main_thread, - test_tsd_sub_thread); + test_tsd_sub_thread, + test_tsd_reincarnation); }