72 lines
2.6 KiB
C
72 lines
2.6 KiB
C
|
|
#include "akerror.h"
|
||
|
|
#include "err_capture.h"
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
/*
|
||
|
|
* 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 (ENSURE_ERROR_READY only
|
||
|
|
* increments when it acquires a fresh slot), so this test sets the count
|
||
|
|
* directly to model a caller that took an extra reference.
|
||
|
|
*/
|
||
|
|
|
||
|
|
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 == 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;
|
||
|
|
}
|