Queue Android activity updates while holding the worker lifetime lock
This commit is contained in:
1 parent
1ed110d47c
commit
4bece684da
3 files changed
+56
No files matched your search
@@ -684,6 +684,38 @@ void instance_lite_restart(const char* new_config_text) {
|
||||
request_restart(new_config_text, 0);
|
||||
}
|
||||
|
||||
static void set_active_trampoline(void* arg) {
|
||||
struct UTUN_INSTANCE* inst = __atomic_load_n(&g_inst, __ATOMIC_ACQUIRE);
|
||||
if (!inst || __atomic_load_n(&g_stop, __ATOMIC_ACQUIRE)) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SYS, "Android activity command cancelled: worker is stopping"); return;
|
||||
}
|
||||
int active = (int)(intptr_t)arg;
|
||||
if (utun_set_client_activity(inst, active) < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "Android activity transition failed requested=%d", active); return;
|
||||
}
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SYS, "Android activity applied requested=%d effective=%u", active, (unsigned)inst->client_activity);
|
||||
}
|
||||
|
||||
int instance_lite_set_active(int active) {
|
||||
if (g_is_worker) { IL_LOGE("activity request refused: frontend thread required"); return -1; }
|
||||
pthread_mutex_lock(&g_stop_mutex);
|
||||
struct UASYNC* ua = __atomic_load_n(&g_ua, __ATOMIC_ACQUIRE);
|
||||
if (!ua || !__atomic_load_n(&g_running, __ATOMIC_ACQUIRE) || __atomic_load_n(&g_stop, __ATOMIC_ACQUIRE)) {
|
||||
pthread_mutex_unlock(&g_stop_mutex);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SYS, "Android activity request rejected: core is not ready"); return -1;
|
||||
}
|
||||
struct posted_task* task = u_malloc(sizeof(*task));
|
||||
if (!task) {
|
||||
pthread_mutex_unlock(&g_stop_mutex);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "Android activity request allocation failed"); return -1;
|
||||
}
|
||||
task->callback = set_active_trampoline;
|
||||
task->arg = (void*)(intptr_t)!!active;
|
||||
uasync_post_reserved(ua, task);
|
||||
pthread_mutex_unlock(&g_stop_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── Лёгкое обновление сокетов (auto_sockets=android) ── */
|
||||
|
||||
static void update_sockets_trampoline(void* arg) {
|
||||
|
||||
@@ -66,6 +66,11 @@ int instance_lite_generate_keys(struct utun_config* config);
|
||||
* Последний ещё не взятый worker запрос restart/regenerate заменяет предыдущий. */
|
||||
int instance_lite_regenerate_keys(const char* config_text);
|
||||
|
||||
/* Frontend: поставить ACTIVE (active!=0) / STANDBY (0) без заимствования сырого uasync.
|
||||
* 0 — задача зарезервирована и поставлена живому worker; -1 — ядро не готово/останавливается, ошибка памяти или неверный поток.
|
||||
* При stop ожидающая команда может отмениться. Результат применения/stream hold логируется в потоке ядра. */
|
||||
int instance_lite_set_active(int active);
|
||||
|
||||
/* Копирует INI, будит poll-loop и пересоздаёт instance и uasync внутри worker.
|
||||
* До остановки проверяет конфиг и сохраняет отсутствующие ключи; отказ оставляет прежнее ядро.
|
||||
* При инициализации ставит запрос в очередь; при завершившемся worker делает join/start.
|
||||
|
||||
@@ -22,6 +22,7 @@ static pthread_cond_t changed = PTHREAD_COND_INITIALIZER;
|
||||
static pthread_t ui, worker;
|
||||
static unsigned attached, detached, destroyed, loops_destroyed, started, errors, checked;
|
||||
static unsigned identity_failures;
|
||||
static unsigned activity_checked;
|
||||
static uint64_t first_node, current_node;
|
||||
static struct UTUN_INSTANCE* last_detached;
|
||||
static int stream_failure, voice_failure;
|
||||
@@ -180,6 +181,7 @@ static void inspect(void* arg) {
|
||||
(void)arg;
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
observe(inst && inst->call_audio_ops && inst->call_audio_ops->on_media, "ready core without audio provider");
|
||||
observe(instance_lite_set_active(1) == -1, "activity posting accepted from native callback");
|
||||
observe(!strcmp(cfg_get_string("lifecycle", "missing"), "owned"), "config provider unavailable before callback stop");
|
||||
observe(utun_bridge_destroy() == -1 && instance_lite_is_running(), "bridge destroyed resources from core callback");
|
||||
observe(!strcmp(cfg_get_string("lifecycle", "missing"), "owned"), "failed stop cleared config callbacks");
|
||||
@@ -195,6 +197,14 @@ static void inspect(void* arg) {
|
||||
pthread_mutex_lock(&observation_lock); ++checked; pthread_cond_broadcast(&changed); pthread_mutex_unlock(&observation_lock);
|
||||
}
|
||||
|
||||
static void inspect_activity(void* arg) {
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
int expected = (int)(intptr_t)arg;
|
||||
observe(inst && inst->client_activity_requested == expected && inst->client_activity == expected,
|
||||
"activity command did not run before subsequent worker callback");
|
||||
pthread_mutex_lock(&observation_lock); ++activity_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();
|
||||
@@ -255,6 +265,7 @@ int main(int argc, char** argv) {
|
||||
instance_lite_restart(config);
|
||||
}
|
||||
CHECK_ANDROID(wait_for(&paused, 1));
|
||||
CHECK_ANDROID(instance_lite_set_active(1) == -1);
|
||||
pthread_t stopper;
|
||||
CHECK_ANDROID(!pthread_create(&stopper, NULL, stop_worker, NULL));
|
||||
CHECK_ANDROID(wait_for(&stop_entered, 1));
|
||||
@@ -266,6 +277,7 @@ int main(int argc, char** argv) {
|
||||
unsigned stopped_early = stop_done;
|
||||
pthread_mutex_unlock(&observation_lock);
|
||||
CHECK_ANDROID(stop_requested && !stopped_early);
|
||||
CHECK_ANDROID(instance_lite_set_active(1) == -1);
|
||||
if (slow) {
|
||||
clock_gettime(CLOCK_REALTIME, &deadline); deadline.tv_sec += 2; deadline.tv_nsec += 200000000;
|
||||
if (deadline.tv_nsec >= 1000000000) { ++deadline.tv_sec; deadline.tv_nsec -= 1000000000; }
|
||||
@@ -281,6 +293,7 @@ int main(int argc, char** argv) {
|
||||
CHECK_ANDROID(!pthread_join(stopper, NULL));
|
||||
CHECK_ANDROID(stop_done == 1 && joins == 1 && !self_detaches && started == (unsigned)cancel_restart);
|
||||
CHECK_ANDROID(!instance_lite_is_running() && !__real_instance_lite_get_instance() && !instance_lite_get_uasync());
|
||||
CHECK_ANDROID(instance_lite_set_active(1) == -1);
|
||||
CHECK_ANDROID(!errors && u_get_allocated_count() == allocated);
|
||||
/* Тот же singleton должен запуститься снова только после полного join. */
|
||||
pause_loop = pause_instance = pause_attach = 0;
|
||||
@@ -297,6 +310,12 @@ int main(int argc, char** argv) {
|
||||
CHECK_ANDROID(!started && attached == (unsigned)voice_failure && detached == 1 && destroyed == 1 && loops_destroyed == 1);
|
||||
} else {
|
||||
CHECK_ANDROID(wait_for(&started, 1) && instance_lite_is_running());
|
||||
CHECK_ANDROID(!instance_lite_set_active(1));
|
||||
uasync_post(instance_lite_get_uasync(), inspect_activity, (void*)(intptr_t)1);
|
||||
CHECK_ANDROID(wait_for(&activity_checked, 1));
|
||||
CHECK_ANDROID(!instance_lite_set_active(0));
|
||||
uasync_post(instance_lite_get_uasync(), inspect_activity, NULL);
|
||||
CHECK_ANDROID(wait_for(&activity_checked, 2));
|
||||
CHECK_ANDROID(!utun_bridge_call_audio_start(42));
|
||||
uasync_post(instance_lite_get_uasync(), inspect, NULL);
|
||||
CHECK_ANDROID(wait_for(&checked, 1));
|
||||
|
||||
Reference in new issue
Block a user