Skip to content

Commit 47447f8

Browse files
Correction to error handling for posix runtime and example
1 parent 2bd0a52 commit 47447f8

6 files changed

Lines changed: 145 additions & 23 deletions

File tree

Examples/hello_posix/config/stream_init.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,17 @@ static void handle_error(int32_t origin, int32_t error_code, int32_t info)
6464
{
6565
(void)info;
6666
CMSISSTREAM_LOG_ERR("Error from origin %d with code %d\n", origin, error_code);
67-
stream_free_all(false);
67+
/*
68+
* This handler can be called from a runtime thread or from a graph node.
69+
* stream_free_all(true) may delete nodes, FIFOs, queues, and scheduler
70+
* resources that are still present on the current call stack. It is safe
71+
* here because the example exits immediately after freeing resources.
72+
*
73+
* If an application wants to recover and restart a graph, do not return
74+
* from this handler after freeing resources. Instead, signal a supervisor
75+
* thread and let that thread stop/free/recreate/start the graph.
76+
*/
77+
stream_free_all(true);
6878
std::exit(1);
6979
}
7080

@@ -140,11 +150,9 @@ void stream_configure_and_start()
140150
CMSISSTREAM_LOG_ERR("Fatal error in main, stopping execution\n");
141151
}
142152

143-
void stream_free_all(bool mustWait)
153+
void stream_free_all(bool callerIsRuntimeThread)
144154
{
145-
if (mustWait) {
146-
stream_wait_for_threads_end();
147-
}
155+
stream_stop_threads(callerIsRuntimeThread);
148156

149157
free_scheduler_hello();
150158

Examples/hello_posix/config/stream_init.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,13 @@ extern stream_execution_context_t contexts[NB_APPS];
1515
extern int currentNetwork;
1616

1717
void stream_configure_and_start();
18-
void stream_free_all(bool mustWait = true);
18+
19+
/**
20+
* @brief Stop stream execution and release all stream resources.
21+
*
22+
* @param callerIsRuntimeThread Use false when called from a normal control
23+
* thread after stream execution has ended. Use true when called from an
24+
* application handler or graph node running on a runtime thread. Peer runtime
25+
* threads are still stopped before graph resources are freed.
26+
*/
27+
void stream_free_all(bool callerIsRuntimeThread = false);

platform/posix_runtime/config/stream_init.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,17 @@ static void handle_error(int32_t origin, int32_t error_code, int32_t info)
6464
{
6565
(void)info;
6666
CMSISSTREAM_LOG_ERR("Error from origin %d with code %d\n", origin, error_code);
67-
stream_free_all(false);
67+
/*
68+
* This handler can be called from a runtime thread or from a graph node.
69+
* stream_free_all(true) may delete nodes, FIFOs, queues, and scheduler
70+
* resources that are still present on the current call stack. It is safe
71+
* here because the example exits immediately after freeing resources.
72+
*
73+
* If an application wants to recover and restart a graph, do not return
74+
* from this handler after freeing resources. Instead, signal a supervisor
75+
* thread and let that thread stop/free/recreate/start the graph.
76+
*/
77+
stream_free_all(true);
6878
std::exit(1);
6979
}
7080

@@ -139,11 +149,9 @@ void stream_configure_and_start()
139149
CMSISSTREAM_LOG_ERR("Fatal error in main, stopping execution\n");
140150
}
141151

142-
void stream_free_all(bool mustWait)
152+
void stream_free_all(bool callerIsRuntimeThread)
143153
{
144-
if (mustWait) {
145-
stream_wait_for_threads_end();
146-
}
154+
stream_stop_threads(callerIsRuntimeThread);
147155

148156
free_scheduler_hello();
149157

platform/posix_runtime/config/stream_init.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,13 @@ extern stream_execution_context_t contexts[NB_APPS];
1515
extern int currentNetwork;
1616

1717
void stream_configure_and_start();
18-
void stream_free_all(bool mustWait = true);
18+
19+
/**
20+
* @brief Stop stream execution and release all stream resources.
21+
*
22+
* @param callerIsRuntimeThread Use false when called from a normal control
23+
* thread after stream execution has ended. Use true when called from an
24+
* application handler or graph node running on a runtime thread. Peer runtime
25+
* threads are still stopped before graph resources are freed.
26+
*/
27+
void stream_free_all(bool callerIsRuntimeThread = false);

platform/posix_runtime/stream_runtime_init.cpp

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ static std::thread *stream_thread = nullptr;
2626
static std::thread *event_thread = nullptr;
2727
static std::atomic<bool> stream_thread_started = false;
2828
static std::atomic<bool> event_thread_started = false;
29+
static std::atomic<bool> runtime_stop_requested = false;
2930

3031
std::atomic<stream_execution_context_t *> current_context = nullptr;
3132

@@ -103,6 +104,10 @@ static void wait_stream_resume(stream_execution_context_t *context)
103104
continue;
104105
}
105106

107+
if (runtime_stop_requested.load()) {
108+
return;
109+
}
110+
106111
context = current_context.load();
107112
context->reset_fifos(1);
108113
if (context->resume_all_nodes) {
@@ -145,6 +150,9 @@ static void pause_stream_thread_on_error(stream_execution_context_t *context, cg
145150

146151
wait_event_thread_paused();
147152
report_runtime_error(kStreamThread, status, CG_UNIDENTIFIED_NODE, 0);
153+
if (runtime_stop_requested.load()) {
154+
return;
155+
}
148156
cg_streamReplyEvent.post(STREAM_PAUSED_EVENT);
149157
wait_stream_resume(context);
150158
}
@@ -160,6 +168,9 @@ static void pause_stream_thread_on_stop_graph(stream_execution_context_t *contex
160168

161169
wait_event_thread_paused();
162170
report_stop_graph();
171+
if (runtime_stop_requested.load()) {
172+
return;
173+
}
163174
cg_streamReplyEvent.post(STREAM_PAUSED_EVENT);
164175
wait_stream_resume(context);
165176
}
@@ -204,6 +215,9 @@ static void event_thread_function()
204215
report_runtime_error(kEventThread, event_error,
205216
event_error_node, event_error_info);
206217
}
218+
if (runtime_stop_requested.load()) {
219+
return;
220+
}
207221
cg_streamReplyEvent.post(EVENT_PAUSED_EVENT);
208222
wait_event_resume();
209223
}
@@ -225,11 +239,17 @@ static void stream_thread_function()
225239
if (is_runtime_scheduler_error(error)) {
226240
CMSISSTREAM_LOG_ERR("Scheduler error %d\n", error);
227241
pause_stream_thread_on_error(context, static_cast<cg_status>(error));
242+
if (runtime_stop_requested.load()) {
243+
break;
244+
}
228245
continue;
229246
}
230247
if ((context->scheduler_length > 0) && (error == CG_STOP_SCHEDULER)) {
231248
CMSISSTREAM_LOG_DBG("Scheduler requested stop graph\n");
232249
pause_stream_thread_on_stop_graph(context);
250+
if (runtime_stop_requested.load()) {
251+
break;
252+
}
233253
continue;
234254
}
235255
if ((error == CG_PAUSED_SCHEDULER) ||
@@ -242,6 +262,9 @@ static void stream_thread_function()
242262
}
243263

244264
wait_stream_resume(context);
265+
if (runtime_stop_requested.load()) {
266+
break;
267+
}
245268
} else {
246269
done = true;
247270
}
@@ -316,6 +339,7 @@ bool stream_start_threads(stream_execution_context_t *context)
316339
}
317340

318341
set_current_context(context);
342+
runtime_stop_requested.store(false);
319343

320344
try {
321345
event_thread = new std::thread(event_thread_function);
@@ -342,20 +366,71 @@ bool stream_start_threads(stream_execution_context_t *context)
342366
return true;
343367
}
344368

345-
void stream_wait_for_threads_end()
369+
static void stop_thread(std::thread *&thread, std::atomic<bool> &started,
370+
bool callerIsRuntimeThread)
346371
{
347-
if (stream_thread_started.load() && (stream_thread != nullptr) && stream_thread->joinable()) {
348-
stream_thread->join();
349-
stream_thread_started.store(false);
350-
delete stream_thread;
351-
stream_thread = nullptr;
372+
if (!started.load() || (thread == nullptr)) {
373+
return;
352374
}
353-
if (event_thread_started.load() && (event_thread != nullptr) && event_thread->joinable()) {
354-
event_thread->join();
355-
event_thread_started.store(false);
356-
delete event_thread;
357-
event_thread = nullptr;
375+
376+
if (thread->joinable()) {
377+
if (thread->get_id() == std::this_thread::get_id()) {
378+
(void)callerIsRuntimeThread;
379+
thread->detach();
380+
} else {
381+
// Always wait for peer runtime threads. Freeing graph resources
382+
// while a peer thread may still execute them is not safe.
383+
thread->join();
384+
}
358385
}
386+
387+
started.store(false);
388+
delete thread;
389+
thread = nullptr;
390+
}
391+
392+
static void wait_thread(std::thread *&thread, std::atomic<bool> &started)
393+
{
394+
if (!started.load() || (thread == nullptr)) {
395+
return;
396+
}
397+
398+
if (thread->joinable()) {
399+
if (thread->get_id() == std::this_thread::get_id()) {
400+
thread->detach();
401+
} else {
402+
thread->join();
403+
}
404+
}
405+
406+
started.store(false);
407+
delete thread;
408+
thread = nullptr;
409+
}
410+
411+
void stream_stop_threads(bool callerIsRuntimeThread)
412+
{
413+
runtime_stop_requested.store(true);
414+
415+
stream_execution_context_t *context = current_context.load();
416+
if ((context != nullptr) && (context->evtQueue != nullptr)) {
417+
context->evtQueue->end();
418+
}
419+
420+
cg_streamEvent.post(STREAM_DONE_EVENT | STREAM_RESUME_EVENT | EVENT_RESUME_EVENT);
421+
cg_streamReplyEvent.post(STREAM_PAUSED_EVENT | STREAM_RESUMED_EVENT |
422+
EVENT_PAUSED_EVENT | EVENT_RESUMED_EVENT);
423+
424+
stop_thread(event_thread, event_thread_started, callerIsRuntimeThread);
425+
stop_thread(stream_thread, stream_thread_started, callerIsRuntimeThread);
426+
set_current_context(nullptr);
427+
}
428+
429+
void stream_wait_for_threads_end()
430+
{
431+
wait_thread(stream_thread, stream_thread_started);
432+
wait_thread(event_thread, event_thread_started);
433+
set_current_context(nullptr);
359434
}
360435

361436
void stream_free_memory()

platform/posix_runtime/stream_runtime_init.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@ extern void stream_pause_current_scheduler();
4747
extern bool stream_resume_scheduler(stream_execution_context_t *context);
4848
extern int stream_init_memory();
4949
extern bool stream_start_threads(stream_execution_context_t *context);
50+
/*
51+
* Request runtime threads to stop.
52+
*
53+
* The function always wakes paused/runtime waits and joins peer runtime threads
54+
* before returning.
55+
*
56+
* @param callerIsRuntimeThread Set to true when this function is called from a
57+
* runtime callback or graph node that is running on one of the runtime threads.
58+
* Peer runtime threads are always joined before this function returns. When the
59+
* caller is itself a runtime thread, that current thread is detached because a
60+
* thread cannot join itself.
61+
*/
62+
extern void stream_stop_threads(bool callerIsRuntimeThread = false);
5063
extern void stream_wait_for_threads_end();
5164
extern void stream_free_memory();
5265
extern arm_cmsis_stream::EventQueue *stream_new_event_queue();

0 commit comments

Comments
 (0)