From da540627fcfda15a83929f01cb0227108403e640 Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 29 Oct 2026 12:14:24 +0100 Subject: [PATCH] Keep accepted commands bound to their instance while restart readiness closes --- tests/test_android_bridge.c | 4 +++- tools/chatgui-android/doc/ARCHITECTURE.md | 3 +++ tools/chatgui-android/libutun_lite/instance_lite.c | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test_android_bridge.c b/tests/test_android_bridge.c index 47bd68d7..5b753fb0 100644 --- a/tests/test_android_bridge.c +++ b/tests/test_android_bridge.c @@ -307,13 +307,15 @@ static int check_worker_requests(const char* directory) { pthread_mutex_lock(&worker_test_mutex); while (!worker_entered) pthread_cond_wait(&worker_test_cond, &worker_test_mutex); pthread_mutex_unlock(&worker_test_mutex); + int retained_payload = 0; + REQUIRE(!instance_lite_post(worker_payload_run, &retained_payload, NULL, worker_payload_discard)); REQUIRE(!instance_lite_restart("[global]\ndb_path=/nonexistent/utun-restart-identity/missing\n")); REQUIRE(!instance_lite_is_running()); pthread_mutex_lock(&worker_test_mutex); worker_release = 1; pthread_cond_broadcast(&worker_test_cond); pthread_mutex_unlock(&worker_test_mutex); for (int i = 0; i < 1000 && !instance_lite_is_running(); i++) usleep(10000); - REQUIRE(instance_lite_is_running()); + REQUIRE(instance_lite_is_running() && __atomic_load_n(&retained_payload, __ATOMIC_ACQUIRE) == 1); struct worker_query retained = {0}; REQUIRE(!instance_lite_call(worker_read, &retained) && retained.node == query.node); pthread_mutex_lock(&worker_test_mutex); worker_entered = 0; worker_release = 0; diff --git a/tools/chatgui-android/doc/ARCHITECTURE.md b/tools/chatgui-android/doc/ARCHITECTURE.md index 72767605..5c76bc34 100644 --- a/tools/chatgui-android/doc/ARCHITECTURE.md +++ b/tools/chatgui-android/doc/ARCHITECTURE.md @@ -93,6 +93,9 @@ Worker снимает media-привязки, уничтожает instance, с каждые две секунды ожидания пишет предупреждение, поток не отсоединяет. Повторный stop безопасен. Restart пересоздаёт instance и uasync. Возврат restart/updateSockets подтверждает принятие копии INI, ошибка очереди возвращается Kotlin; готовность нового запуска подтверждается SERVICE_STARTED. +Принятие restart сразу закрывает ready для новых frontend/media запросов. Ошибка подготовки возвращает +ready прежнему instance. Ранее принятые команды могут завершиться в прежнем worker до его снятия; +после снятия instance они отменяются, даже если uasync обрабатывает оставшиеся callbacks при teardown. Fatal signal handlers принадлежат платформе: worker не заменяет их обработчиком с небезопасными locks/JNI/backtrace. Общие правила освобождения: `../../../doc/service_lifecycle.md`. diff --git a/tools/chatgui-android/libutun_lite/instance_lite.c b/tools/chatgui-android/libutun_lite/instance_lite.c index 396c8eda..5459f3e6 100644 --- a/tools/chatgui-android/libutun_lite/instance_lite.c +++ b/tools/chatgui-android/libutun_lite/instance_lite.c @@ -108,6 +108,7 @@ struct frontend_request { void (*callback)(void*); void (*discard)(void*); void* arg; + struct UTUN_INSTANCE* instance; /* Привязка к публикации; pending restart закрывает ready, но ещё не уничтожает instance. */ }; static struct frontend_request* g_frontend_requests; /* Список под g_stop_mutex. */ @@ -117,7 +118,8 @@ static void frontend_request_run(void* arg) { struct frontend_request** entry = &g_frontend_requests; while (*entry && *entry != request) entry = &(*entry)->next; if (*entry) *entry = request->next; - int cancelled = __atomic_load_n(&g_stop, __ATOMIC_ACQUIRE) || !__atomic_load_n(&g_running, __ATOMIC_ACQUIRE); + int cancelled = __atomic_load_n(&g_stop, __ATOMIC_ACQUIRE) || + __atomic_load_n(&g_inst, __ATOMIC_ACQUIRE) != request->instance; pthread_mutex_unlock(&g_stop_mutex); if (!cancelled) request->callback(request->arg); else if (request->discard) request->discard(request->arg); @@ -158,6 +160,7 @@ int instance_lite_post(void (*callback)(void*), void* arg, struct UTUN_INSTANCE* u_free(task); u_free(request); return -1; } if (instance_field) *instance_field = inst; + request->instance = inst; request->next = g_frontend_requests; g_frontend_requests = request; uasync_post_reserved(ua, task); pthread_mutex_unlock(&g_stop_mutex);