#include "akerror.h" #include "err_capture.h" #include /* * akerr_release_error() drops one reference and only recycles the slot when the * last one goes away. Two of its refcount edges had no test: * * - refcount > 1: the release must decrement and return the context intact, * not wipe it out from under the reference that is still held. * - refcount == 0: releasing a context nobody holds must not underflow the * count; it wipes and returns NULL like any other fully-released slot. * * The macro API never produces a refcount above 1 (akerr_next_error() takes the * one reference a fresh slot gets), so this test sets the count directly to * model a caller that took an extra reference, and clears it to model a slot * nobody holds. */ int main(void) { akerr_capture_install(); akerr_init(); AKERR_CHECK(akerr_slots_in_use() == 0); /* Check out a slot and give it two holders. */ akerr_ErrorContext *held = akerr_next_error(); AKERR_CHECK(held != NULL); int slotid = held->arrayid; held->refcount = 2; held->status = AKERR_VALUE; snprintf((char *)held->message, AKERR_MAX_ERROR_CONTEXT_STRING_LENGTH, "still referenced"); /* First release: one holder left, so the context survives untouched. */ akerr_ErrorContext *ret = akerr_release_error(held); AKERR_CHECK(ret == held); AKERR_CHECK(held->refcount == 1); AKERR_CHECK(held->status == AKERR_VALUE); AKERR_CHECK(strcmp(held->message, "still referenced") == 0); AKERR_CHECK(akerr_slots_in_use() == 1); /* Second release: last holder gone, so the slot is wiped and recycled. */ ret = akerr_release_error(held); AKERR_CHECK(ret == NULL); AKERR_CHECK(held->refcount == 0); AKERR_CHECK(held->status == 0); AKERR_CHECK(held->message[0] == '\0'); /* The wipe must preserve the slot's identity and stacktrace cursor. */ AKERR_CHECK(held->arrayid == slotid); AKERR_CHECK(held->stacktracebufptr == (char *)&held->stacktracebuf); AKERR_CHECK(akerr_slots_in_use() == 0); /* * Releasing an unheld slot: refcount is already 0, so there is nothing to * decrement and the count must not go negative. */ akerr_ErrorContext *unheld = akerr_next_error(); AKERR_CHECK(unheld != NULL); AKERR_CHECK(unheld->refcount == 1); unheld->refcount = 0; ret = akerr_release_error(unheld); AKERR_CHECK(ret == NULL); AKERR_CHECK(unheld->refcount == 0); AKERR_CHECK(akerr_slots_in_use() == 0); /* The pool is still healthy afterwards. */ akerr_ErrorContext *probe = akerr_next_error(); AKERR_CHECK(probe != NULL); fprintf(stderr, "err_release_refcount ok\n"); return 0; }