Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FL-3847, FL-3513] Thread Signals #3730

Merged
merged 13 commits into from
Jun 21, 2024
56 changes: 52 additions & 4 deletions applications/services/gui/view_dispatcher.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,9 @@ void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher) {
FuriEventLoopEventIn,
view_dispatcher_run_event_callback,
view_dispatcher);
}

void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher, void* context) {
furi_check(view_dispatcher);
view_dispatcher->event_context = context;
furi_thread_set_signal_callback(
furi_thread_get_current(), view_dispatcher_handle_signal_event, view_dispatcher);
}

void view_dispatcher_set_navigation_event_callback(
Expand All @@ -97,6 +95,18 @@ void view_dispatcher_set_tick_event_callback(
view_dispatcher->tick_period = tick_period;
}

void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher, void* context) {
furi_check(view_dispatcher);
view_dispatcher->event_context = context;
}

void view_dispatcher_set_signal_event_callback(
ViewDispatcher* view_dispatcher,
ViewDispatcherSignalEventCallback callback) {
furi_check(view_dispatcher);
view_dispatcher->signal_callback = callback;
}

FuriEventLoop* view_dispatcher_get_event_loop(ViewDispatcher* view_dispatcher) {
furi_check(view_dispatcher);
furi_check(view_dispatcher->event_loop);
Expand Down Expand Up @@ -326,6 +336,44 @@ void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32
}
}

static bool view_dispatcher_handle_signal_exit(ViewDispatcher* instance) {
bool is_consumed = false;

if(instance->event_loop) {
furi_event_loop_stop(instance->event_loop);
is_consumed = true;
}

return is_consumed;
}

bool view_dispatcher_handle_signal_event(uint32_t signal, void* arg, void* context) {
skotopes marked this conversation as resolved.
Show resolved Hide resolved
furi_assert(context);
ViewDispatcher* instance = context;

bool is_consumed = false;

do {
if(instance->signal_callback) {
is_consumed = instance->signal_callback(signal, arg, instance->event_context);
}

if(is_consumed) break;

switch(signal) {
case FuriSignalExit:
is_consumed = view_dispatcher_handle_signal_exit(instance);
break;
// Room for possible other standard signal handlers
default:
break;
}

} while(false);

return is_consumed;
}

void view_dispatcher_send_custom_event(ViewDispatcher* view_dispatcher, uint32_t event) {
furi_check(view_dispatcher);
furi_check(view_dispatcher->event_loop);
Expand Down
14 changes: 13 additions & 1 deletion applications/services/gui/view_dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ typedef bool (*ViewDispatcherNavigationEventCallback)(void* context);
/** Prototype for tick event callback */
typedef void (*ViewDispatcherTickEventCallback)(void* context);

/** Prototype for signal event callback */
typedef bool (*ViewDispatcherSignalEventCallback)(uint32_t signal, void* arg, void* context);

/** Allocate ViewDispatcher instance
*
* @return pointer to ViewDispatcher instance
Expand Down Expand Up @@ -94,6 +97,15 @@ void view_dispatcher_set_tick_event_callback(
ViewDispatcherTickEventCallback callback,
uint32_t tick_period);

/** Set custom signal event callback
*
* @param view_dispatcher ViewDispatcher instance
* @param callback ViewDispatcherSignalEventCallback
*/
void view_dispatcher_set_signal_event_callback(
ViewDispatcher* view_dispatcher,
ViewDispatcherSignalEventCallback callback);

/** Set event callback context
*
* @param view_dispatcher ViewDispatcher instance
Expand All @@ -107,7 +119,7 @@ void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher,
* in view_dispatcher_run.
*
* You can add your objects into event_loop instance, but don't run the loop on
* your side it will cause issues with input processing on dispatcher stop.
* your side as it will cause issues with input processing on dispatcher stop.
*
* @param view_dispatcher ViewDispatcher instance
*
Expand Down
4 changes: 4 additions & 0 deletions applications/services/gui/view_dispatcher_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct ViewDispatcher {
ViewDispatcherCustomEventCallback custom_event_callback;
ViewDispatcherNavigationEventCallback navigation_event_callback;
ViewDispatcherTickEventCallback tick_event_callback;
ViewDispatcherSignalEventCallback signal_callback;
uint32_t tick_period;
void* event_context;
};
Expand All @@ -49,6 +50,9 @@ void view_dispatcher_handle_tick_event(void* context);
/** Custom event handler */
void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32_t event);

/** Signal event handler */
bool view_dispatcher_handle_signal_event(uint32_t signal, void* arg, void* context);

/** Set current view, dispatches view enter and exit */
void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* view);

Expand Down
71 changes: 70 additions & 1 deletion applications/services/loader/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,43 @@ FuriPubSub* loader_get_pubsub(Loader* loader) {
return loader->pubsub;
}

bool loader_signal(Loader* loader, uint32_t signal, void* arg) {
furi_check(loader);

LoaderMessageBoolResult result;

LoaderMessage message = {
.type = LoaderMessageTypeSignal,
.api_lock = api_lock_alloc_locked(),
.signal.signal = signal,
.signal.arg = arg,
.bool_value = &result,
};

furi_message_queue_put(loader->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);

return result.value;
}

bool loader_get_application_name(Loader* loader, FuriString* name) {
furi_check(loader);

LoaderMessageBoolResult result;

LoaderMessage message = {
.type = LoaderMessageTypeGetApplicationName,
.api_lock = api_lock_alloc_locked(),
.application_name = name,
.bool_value = &result,
};

furi_message_queue_put(loader->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);

return result.value;
}

// callbacks

static void loader_menu_closed_callback(void* context) {
Expand Down Expand Up @@ -654,6 +691,28 @@ static void loader_do_app_closed(Loader* loader) {
furi_pubsub_publish(loader->pubsub, &event);
}

static bool loader_is_application_running(Loader* loader) {
FuriThread* app_thread = loader->app.thread;
return app_thread && (app_thread != (FuriThread*)LOADER_MAGIC_THREAD_VALUE);
}

static bool loader_do_signal(Loader* loader, uint32_t signal, void* arg) {
if(loader_is_application_running(loader)) {
return furi_thread_signal(loader->app.thread, signal, arg);
}

return false;
}

static bool loader_do_get_application_name(Loader* loader, FuriString* name) {
if(loader_is_application_running(loader)) {
furi_string_set(name, furi_thread_get_name(loader->app.thread));
return true;
}

return false;
}

// app

int32_t loader_srv(void* p) {
Expand Down Expand Up @@ -714,9 +773,19 @@ int32_t loader_srv(void* p) {
case LoaderMessageTypeApplicationsClosed:
loader_do_applications_closed(loader);
break;
case LoaderMessageTypeSignal:
message.bool_value->value =
loader_do_signal(loader, message.signal.signal, message.signal.arg);
api_lock_unlock(message.api_lock);
break;
case LoaderMessageTypeGetApplicationName:
message.bool_value->value =
loader_do_get_application_name(loader, message.application_name);
api_lock_unlock(message.api_lock);
break;
}
}
}

return 0;
}
}
22 changes: 21 additions & 1 deletion applications/services/loader/loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,26 @@ void loader_show_menu(Loader* instance);
*/
FuriPubSub* loader_get_pubsub(Loader* instance);

/**
* @brief Send a signal to the currently running application
*
* @param[in] instance pointer to the loader instance
* @param[in] signal signal value to be sent
* @param[in,out] arg optional argument (can be of any value, including NULL)
*
* @return true if the signal was handled by the application, false otherwise
*/
bool loader_signal(Loader* instance, uint32_t signal, void* arg);

/**
* @brief Get the name of the currently running application
*
* @param[in] instance pointer to the loader instance
* @param[in,out] name pointer to the string to contain the name (must be allocated)
* @return true if it was possible to get an application name, false otherwise
*/
bool loader_get_application_name(Loader* instance, FuriString* name);

#ifdef __cplusplus
}
#endif
#endif
60 changes: 34 additions & 26 deletions applications/services/loader/loader_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ static void loader_cli_print_usage(void) {
printf("\tlist\t - List available applications\r\n");
printf("\topen <Application Name:string>\t - Open application by name\r\n");
printf("\tinfo\t - Show loader state\r\n");
printf("\tclose\t - Close the current application\r\n");
}

static void loader_cli_list(void) {
Expand All @@ -25,12 +26,15 @@ static void loader_cli_list(void) {
}

static void loader_cli_info(Loader* loader) {
if(!loader_is_locked(loader)) {
FuriString* app_name = furi_string_alloc();

if(!loader_get_application_name(loader, app_name)) {
printf("No application is running\r\n");
} else {
// TODO FL-3513: print application name ???
printf("Application is running\r\n");
printf("Application \"%s\" is running\r\n", furi_string_get_cstr(app_name));
}

furi_string_free(app_name);
}

static void loader_cli_open(FuriString* args, Loader* loader) {
Expand Down Expand Up @@ -60,6 +64,20 @@ static void loader_cli_open(FuriString* args, Loader* loader) {
furi_string_free(app_name);
}

static void loader_cli_close(Loader* loader) {
FuriString* app_name = furi_string_alloc();

if(!loader_get_application_name(loader, app_name)) {
printf("No application is running\r\n");
} else if(!loader_signal(loader, FuriSignalExit, NULL)) {
printf("Application \"%s\" has to be closed manually\r\n", furi_string_get_cstr(app_name));
} else {
printf("Application \"%s\" was closed\r\n", furi_string_get_cstr(app_name));
}

furi_string_free(app_name);
}

static void loader_cli(Cli* cli, FuriString* args, void* context) {
UNUSED(cli);
UNUSED(context);
Expand All @@ -68,29 +86,19 @@ static void loader_cli(Cli* cli, FuriString* args, void* context) {
FuriString* cmd;
cmd = furi_string_alloc();

do {
if(!args_read_string_and_trim(args, cmd)) {
loader_cli_print_usage();
break;
}

if(furi_string_cmp_str(cmd, "list") == 0) {
loader_cli_list();
break;
}

if(furi_string_cmp_str(cmd, "open") == 0) {
loader_cli_open(args, loader);
break;
}

if(furi_string_cmp_str(cmd, "info") == 0) {
loader_cli_info(loader);
break;
}

if(!args_read_string_and_trim(args, cmd)) {
loader_cli_print_usage();
} while(false);
} else if(furi_string_equal(cmd, "list")) {
loader_cli_list();
} else if(furi_string_equal(cmd, "open")) {
loader_cli_open(args, loader);
} else if(furi_string_equal(cmd, "info")) {
loader_cli_info(loader);
} else if(furi_string_equal(cmd, "close")) {
loader_cli_close(loader);
} else {
loader_cli_print_usage();
}

furi_string_free(cmd);
furi_record_close(RECORD_LOADER);
Expand All @@ -104,4 +112,4 @@ void loader_on_system_start(void) {
#else
UNUSED(loader_cli);
#endif
}
}
9 changes: 9 additions & 0 deletions applications/services/loader/loader_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ typedef enum {
LoaderMessageTypeUnlock,
LoaderMessageTypeIsLocked,
LoaderMessageTypeStartByNameDetachedWithGuiError,
LoaderMessageTypeSignal,
LoaderMessageTypeGetApplicationName,
} LoaderMessageType;

typedef struct {
Expand All @@ -39,6 +41,11 @@ typedef struct {
FuriString* error_message;
} LoaderMessageStartByName;

typedef struct {
uint32_t signal;
void* arg;
} LoaderMessageSignal;

typedef enum {
LoaderStatusErrorUnknown,
LoaderStatusErrorInvalidFile,
Expand All @@ -65,6 +72,8 @@ typedef struct {

union {
LoaderMessageStartByName start;
LoaderMessageSignal signal;
FuriString* application_name;
};

union {
Expand Down
6 changes: 6 additions & 0 deletions furi/core/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ typedef enum {
FuriStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
} FuriStatus;

typedef enum {
FuriSignalExit, /**< Request (graceful) exit. */
// Other standard signals may be added in the future
FuriSignalCustom = 100, /**< Custom signal values start from here. */
} FuriSignal;

#ifdef __cplusplus
}
#endif
Loading
Loading