diff options
author | Rickard Green <[email protected]> | 2010-12-15 00:56:56 +0100 |
---|---|---|
committer | Rickard Green <[email protected]> | 2010-12-16 12:12:25 +0100 |
commit | 8b18824e2b13e60fb1a067f80dbb228172f6a3d2 (patch) | |
tree | 1a1a5bd6f105eeee03cf38a35d2b9a24c7f20ba8 /erts/emulator/beam/erl_db.c | |
parent | 766c7cabb1545418bf59e8dcfcc1a5fae8b01d40 (diff) | |
download | otp-8b18824e2b13e60fb1a067f80dbb228172f6a3d2.tar.gz otp-8b18824e2b13e60fb1a067f80dbb228172f6a3d2.tar.bz2 otp-8b18824e2b13e60fb1a067f80dbb228172f6a3d2.zip |
Safe deallocation of ETS-table structures
Ensure that all threads potentially accessing an ETS-table have dropped
all references to the table before deallocating it.
Diffstat (limited to 'erts/emulator/beam/erl_db.c')
-rw-r--r-- | erts/emulator/beam/erl_db.c | 36 |
1 files changed, 33 insertions, 3 deletions
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 0dfc0e721f..e662b17592 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -227,10 +227,10 @@ static ERTS_INLINE DbTable* db_ref(DbTable* tb, db_lock_kind_t kind) } return tb; } - -static ERTS_INLINE DbTable* db_unref(DbTable* tb, db_lock_kind_t kind) + +static void +free_dbtable(DbTable* tb) { - if (kind != LCK_READ && !erts_refc_dectest(&tb->common.ref, 0)) { #ifdef HARDDEBUG if (erts_smp_atomic_read(&tb->common.memory_size) != sizeof(DbTable)) { erts_fprintf(stderr, "ets: db_unref memory remain=%ld fix=%x\n", @@ -257,6 +257,36 @@ static ERTS_INLINE DbTable* db_unref(DbTable* tb, db_lock_kind_t kind) ASSERT(is_immed(tb->common.heir_data)); erts_db_free(ERTS_ALC_T_DB_TABLE, tb, (void *) tb, sizeof(DbTable)); ERTS_ETS_MISC_MEM_ADD(-sizeof(DbTable)); +} + +#ifdef ERTS_SMP +static void +chk_free_dbtable(void *vtb) +{ + DbTable * tb = (DbTable *) vtb; + ERTS_THR_MEMORY_BARRIER; + if (erts_refc_dectest(&tb->common.ref, 0) == 0) + free_dbtable(tb); +} +#endif + +static ERTS_INLINE DbTable* db_unref(DbTable* tb, db_lock_kind_t kind) +{ + if (kind != LCK_READ && erts_refc_dectest(&tb->common.ref, 0) == 0) { +#ifdef ERTS_SMP + int scheds = erts_get_max_no_executing_schedulers(); + if (scheds == 1) + free_dbtable(tb); + else { + int refs = scheds - 1; + ASSERT(scheds > 1); + ERTS_THR_MEMORY_BARRIER; + erts_refc_add(&tb->common.ref, refs, refs); + erts_smp_schedule_misc_aux_work(1, scheds, chk_free_dbtable, tb); + } +#else + free_dbtable(tb); +#endif return NULL; } return tb; |