diff --git a/tools/chatgui-android/libutun_lite/instance_lite.c b/tools/chatgui-android/libutun_lite/instance_lite.c index 44ef31c6..1894edde 100644 --- a/tools/chatgui-android/libutun_lite/instance_lite.c +++ b/tools/chatgui-android/libutun_lite/instance_lite.c @@ -377,9 +377,10 @@ static void* instance_thread(void* arg) { uasync_mark_running(g_ua); chat_event_post(g_inst, CHAT_EVT_SERVICE_STARTED, NULL, 0); - while (!__atomic_load_n(&g_stop, __ATOMIC_ACQUIRE)) { + while (!__atomic_load_n(&g_stop, __ATOMIC_ACQUIRE) && g_inst->running) { uasync_poll(g_ua, -1); } + if (!g_inst->running) IL_LOGE("poll exit: core requested stop; cleanup runs outside callbacks"); while (__atomic_load_n(&g_do_restart, __ATOMIC_ACQUIRE)) { IL_LOGI("poll exit: restart requested, destroying old instance"); @@ -442,7 +443,8 @@ static void* instance_thread(void* arg) { chat_event_post(g_inst, CHAT_EVT_SERVICE_STARTED, NULL, 0); IL_LOGI("poll exit: restart done, entering new poll loop"); - while (!__atomic_load_n(&g_stop, __ATOMIC_ACQUIRE)) uasync_poll(g_ua, -1); + while (!__atomic_load_n(&g_stop, __ATOMIC_ACQUIRE) && g_inst->running) uasync_poll(g_ua, -1); + if (!g_inst->running) IL_LOGE("poll exit: restarted core requested stop; cleanup runs outside callbacks"); } IL_LOGI("poll exit: final cleanup"); diff --git a/tools/chatgui/CMakeLists.txt b/tools/chatgui/CMakeLists.txt index 67f11514..9d6e6339 100644 --- a/tools/chatgui/CMakeLists.txt +++ b/tools/chatgui/CMakeLists.txt @@ -712,7 +712,7 @@ if(UNIX AND NOT APPLE) "-Wl,--wrap=call_audio_init" "-Wl,--wrap=call_audio_destroy" "-Wl,--wrap=utun_instance_destroy" "-Wl,--wrap=uasync_destroy" "-Wl,--wrap=pthread_detach" "-Wl,--wrap=instance_lite_get_instance" "-Wl,--wrap=utun_bridge_get_local_sockets_json") - foreach(scenario normal restart stream_failure voice_failure) + foreach(scenario normal restart stream_failure voice_failure core_stop restart_core_stop) add_test(NAME test_android_audio_lifecycle_${scenario} COMMAND test_android_audio_lifecycle ${scenario}) set_tests_properties(test_android_audio_lifecycle_${scenario} PROPERTIES TIMEOUT 20) endforeach() diff --git a/tools/chatgui/tests/test_android_audio_lifecycle.c b/tools/chatgui/tests/test_android_audio_lifecycle.c index fe5ab5d9..adcdc321 100644 --- a/tools/chatgui/tests/test_android_audio_lifecycle.c +++ b/tools/chatgui/tests/test_android_audio_lifecycle.c @@ -74,7 +74,7 @@ void __real_uasync_destroy(struct UASYNC*, int); void __wrap_uasync_destroy(struct UASYNC* ua, int flags) { observe(destroyed && detached >= destroyed, "loop destroyed before core detach/destroy"); __real_uasync_destroy(ua, flags); - pthread_mutex_lock(&observation_lock); ++loops_destroyed; pthread_mutex_unlock(&observation_lock); + pthread_mutex_lock(&observation_lock); ++loops_destroyed; pthread_cond_broadcast(&changed); pthread_mutex_unlock(&observation_lock); } int __real_pthread_detach(pthread_t); @@ -119,11 +119,19 @@ static void inspect(void* arg) { pthread_mutex_lock(&observation_lock); ++checked; pthread_cond_broadcast(&changed); pthread_mutex_unlock(&observation_lock); } +static void stop_core(void* arg) { + (void)arg; + struct UTUN_INSTANCE* inst = instance_lite_get_instance(); + observe(inst && inst->running && inst->call_audio_ops, "core stop without live voice owner"); + utun_instance_stop(inst); +} + int main(int argc, char** argv) { CHECK_ANDROID(argc == 2); - int restart = !strcmp(argv[1], "restart"), normal = !strcmp(argv[1], "normal"); + int core_stop = !strcmp(argv[1], "core_stop") || !strcmp(argv[1], "restart_core_stop"); + int restart = !strcmp(argv[1], "restart") || !strcmp(argv[1], "restart_core_stop"), normal = !strcmp(argv[1], "normal"); stream_failure = !strcmp(argv[1], "stream_failure"); voice_failure = !strcmp(argv[1], "voice_failure"); - CHECK_ANDROID(restart || normal || stream_failure || voice_failure); + CHECK_ANDROID(restart || normal || core_stop || stream_failure || voice_failure); ui = pthread_self(); worker = ui; debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_category_level(DEBUG_CATEGORY_CALL, DEBUG_LEVEL_INFO); utun_instance_set_tun_init_enabled(0); @@ -157,6 +165,10 @@ int main(int argc, char** argv) { CHECK_ANDROID(wait_for(&checked, 2)); utun_bridge_call_audio_stop(); } + if (core_stop) { + uasync_post(instance_lite_get_uasync(), stop_core, NULL); + CHECK_ANDROID(wait_for(&loops_destroyed, 1u + restart)); + } instance_lite_stop(); CHECK_ANDROID(attached == 1u + restart && detached == attached && destroyed == attached && loops_destroyed == attached); }