Remove explicit app closing function (#618)

This commit is contained in:
Ken Van Hoeylandt 2026-08-21 08:59:52 +02:00 committed by GitHub
parent 0ff1627385
commit 4fea48f433
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 19 additions and 89 deletions

View File

@ -23,7 +23,8 @@ extern "C" {
* firmware binary. Called on the dedicated task app-module's scheduler spawns for this instance,
* blocking for the app's whole lifetime - same contract as an external app's main(), plus
* @a app_instance_id identifying this running instance (use it with
* app_event_subscribe()/window_manager_create()/app_manager_finish()/etc.).
* app_event_subscribe()/window_manager_create()/etc.). The instance closes when this function
* returns - no separate call is needed.
* AppManifest::location.location holds this cast to void*.
*/
typedef int32_t (*AppMainFn)(uint32_t app_instance_id, int argc, char* argv[]);

View File

@ -90,23 +90,11 @@ error_t app_manager_start_for_result(const char* id, AppInstanceId parent_instan
* Stop an app instance permanently. Emits APP_EVENT_CLOSE and bound-waits for its task to exit
* if it was running.
* @warning Must not be called from the instance's own task (it bound-waits via thread_join(),
* which asserts against joining yourself) - an app closing itself must call app_manager_finish()
* instead, right before returning from its own AppMainFn/AppLoaderApi::run().
* which asserts against joining yourself) - an app closes itself by returning from its own
* AppMainFn/AppLoaderApi::run(), not by calling this on itself.
*/
error_t app_manager_stop(AppInstanceId app_instance_id);
/**
* Called by an app instance, from its own task, right before it returns in response to
* APP_EVENT_CLOSE - whether that close was self-initiated (e.g. its own back button) or came
* from someone else. Marks this instance Stopped immediately (rather than waiting for its task
* to actually exit) so app_manager_get_state()/app_manager_get_topmost_instance_id() reflect the
* closure as soon as the app has decided to close, not just once its task has fully unwound.
* @warning Does not join or free this instance's own task/ledger entry (can't - this runs on
* that very task); those are cleaned up on a later app_manager_stop() call, same as any
* self-terminating instance.
*/
error_t app_manager_finish(AppInstanceId app_instance_id);
/** @return the instance's current state, or APP_INSTANCE_STATE_STOPPED if the id is unknown. */
AppInstanceState app_manager_get_state(AppInstanceId app_instance_id);

View File

@ -156,9 +156,9 @@ void app_task_main(void* context) {
deliver_result_to_parent_if_any(ctx->app_instance_id, result);
// A safe default terminal marker for CLOSE (and any other exit): an app that calls
// app_manager_finish() already marked itself Stopped before returning, so this is a no-op
// for it - but it's still needed as the terminal marker for any other exit path.
// The terminal marker for every exit path: an app instance is Stopped exactly when its
// AppMainFn/AppLoaderApi::run() has returned, whether that return was self-initiated or in
// response to APP_EVENT_CLOSE.
set_state(ctx->app_instance_id, APP_INSTANCE_STATE_STOPPED);
app_ledger_free_arguments(ctx->argc, ctx->argv);
@ -269,6 +269,11 @@ error_t app_scheduler_stop(AppInstanceId app_instance_id, TickType_t join_timeou
AppEvent event { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} };
app_event_emit(app_instance_id, &event);
// Marked as soon as the app has been told to close, not once its task has actually
// unwound - so app_manager_get_state()/app_manager_get_topmost_instance_id() reflect the
// closure immediately, without waiting on whatever teardown the app still has left to do.
set_state(app_instance_id, APP_INSTANCE_STATE_STOPPING);
// Blocks until app_task_main() gives this dedicated semaphore as the literal last thing it does before vTaskDelete().
// Uses aa dedicated semaphore rather than this task's default FreeRTOS notification because app_event.cpp's AppEventSubscription also uses that shared slot.
// An unrelated event (e.g. a different child's APP_EVENT_RESULT) delivered to this same task could otherwise unblock this early.

View File

@ -138,17 +138,6 @@ error_t app_manager_stop(AppInstanceId app_instance_id) {
return app_scheduler_stop(app_instance_id, pdMS_TO_TICKS(2000));
}
error_t app_manager_finish(AppInstanceId app_instance_id) {
auto& ledger = app_ledger();
mutex_lock(&ledger.mutex);
auto iterator = ledger.instances.find(app_instance_id);
if (iterator != ledger.instances.end()) {
iterator->second.state = APP_INSTANCE_STATE_STOPPED;
}
mutex_unlock(&ledger.mutex);
return ERROR_NONE;
}
AppInstanceState app_manager_get_state(AppInstanceId app_instance_id) {
auto& ledger = app_ledger();
mutex_lock(&ledger.mutex);

View File

@ -30,7 +30,6 @@ const ModuleSymbol app_module_symbols[] = {
DEFINE_MODULE_SYMBOL(app_manager_start_with_parameters),
DEFINE_MODULE_SYMBOL(app_manager_start_for_result),
DEFINE_MODULE_SYMBOL(app_manager_stop),
DEFINE_MODULE_SYMBOL(app_manager_finish),
DEFINE_MODULE_SYMBOL(app_manager_get_state),
DEFINE_MODULE_SYMBOL(app_manager_find_manifest),
DEFINE_MODULE_SYMBOL(app_manager_for_each_manifest),

View File

@ -76,7 +76,6 @@ int32_t fake_run(void*, uint32_t app_instance_id, int argc, char* argv[]) {
break; // safety net so a bug here can't hang the test suite
}
if (event.type == APP_EVENT_CLOSE) {
app_manager_finish(app_instance_id);
break;
}
}

View File

@ -217,7 +217,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
case APP_EVENT_RESULT:

View File

@ -114,7 +114,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
break;
}
if (event.type == APP_EVENT_CLOSE) {
app_manager_finish(appInstanceId); // no-op: modal children never supersede anything
break;
}
}

View File

@ -111,7 +111,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
ctx.targetAppId = (argc > 0) ? argv[0] : std::string();
if (app_manager_find_manifest(ctx.targetAppId.c_str(), &ctx.targetManifest) != ERROR_NONE) {
LOG_W(TAG, "App %s not found", ctx.targetAppId.c_str());
app_manager_finish(appInstanceId);
return 0;
}
@ -129,14 +128,12 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
case APP_EVENT_RESULT:
if (event.result.launch_id == ctx.pendingUninstallDialogId) {
if (event.result.result == 0) { // 0 = Yes
app_uninstall(ctx.targetManifest.id);
app_manager_finish(appInstanceId);
shouldClose = true;
}
app_manager_stop(event.result.launch_id);

View File

@ -215,7 +215,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -244,7 +244,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
case APP_EVENT_RESULT: {

View File

@ -93,7 +93,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
break;
}
if (event.type == APP_EVENT_CLOSE) {
app_manager_finish(appInstanceId);
break;
}
}

View File

@ -104,7 +104,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -140,7 +140,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -229,7 +229,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -310,7 +310,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
break;
}
if (event.type == APP_EVENT_CLOSE) {
app_manager_finish(appInstanceId);
break;
}
}

View File

@ -249,7 +249,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -213,7 +213,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
case APP_EVENT_RESULT:
@ -226,7 +225,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
}
bluetooth::unpair(ctx.addr);
app_manager_finish(appInstanceId);
shouldClose = true;
}
app_manager_stop(event.result.launch_id);

View File

@ -199,7 +199,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -168,15 +168,12 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:
break;
}
}
} else {
app_manager_finish(appInstanceId);
}
window_manager_remove(window);

View File

@ -171,7 +171,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
LOG_E(TAG, "Service not found");
// No window/subscription was ever created - matches the old model, where onCreate()
// aborting the app meant onShow() was never called either.
app_manager_finish(appInstanceId);
return 0;
}
@ -204,7 +203,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -44,7 +44,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
case APP_EVENT_RESULT:

View File

@ -74,7 +74,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
break;
}
if (event.type == APP_EVENT_CLOSE) {
app_manager_finish(appInstanceId); // no-op: modal children never supersede anything
break;
}
}

View File

@ -282,7 +282,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
case APP_EVENT_RESULT:

View File

@ -107,7 +107,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -403,7 +403,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
switch (event.type) {
case APP_EVENT_CLOSE:
stopScanningIfRunning(&ctx);
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -103,7 +103,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -123,7 +123,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
break;
}
if (event.type == APP_EVENT_CLOSE) {
app_manager_finish(appInstanceId); // no-op: modal children never supersede anything
break;
}
}

View File

@ -323,7 +323,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
switch (event.type) {
case APP_EVENT_CLOSE:
persistIfUpdated(ctx);
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -224,7 +224,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
switch (event.type) {
case APP_EVENT_CLOSE:
persistIfUpdated(ctx);
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -248,7 +248,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
break;
}
if (event.type == APP_EVENT_CLOSE) {
app_manager_finish(appInstanceId);
break;
}
}

View File

@ -159,7 +159,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -204,7 +204,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
case APP_EVENT_RESULT:

View File

@ -255,7 +255,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -150,7 +150,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -264,7 +264,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -116,7 +116,6 @@ int32_t appMain(AppInstanceId appInstanceId, int argc, char* argv[]) {
break;
}
if (event.type == APP_EVENT_CLOSE) {
app_manager_finish(appInstanceId); // no-op: modal children never supersede anything
break;
}
}

View File

@ -91,7 +91,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
break;
}
if (event.type == APP_EVENT_CLOSE) {
app_manager_finish(appInstanceId);
break;
}
}

View File

@ -157,9 +157,9 @@ void onContinueClicked(lv_event_t* event) {
break;
case Phase::Done: {
markCompleted();
// Async, non-blocking - must NOT call app_manager_stop()/app_manager_finish()
// directly here: this callback runs ON the LVGL task, and app-lifecycle
// transitions must happen on this app's own thread (woken via app_event_await()).
// Async, non-blocking - must NOT call app_manager_stop() directly here: this
// callback runs ON the LVGL task, and app-lifecycle transitions must happen on this
// app's own thread (woken via app_event_await()), which closes by returning.
AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} };
app_event_emit(ctx->appInstanceId, &closeEvent);
break;
@ -243,7 +243,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
case APP_EVENT_RESULT:

View File

@ -433,7 +433,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -171,7 +171,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
case APP_EVENT_RESULT:

View File

@ -253,7 +253,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
break;
}
if (event.type == APP_EVENT_CLOSE) {
app_manager_finish(appInstanceId); // no-op: modal children never supersede anything
break;
}
}

View File

@ -172,10 +172,10 @@ void onPress(lv_event_t* event) {
return;
}
// Async, non-blocking - must NOT call app_manager_stop()/app_manager_finish() directly
// here: this callback runs ON the LVGL task, and app-lifecycle transitions must happen on
// this app's own thread (woken up via app_event_await() below). The result (Ok/Error) is
// reported by appMain() itself when it returns, based on ctx.calibrationApplied.
// Async, non-blocking - must NOT call app_manager_stop() directly here: this callback runs
// ON the LVGL task, and app-lifecycle transitions must happen on this app's own thread
// (woken up via app_event_await() below), which closes by returning. The result (Ok/Error)
// is reported by appMain() itself when it returns, based on ctx.calibrationApplied.
AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} };
app_event_emit(ctx->appInstanceId, &closeEvent);
}
@ -247,7 +247,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -287,7 +287,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
switch (event.type) {
case APP_EVENT_CLOSE:
persistIfUpdated(ctx);
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -99,7 +99,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -376,7 +376,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -228,7 +228,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
case APP_EVENT_RESULT:
@ -243,7 +242,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
) {
service::wifi::disconnect();
}
app_manager_finish(appInstanceId);
shouldClose = true;
}
}

View File

@ -334,7 +334,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -160,7 +160,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
}
switch (event.type) {
case APP_EVENT_CLOSE:
app_manager_finish(appInstanceId);
shouldClose = true;
break;
default:

View File

@ -33,7 +33,6 @@ int main(int argc, char* argv[]) {
break;
}
if (event.type == APP_EVENT_CLOSE) {
app_manager_finish(app_instance_id);
should_close = true;
}
}