Add quiescence sync before deleting base during arena_destroy.
This commit is contained in:
parent
a137a68252
commit
862219e461
88
src/arena.c
88
src/arena.c
@ -605,6 +605,90 @@ arena_reset(tsd_t *tsd, arena_t *arena) {
|
|||||||
pa_shard_reset(tsd_tsdn(tsd), &arena->pa_shard);
|
pa_shard_reset(tsd_tsdn(tsd), &arena->pa_shard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
arena_prepare_base_deletion_sync_finish(tsd_t *tsd, malloc_mutex_t **mutexes,
|
||||||
|
unsigned n_mtx) {
|
||||||
|
for (unsigned i = 0; i < n_mtx; i++) {
|
||||||
|
malloc_mutex_lock(tsd_tsdn(tsd), mutexes[i]);
|
||||||
|
malloc_mutex_unlock(tsd_tsdn(tsd), mutexes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARENA_DESTROY_MAX_DELAYED_MTX 32
|
||||||
|
static void
|
||||||
|
arena_prepare_base_deletion_sync(tsd_t *tsd, malloc_mutex_t *mtx,
|
||||||
|
malloc_mutex_t **delayed_mtx, unsigned *n_delayed) {
|
||||||
|
if (!malloc_mutex_trylock(tsd_tsdn(tsd), mtx)) {
|
||||||
|
/* No contention. */
|
||||||
|
malloc_mutex_unlock(tsd_tsdn(tsd), mtx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsigned n = *n_delayed;
|
||||||
|
assert(n < ARENA_DESTROY_MAX_DELAYED_MTX);
|
||||||
|
/* Add another to the batch. */
|
||||||
|
delayed_mtx[n++] = mtx;
|
||||||
|
|
||||||
|
if (n == ARENA_DESTROY_MAX_DELAYED_MTX) {
|
||||||
|
arena_prepare_base_deletion_sync_finish(tsd, delayed_mtx, n);
|
||||||
|
n = 0;
|
||||||
|
}
|
||||||
|
*n_delayed = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
arena_prepare_base_deletion(tsd_t *tsd, base_t *base_to_destroy) {
|
||||||
|
/*
|
||||||
|
* In order to coalesce, emap_try_acquire_edata_neighbor will attempt to
|
||||||
|
* check neighbor edata's state to determine eligibility. This means
|
||||||
|
* under certain conditions, the metadata from an arena can be accessed
|
||||||
|
* w/o holding any locks from that arena. In order to guarantee safe
|
||||||
|
* memory access, the metadata and the underlying base allocator needs
|
||||||
|
* to be kept alive, until all pending accesses are done.
|
||||||
|
*
|
||||||
|
* 1) with opt_retain, the arena boundary implies the is_head state
|
||||||
|
* (tracked in the rtree leaf), and the coalesce flow will stop at the
|
||||||
|
* head state branch. Therefore no cross arena metadata access
|
||||||
|
* possible.
|
||||||
|
*
|
||||||
|
* 2) w/o opt_retain, the arena id needs to be read from the edata_t,
|
||||||
|
* meaning read only cross-arena metadata access is possible. The
|
||||||
|
* coalesce attempt will stop at the arena_id mismatch, and is always
|
||||||
|
* under one of the ecache locks. To allow safe passthrough of such
|
||||||
|
* metadata accesses, the loop below will iterate through all manual
|
||||||
|
* arenas' ecache locks. As all the metadata from this base allocator
|
||||||
|
* have been unlinked from the rtree, after going through all the
|
||||||
|
* relevant ecache locks, it's safe to say that a) pending accesses are
|
||||||
|
* all finished, and b) no new access will be generated.
|
||||||
|
*/
|
||||||
|
if (opt_retain) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsigned destroy_ind = base_ind_get(base_to_destroy);
|
||||||
|
assert(destroy_ind >= manual_arena_base);
|
||||||
|
|
||||||
|
tsdn_t *tsdn = tsd_tsdn(tsd);
|
||||||
|
malloc_mutex_t *delayed_mtx[ARENA_DESTROY_MAX_DELAYED_MTX];
|
||||||
|
unsigned n_delayed = 0, total = narenas_total_get();
|
||||||
|
for (unsigned i = 0; i < total; i++) {
|
||||||
|
if (i == destroy_ind) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
arena_t *arena = arena_get(tsdn, i, false);
|
||||||
|
if (arena == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pac_t *pac = &arena->pa_shard.pac;
|
||||||
|
arena_prepare_base_deletion_sync(tsd, &pac->ecache_dirty.mtx,
|
||||||
|
delayed_mtx, &n_delayed);
|
||||||
|
arena_prepare_base_deletion_sync(tsd, &pac->ecache_muzzy.mtx,
|
||||||
|
delayed_mtx, &n_delayed);
|
||||||
|
arena_prepare_base_deletion_sync(tsd, &pac->ecache_retained.mtx,
|
||||||
|
delayed_mtx, &n_delayed);
|
||||||
|
}
|
||||||
|
arena_prepare_base_deletion_sync_finish(tsd, delayed_mtx, n_delayed);
|
||||||
|
}
|
||||||
|
#undef ARENA_DESTROY_MAX_DELAYED_MTX
|
||||||
|
|
||||||
void
|
void
|
||||||
arena_destroy(tsd_t *tsd, arena_t *arena) {
|
arena_destroy(tsd_t *tsd, arena_t *arena) {
|
||||||
assert(base_ind_get(arena->base) >= narenas_auto);
|
assert(base_ind_get(arena->base) >= narenas_auto);
|
||||||
@ -633,8 +717,10 @@ arena_destroy(tsd_t *tsd, arena_t *arena) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Destroy the base allocator, which manages all metadata ever mapped by
|
* Destroy the base allocator, which manages all metadata ever mapped by
|
||||||
* this arena.
|
* this arena. The prepare function will make sure no pending access to
|
||||||
|
* the metadata in this base anymore.
|
||||||
*/
|
*/
|
||||||
|
arena_prepare_base_deletion(tsd, arena->base);
|
||||||
base_delete(tsd_tsdn(tsd), arena->base);
|
base_delete(tsd_tsdn(tsd), arena->base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user