feat: xce_disable_hook() + _Atomic hook_fn_t + NULL-slot reuse (P1-2a)
- hook_item_t.fn changed to _Atomic hook_fn_t (C11 stdatomic) - xce_disable_hook(): atomically stores NULL into the slot fn field; returns 0 on success, -ENOENT if fn not found, -EINVAL on bad args - xce_add_hook(): scans for NULL (disabled) slots before allocating; uses atomic_store_explicit(release) when writing fn pointer - xce_run_hooks(): atomic_load_explicit(acquire) + NULL guard so disabled slots are silently skipped without disrupting the loop - Add src/lib/testsuite/test_hooks.c with 3 TDD tests: test_disable_hook_stops_invocation test_disable_hook_returns_enoent_if_not_found test_add_hook_reuses_null_slot - Register test_hooks in meson.build and test.c runner Required by ha_demote_to_standby() to atomically disable persistence hooks when the controller steps down from primary, preventing unbounded hook list growth across promote/demote cycles (resolution A6-015, A7-004, A7-016).
This commit is contained in:
parent
89c360f7b5
commit
f155b1ef97
5 changed files with 169 additions and 4 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#include <errno.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -41,19 +42,60 @@ int xce_add_hook(hooksys_t* s, int typ, hook_fn_t fn, void* data)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Scan for a disabled (NULL-fn) slot of the same type to reuse */
|
||||
{
|
||||
xlist_t* l = &s->hooks[typ];
|
||||
int i;
|
||||
for (i = 0; i < l->count; i++) {
|
||||
hook_item_t* slot = (hook_item_t*)l->data[i];
|
||||
if (!slot) continue;
|
||||
hook_fn_t cur =
|
||||
atomic_load_explicit(&slot->fn, memory_order_acquire);
|
||||
if (cur == NULL) {
|
||||
slot->data = data;
|
||||
atomic_store_explicit(&slot->fn, fn, memory_order_release);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* No reusable slot — allocate new */
|
||||
it = (hook_item_t*)calloc(1, sizeof(*it));
|
||||
if (it == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
it->fn = fn;
|
||||
it->data = data;
|
||||
atomic_store_explicit(&it->fn, fn, memory_order_release);
|
||||
|
||||
xlist_store(&(s->hooks[typ]), it);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xce_disable_hook(hooksys_t* s, int typ, hook_fn_t fn)
|
||||
{
|
||||
xlist_t* l;
|
||||
int i;
|
||||
|
||||
if (!s || !fn || typ < 0 || typ >= s->maxtypes) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
l = &s->hooks[typ];
|
||||
for (i = 0; i < l->count; i++) {
|
||||
hook_item_t* it = (hook_item_t*)l->data[i];
|
||||
if (!it) continue;
|
||||
hook_fn_t cur = atomic_load_explicit(&it->fn, memory_order_acquire);
|
||||
if (cur == fn) {
|
||||
atomic_store_explicit(&it->fn, (hook_fn_t)NULL,
|
||||
memory_order_release);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* HOOK_BEFORE_EVENT returns
|
||||
* negative value will cause
|
||||
* fail
|
||||
|
|
@ -76,14 +118,20 @@ int xce_run_hooks(hooksys_t* s, int type, int situation, int detail, ...)
|
|||
|
||||
for (i = 0; i < l->count; i++) {
|
||||
hook_item_t* it = l->data[i];
|
||||
hook_fn_t local_fn;
|
||||
va_list ap;
|
||||
|
||||
if (it == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
local_fn = atomic_load_explicit(&it->fn, memory_order_acquire);
|
||||
if (!local_fn) {
|
||||
continue; /* disabled slot */
|
||||
}
|
||||
|
||||
va_start(ap, detail);
|
||||
rc = (*(it->fn))(type, situation, detail, it->data, ap);
|
||||
rc = local_fn(type, situation, detail, it->data, ap);
|
||||
va_end(ap);
|
||||
|
||||
if ((situation == HOOK_BEFORE_EVENT) && (rc < 0)) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue