52 lines
1.4 KiB
C
52 lines
1.4 KiB
C
|
|
#include "akerror.h"
|
||
|
|
#include "err_capture.h"
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Regression test for the refcount leak: ENSURE_ERROR_READY must increment
|
||
|
|
* refcount only when it *acquires* a fresh context, not on every FAIL/SUCCEED.
|
||
|
|
* A function that calls FAIL more than once on the same context and then
|
||
|
|
* propagates used to arrive at the caller with refcount 2; the caller released
|
||
|
|
* once, leaking the slot. After enough leaks the pool is exhausted and the
|
||
|
|
* library exit(1)s.
|
||
|
|
*/
|
||
|
|
|
||
|
|
akerr_ErrorContext *validate(void)
|
||
|
|
{
|
||
|
|
PREPARE_ERROR(e);
|
||
|
|
ATTEMPT {
|
||
|
|
FAIL(e, AKERR_VALUE, "condition 1 failed"); /* acquires the context */
|
||
|
|
FAIL(e, AKERR_KEY, "condition 2 failed"); /* must NOT re-acquire it */
|
||
|
|
} CLEANUP {
|
||
|
|
} PROCESS(e) {
|
||
|
|
} FINISH(e, true); /* unhandled -> propagate */
|
||
|
|
SUCCEED_RETURN(e);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* One raise -> catch -> handle cycle; returns NULL (context released). */
|
||
|
|
akerr_ErrorContext *one_cycle(void)
|
||
|
|
{
|
||
|
|
PREPARE_ERROR(e);
|
||
|
|
ATTEMPT {
|
||
|
|
CATCH(e, validate());
|
||
|
|
} CLEANUP {
|
||
|
|
} PROCESS(e) {
|
||
|
|
} HANDLE(e, AKERR_KEY) {
|
||
|
|
} HANDLE(e, AKERR_VALUE) {
|
||
|
|
} FINISH(e, false);
|
||
|
|
return e;
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(void)
|
||
|
|
{
|
||
|
|
akerr_init();
|
||
|
|
AKERR_CHECK(akerr_slots_in_use() == 0);
|
||
|
|
|
||
|
|
for ( int i = 0; i < 32; i++ ) {
|
||
|
|
(void)one_cycle();
|
||
|
|
}
|
||
|
|
|
||
|
|
AKERR_CHECK(akerr_slots_in_use() == 0);
|
||
|
|
fprintf(stderr, "err_refcount_double_fail ok\n");
|
||
|
|
return 0;
|
||
|
|
}
|