|
|
|
|
@ -281,6 +281,32 @@ static void test_waiter_cancel(void) {
|
|
|
|
|
PASS(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_waiter_cancel_deferred(void) { |
|
|
|
|
TEST("waiter cancel deferred call_soon (internal==NULL)"); |
|
|
|
|
struct UASYNC *ua = uasync_create(); |
|
|
|
|
struct ll_queue *q = queue_new(ua, 0, 0, 0, "wcd"); |
|
|
|
|
queue_set_threshold(q, 0, 0); |
|
|
|
|
queue_set_waiter_defer(q, 1); |
|
|
|
|
|
|
|
|
|
int called = 0; |
|
|
|
|
struct queue_waiter_handle h = {0}; |
|
|
|
|
int ret = queue_waiter_wait(q, &h, waiter_cb, &called); |
|
|
|
|
ASSERT(ret == 1, "immediate branch (empty queue)"); |
|
|
|
|
ASSERT(h.internal == NULL, "no waiter registered (deferred path)"); |
|
|
|
|
ASSERT(h.call_soon_id != NULL, "deferred call scheduled"); |
|
|
|
|
|
|
|
|
|
/* До фикса: queue_waiter_cancel выходил по !internal, call_soon_id оставался
|
|
|
|
|
* висеть и deferred-колбэк срабатывал позже (UAF после teardown). */ |
|
|
|
|
queue_waiter_cancel(q, &h); |
|
|
|
|
ASSERT(h.call_soon_id == NULL, "call_soon_id cleared by cancel"); |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 10; i++) uasync_poll(ua, 1); |
|
|
|
|
ASSERT_EQ(called, 0, "deferred callback cancelled (did not fire)"); |
|
|
|
|
|
|
|
|
|
queue_free(q); uasync_destroy(ua, 0); |
|
|
|
|
PASS(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_waiter_queue_free(void) { |
|
|
|
|
TEST("queue_free clears waiters"); |
|
|
|
|
struct UASYNC *ua = uasync_create(); |
|
|
|
|
@ -454,6 +480,7 @@ int main(void) {
|
|
|
|
|
test_waiter(); |
|
|
|
|
test_waiter_multiple(); |
|
|
|
|
test_waiter_cancel(); |
|
|
|
|
test_waiter_cancel_deferred(); |
|
|
|
|
test_waiter_queue_free(); |
|
|
|
|
test_waiter_threshold(); |
|
|
|
|
test_waiter_reuse(); |
|
|
|
|
|