Skip to content

Commit 5b36c54

Browse files
FuriThread: Improve state callbacks (#3881)
State callbacks assumed they were invoked from the thread that changed its state, but this wasn't true for FuriThreadStateStarting in the past, and now it's not true for FuriThreadStateStopped either. Now it is safe to release the thread memory form the state callback once it switches to FuriThreadStateStopped. Therefore, pending deletion calls can be removed. Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
1 parent 369e19d commit 5b36c54

File tree

8 files changed

+58
-66
lines changed

8 files changed

+58
-66
lines changed

applications/services/loader/loader.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,12 +308,14 @@ static void loader_applications_closed_callback(void* context) {
308308
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
309309
}
310310

311-
static void loader_thread_state_callback(FuriThreadState thread_state, void* context) {
311+
static void
312+
loader_thread_state_callback(FuriThread* thread, FuriThreadState thread_state, void* context) {
313+
UNUSED(thread);
312314
furi_assert(context);
313315

314-
Loader* loader = context;
315-
316316
if(thread_state == FuriThreadStateStopped) {
317+
Loader* loader = context;
318+
317319
LoaderMessage message;
318320
message.type = LoaderMessageTypeAppClosed;
319321
furi_message_queue_put(loader->queue, &message, FuriWaitForever);

applications/services/region/region.c

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -104,19 +104,12 @@ static int32_t region_load_file(void* context) {
104104
return 0;
105105
}
106106

107-
static void region_loader_pending_callback(void* context, uint32_t arg) {
108-
UNUSED(arg);
109-
110-
FuriThread* loader = context;
111-
furi_thread_join(loader);
112-
furi_thread_free(loader);
113-
}
114-
115-
static void region_loader_state_callback(FuriThreadState state, void* context) {
107+
static void
108+
region_loader_release_callback(FuriThread* thread, FuriThreadState state, void* context) {
116109
UNUSED(context);
117110

118111
if(state == FuriThreadStateStopped) {
119-
furi_timer_pending_callback(region_loader_pending_callback, furi_thread_get_current(), 0);
112+
furi_thread_free(thread);
120113
}
121114
}
122115

@@ -126,7 +119,7 @@ static void region_storage_callback(const void* message, void* context) {
126119

127120
if(event->type == StorageEventTypeCardMount) {
128121
FuriThread* loader = furi_thread_alloc_ex(NULL, 2048, region_load_file, NULL);
129-
furi_thread_set_state_callback(loader, region_loader_state_callback);
122+
furi_thread_set_state_callback(loader, region_loader_release_callback);
130123
furi_thread_start(loader);
131124
}
132125
}

applications/services/rpc/rpc.c

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ static RpcSystemCallbacks rpc_systems[] = {
6767
struct RpcSession {
6868
Rpc* rpc;
6969

70-
FuriThread* thread;
70+
FuriThreadId thread_id;
7171

7272
RpcHandlerDict_t handlers;
7373
FuriStreamBuffer* stream;
@@ -172,7 +172,7 @@ size_t rpc_session_feed(
172172

173173
size_t bytes_sent = furi_stream_buffer_send(session->stream, encoded_bytes, size, timeout);
174174

175-
furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtNewData);
175+
furi_thread_flags_set(session->thread_id, RpcEvtNewData);
176176

177177
return bytes_sent;
178178
}
@@ -220,7 +220,7 @@ bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {
220220
break;
221221
} else {
222222
/* Save disconnect flag and continue reading buffer */
223-
furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtDisconnect);
223+
furi_thread_flags_set(session->thread_id, RpcEvtDisconnect);
224224
}
225225
} else if(flags & RpcEvtNewData) {
226226
// Just wake thread up
@@ -347,35 +347,32 @@ static int32_t rpc_session_worker(void* context) {
347347
return 0;
348348
}
349349

350-
static void rpc_session_thread_pending_callback(void* context, uint32_t arg) {
351-
UNUSED(arg);
352-
RpcSession* session = (RpcSession*)context;
350+
static void rpc_session_thread_release_callback(
351+
FuriThread* thread,
352+
FuriThreadState thread_state,
353+
void* context) {
354+
if(thread_state == FuriThreadStateStopped) {
355+
RpcSession* session = (RpcSession*)context;
353356

354-
for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) {
355-
if(rpc_systems[i].free) {
356-
(rpc_systems[i].free)(session->system_contexts[i]);
357+
for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) {
358+
if(rpc_systems[i].free) {
359+
(rpc_systems[i].free)(session->system_contexts[i]);
360+
}
357361
}
358-
}
359-
free(session->system_contexts);
360-
free(session->decoded_message);
361-
RpcHandlerDict_clear(session->handlers);
362-
furi_stream_buffer_free(session->stream);
363-
364-
furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);
365-
if(session->terminated_callback) {
366-
session->terminated_callback(session->context);
367-
}
368-
furi_mutex_release(session->callbacks_mutex);
369-
370-
furi_mutex_free(session->callbacks_mutex);
371-
furi_thread_join(session->thread);
372-
furi_thread_free(session->thread);
373-
free(session);
374-
}
362+
free(session->system_contexts);
363+
free(session->decoded_message);
364+
RpcHandlerDict_clear(session->handlers);
365+
furi_stream_buffer_free(session->stream);
366+
367+
furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);
368+
if(session->terminated_callback) {
369+
session->terminated_callback(session->context);
370+
}
371+
furi_mutex_release(session->callbacks_mutex);
375372

376-
static void rpc_session_thread_state_callback(FuriThreadState thread_state, void* context) {
377-
if(thread_state == FuriThreadStateStopped) {
378-
furi_timer_pending_callback(rpc_session_thread_pending_callback, context, 0);
373+
furi_mutex_free(session->callbacks_mutex);
374+
furi_thread_free(thread);
375+
free(session);
379376
}
380377
}
381378

@@ -407,12 +404,14 @@ RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) {
407404
};
408405
rpc_add_handler(session, PB_Main_stop_session_tag, &rpc_handler);
409406

410-
session->thread = furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session);
407+
FuriThread* thread =
408+
furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session);
409+
session->thread_id = furi_thread_get_id(thread);
411410

412-
furi_thread_set_state_context(session->thread, session);
413-
furi_thread_set_state_callback(session->thread, rpc_session_thread_state_callback);
411+
furi_thread_set_state_context(thread, session);
412+
furi_thread_set_state_callback(thread, rpc_session_thread_release_callback);
414413

415-
furi_thread_start(session->thread);
414+
furi_thread_start(thread);
416415

417416
return session;
418417
}
@@ -424,7 +423,7 @@ void rpc_session_close(RpcSession* session) {
424423
rpc_session_set_send_bytes_callback(session, NULL);
425424
rpc_session_set_close_callback(session, NULL);
426425
rpc_session_set_buffer_is_empty_callback(session, NULL);
427-
furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtDisconnect);
426+
furi_thread_flags_set(session->thread_id, RpcEvtDisconnect);
428427
}
429428

430429
void rpc_on_system_start(void* p) {

applications/system/updater/util/update_task.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -395,14 +395,15 @@ bool update_task_open_file(UpdateTask* update_task, FuriString* filename) {
395395
return open_success;
396396
}
397397

398-
static void update_task_worker_thread_cb(FuriThreadState state, void* context) {
399-
UpdateTask* update_task = context;
398+
static void
399+
update_task_worker_thread_cb(FuriThread* thread, FuriThreadState state, void* context) {
400+
UNUSED(context);
400401

401402
if(state != FuriThreadStateStopped) {
402403
return;
403404
}
404405

405-
if(furi_thread_get_return_code(update_task->thread) == UPDATE_TASK_NOERR) {
406+
if(furi_thread_get_return_code(thread) == UPDATE_TASK_NOERR) {
406407
furi_delay_ms(UPDATE_DELAY_OPERATION_OK);
407408
furi_hal_power_reset();
408409
}
@@ -427,7 +428,6 @@ UpdateTask* update_task_alloc(void) {
427428
furi_thread_alloc_ex("UpdateWorker", 5120, NULL, update_task);
428429

429430
furi_thread_set_state_callback(thread, update_task_worker_thread_cb);
430-
furi_thread_set_state_context(thread, update_task);
431431
#ifdef FURI_RAM_EXEC
432432
UNUSED(update_task_worker_backup_restore);
433433
furi_thread_set_callback(thread, update_task_worker_flash_writer);

furi/core/thread.c

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ struct FuriThread {
3333
StaticTask_t container;
3434
StackType_t* stack_buffer;
3535

36-
FuriThreadState state;
36+
volatile FuriThreadState state;
3737
int32_t ret;
3838

3939
FuriThreadCallback callback;
@@ -59,7 +59,6 @@ struct FuriThread {
5959
// this ensures that the size of this structure is minimal
6060
bool is_service;
6161
bool heap_trace_enabled;
62-
volatile bool is_active;
6362
};
6463

6564
// IMPORTANT: container MUST be the FIRST struct member
@@ -84,7 +83,7 @@ static void furi_thread_set_state(FuriThread* thread, FuriThreadState state) {
8483
furi_assert(thread);
8584
thread->state = state;
8685
if(thread->state_callback) {
87-
thread->state_callback(state, thread->state_context);
86+
thread->state_callback(thread, state, thread->state_context);
8887
}
8988
}
9089

@@ -124,7 +123,7 @@ static void furi_thread_body(void* context) {
124123
// flush stdout
125124
__furi_thread_stdout_flush(thread);
126125

127-
furi_thread_set_state(thread, FuriThreadStateStopped);
126+
furi_thread_set_state(thread, FuriThreadStateStopping);
128127

129128
vTaskDelete(NULL);
130129
furi_thread_catch();
@@ -207,7 +206,6 @@ void furi_thread_free(FuriThread* thread) {
207206
furi_check(thread->is_service == false);
208207
// Cannot free a non-joined thread
209208
furi_check(thread->state == FuriThreadStateStopped);
210-
furi_check(!thread->is_active);
211209

212210
furi_thread_set_name(thread, NULL);
213211
furi_thread_set_appid(thread, NULL);
@@ -349,8 +347,6 @@ void furi_thread_start(FuriThread* thread) {
349347

350348
uint32_t stack_depth = thread->stack_size / sizeof(StackType_t);
351349

352-
thread->is_active = true;
353-
354350
furi_check(
355351
xTaskCreateStatic(
356352
furi_thread_body,
@@ -368,7 +364,7 @@ void furi_thread_cleanup_tcb_event(TaskHandle_t task) {
368364
// clear thread local storage
369365
vTaskSetThreadLocalStoragePointer(task, 0, NULL);
370366
furi_check(thread == (FuriThread*)task);
371-
thread->is_active = false;
367+
furi_thread_set_state(thread, FuriThreadStateStopped);
372368
}
373369
}
374370

@@ -383,8 +379,8 @@ bool furi_thread_join(FuriThread* thread) {
383379
//
384380
// If your thread exited, but your app stuck here: some other thread uses
385381
// all cpu time, which delays kernel from releasing task handle
386-
while(thread->is_active) {
387-
furi_delay_ms(10);
382+
while(thread->state != FuriThreadStateStopped) {
383+
furi_delay_tick(2);
388384
}
389385

390386
return true;

furi/core/thread.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ extern "C" {
2121
* Many of the FuriThread functions MUST ONLY be called when the thread is STOPPED.
2222
*/
2323
typedef enum {
24-
FuriThreadStateStopped, /**< Thread is stopped */
24+
FuriThreadStateStopped, /**< Thread is stopped and is safe to release */
25+
FuriThreadStateStopping, /**< Thread is stopping */
2526
FuriThreadStateStarting, /**< Thread is starting */
2627
FuriThreadStateRunning, /**< Thread is running */
2728
} FuriThreadState;
@@ -80,10 +81,11 @@ typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size);
8081
*
8182
* The function to be used as a state callback MUST follow this signature.
8283
*
84+
* @param[in] pointer to the FuriThread instance that changed the state
8385
* @param[in] state identifier of the state the thread has transitioned to
8486
* @param[in,out] context pointer to a user-specified object
8587
*/
86-
typedef void (*FuriThreadStateCallback)(FuriThreadState state, void* context);
88+
typedef void (*FuriThreadStateCallback)(FuriThread* thread, FuriThreadState state, void* context);
8789

8890
/**
8991
* @brief Signal handler callback function pointer type.

targets/f18/api_symbols.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
entry,status,name,type,params
2-
Version,+,75.0,,
2+
Version,+,76.0,,
33
Header,+,applications/services/bt/bt_service/bt.h,,
44
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
55
Header,+,applications/services/cli/cli.h,,

targets/f7/api_symbols.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
entry,status,name,type,params
2-
Version,+,75.0,,
2+
Version,+,76.0,,
33
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
44
Header,+,applications/services/bt/bt_service/bt.h,,
55
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,

0 commit comments

Comments
 (0)