diff --git a/lib/u_async.c b/lib/u_async.c index def04034..520a3a3e 100644 --- a/lib/u_async.c +++ b/lib/u_async.c @@ -1853,10 +1853,15 @@ void uasync_post(struct UASYNC* ua, uasync_post_callback_t callback, void* arg) if (!ua || !callback) return; struct posted_task* task = u_malloc(sizeof(struct posted_task)); - if (!task) return; + if (!task) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "uasync_post: task allocation failed"); return; } task->callback = callback; task->arg = arg; + uasync_post_reserved(ua, task); +} + +/* Worker резервирует completion до запуска, поэтому завершение не требует памяти. */ +void uasync_post_reserved(struct UASYNC* ua, struct posted_task* task) { task->next = NULL; #ifdef _WIN32 @@ -1883,6 +1888,31 @@ void uasync_post(struct UASYNC* ua, uasync_post_callback_t callback, void* arg) uasync_wakeup(ua); // будим mainloop } +/* Владелец отменяет pending completion после join производителя вне callbacks. */ +err_t uasync_cancel_post(struct UASYNC* ua, struct posted_task* task) { +#ifdef _WIN32 + EnterCriticalSection(&ua->posted_lock); +#else + pthread_mutex_lock(&ua->posted_lock); +#endif + struct posted_task** p = &ua->posted_tasks_head; + struct posted_task* previous = NULL; + while (*p && *p != task) { previous = *p; p = &(*p)->next; } + int found = *p != NULL; + if (found) { + *p = task->next; + if (ua->posted_tasks_tail == task) ua->posted_tasks_tail = previous; + } +#ifdef _WIN32 + LeaveCriticalSection(&ua->posted_lock); +#else + pthread_mutex_unlock(&ua->posted_lock); +#endif + if (found) u_free(task); + else DEBUG_ERROR(DEBUG_CATEGORY_SYS, "uasync_cancel_post: completion is not pending"); + return found ? ERR_OK : ERR_FAIL; +} + // Wakeup mechanism int uasync_wakeup(struct UASYNC* ua) { if (!ua || !ua->wakeup_initialized) return -1; diff --git a/lib/u_async.h b/lib/u_async.h index f78ac493..145d5982 100644 --- a/lib/u_async.h +++ b/lib/u_async.h @@ -147,6 +147,13 @@ int uasync_get_wakeup_fd(struct UASYNC* ua); // returns write fd for wakeup pipe // сообщить async (из другого thread) чтобы он вызвал callback с аргументом void uasync_post(struct UASYNC* ua, uasync_post_callback_t callback, void* user_arg); +/* Передать заранее выделенный u_malloc task без аллокаций. uasync освобождает + * task после callback. Производитель больше не обращается к task после передачи. + * Отмена — в потоке владельца вне callbacks, после завершения производителя; + * ERR_OK означает, что task удалён из очереди и освобождён. */ +void uasync_post_reserved(struct UASYNC* ua, struct posted_task* task); +err_t uasync_cancel_post(struct UASYNC* ua, struct posted_task* task); + // синхронизация памяти (для доступности из других потоков) void uasync_memsync(struct UASYNC* ua);