diff options
author | Sybren A. Stüvel <sybren@blender.org> | 2021-10-25 13:07:37 +0300 |
---|---|---|
committer | Sybren A. Stüvel <sybren@blender.org> | 2021-10-25 13:08:08 +0300 |
commit | beea601e7253f1eafbf43ce3c0e59497788335bc (patch) | |
tree | 38b8a64cb4bf5ac73f29ad7f598e9388a3d60b80 | |
parent | 8e56f3e8a36d76ad1765f939c018a5ec3c01faab (diff) |
BKE Callbacks: more explicit initialisation check
Add static boolean to track whether the callbacks system has been
initialised. This makes it possible to make the `BKE_callback_remove()`
function more noisy in case of programming errors, and avoids accessing
`funcstore->alloc` when `funcstore` was potentially already freed.
Thanks @campbellbarton for pointing this out.
-rw-r--r-- | source/blender/blenkernel/intern/callbacks.c | 29 |
1 files changed, 24 insertions, 5 deletions
diff --git a/source/blender/blenkernel/intern/callbacks.c b/source/blender/blenkernel/intern/callbacks.c index dbc213907ac..72dd51a940d 100644 --- a/source/blender/blenkernel/intern/callbacks.c +++ b/source/blender/blenkernel/intern/callbacks.c @@ -30,11 +30,20 @@ static ListBase callback_slots[BKE_CB_EVT_TOT] = {{NULL}}; +static bool callbacks_initialized = false; + +#define ASSERT_CALLBACKS_INITIALIZED() \ + BLI_assert_msg(callbacks_initialized, \ + "Callbacks should be initialized with BKE_callback_global_init() before using " \ + "the callback system.") + void BKE_callback_exec(struct Main *bmain, struct PointerRNA **pointers, const int num_pointers, eCbEvent evt) { + ASSERT_CALLBACKS_INITIALIZED(); + /* Use mutable iteration so handlers are able to remove themselves. */ ListBase *lb = &callback_slots[evt]; LISTBASE_FOREACH_MUTABLE (bCallbackFuncStore *, funcstore, lb) { @@ -75,18 +84,26 @@ void BKE_callback_exec_id_depsgraph(struct Main *bmain, void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt) { + ASSERT_CALLBACKS_INITIALIZED(); ListBase *lb = &callback_slots[evt]; BLI_addtail(lb, funcstore); } void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt) { - ListBase *lb = &callback_slots[evt]; - - /* Be safe, as the callback may have already been removed by BKE_callback_global_finalize(), for + /* The callback may have already been removed by BKE_callback_global_finalize(), for * example when removing callbacks in response to a BKE_blender_atexit_register callback * function. `BKE_blender_atexit()` runs after `BKE_callback_global_finalize()`. */ - BLI_remlink_safe(lb, funcstore); + if (!callbacks_initialized) { + return; + } + + ListBase *lb = &callback_slots[evt]; + + /* Be noisy about potential programming errors. */ + BLI_assert_msg(BLI_findindex(lb, funcstore) != -1, "To-be-removed callback not found"); + + BLI_remlink(lb, funcstore); if (funcstore->alloc) { MEM_freeN(funcstore); @@ -95,7 +112,7 @@ void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt) void BKE_callback_global_init(void) { - /* do nothing */ + callbacks_initialized = true; } /* call on application exit */ @@ -111,4 +128,6 @@ void BKE_callback_global_finalize(void) BKE_callback_remove(funcstore, evt); } } + + callbacks_initialized = false; } |