Test queued cancellation against running work and reserved cleanup

This commit is contained in:
evgeny committed 2026-11-03 20:09:45 +01:00
1 parent 1a7f877f32
commit ae26d626c8
1 file changed
+58 -1
+58 -1
View File
@@ -519,6 +519,63 @@ static void worker_completion_test(void) {
}
#endif
struct cancel_case {
pthread_mutex_t mutex;
pthread_cond_t condition;
int started, released, worked, done, cancelled;
};
static void cancel_block(void* arg) {
struct cancel_case* t = arg;
pthread_mutex_lock(&t->mutex); t->started = 1; pthread_cond_signal(&t->condition);
while (!t->released) pthread_cond_wait(&t->condition, &t->mutex);
pthread_mutex_unlock(&t->mutex); t->worked++;
}
static void cancel_work(void* arg) { ((struct cancel_case*)arg)->worked++; }
static void cancel_done(void* arg, int error) {
struct cancel_case* t = arg;
EXPECT(!error || error == MF_WORK_CANCELLED, "cancel completion status");
t->done++; t->cancelled += error == MF_WORK_CANCELLED;
}
/* Диск стоит на барьере: отмена головы/хвоста не выполняет work и сохраняет обязательную cleanup. */
static void worker_cancel_test(void) {
size_t before = u_get_allocated_count();
struct cancel_case running = { .mutex = PTHREAD_MUTEX_INITIALIZER, .condition = PTHREAD_COND_INITIALIZER };
struct cancel_case head = {0}, tail = {0};
struct UASYNC* ua = uasync_create();
struct mf_worker* worker = ua ? mf_worker_create(ua) : NULL;
struct mf_work* cleanup = mf_worker_reserve();
EXPECT(worker && cleanup, "cancel worker fixture");
if (worker && cleanup) {
mf_worker_submit(worker, cancel_block, &running, cancel_done, &running);
pthread_mutex_lock(&running.mutex);
while (!running.started) pthread_cond_wait(&running.condition, &running.mutex);
pthread_mutex_unlock(&running.mutex);
mf_worker_submit(worker, cancel_work, &head, cancel_done, &head);
mf_worker_submit_reserved(worker, cleanup, cancel_work, &head, cancel_done, &head); cleanup = NULL;
mf_worker_submit(worker, cancel_work, &tail, cancel_done, &tail);
EXPECT(mf_worker_cancel(worker, &running) == 0, "running work cannot be cancelled");
EXPECT(mf_worker_cancel(worker, &head) == 1, "cancel head preserves reserved cleanup with same data");
EXPECT(mf_worker_cancel(worker, &tail) == 1, "cancel tail");
EXPECT(mf_worker_cancel(worker, &head) == 0, "cancel is idempotent");
uasync_poll(ua, 0);
EXPECT(head.done == 1 && head.cancelled == 1 && tail.done == 1 && tail.cancelled == 1,
"cancel completion delivered while disk remains blocked");
EXPECT(!head.worked && !tail.worked && !running.done, "cancelled work has no side effects");
pthread_mutex_lock(&running.mutex); running.released = 1; pthread_cond_signal(&running.condition);
pthread_mutex_unlock(&running.mutex);
}
mf_worker_destroy(worker); mf_worker_unreserve(cleanup);
if (ua) { uasync_poll(ua, 0); uasync_destroy(ua, 1); }
EXPECT(running.worked == 1 && running.done == 1 && !running.cancelled, "running command finishes once");
EXPECT(head.worked == 1 && head.done == 2 && head.cancelled == 1, "reserved cleanup finishes once");
EXPECT(!tail.worked && tail.done == 1 && tail.cancelled == 1, "cancelled tail never executes");
pthread_cond_destroy(&running.condition); pthread_mutex_destroy(&running.mutex);
EXPECT(u_get_allocated_count() == before, "cancelled jobs/completions freed");
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "worker cancellation: queued jobs skipped, running command and reserved cleanup completed");
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_MEDIA, DEBUG_LEVEL_DEBUG);
char directory[1024] = "/tmp/utun_member_files_XXXXXX";
@@ -528,7 +585,7 @@ int main(void) {
#ifndef _WIN32
local_fifo_test(directory); large_offset_test(directory);
#endif
worker_test();
worker_test(); worker_cancel_test();
#ifdef __linux__
worker_completion_test();
#endif