diff --git a/applications/debug/unit_tests/tests/furi/furi_stdio_test.c b/applications/debug/unit_tests/tests/furi/furi_stdio_test.c index 94e2f613b6a..d80bd7bd576 100644 --- a/applications/debug/unit_tests/tests/furi/furi_stdio_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_stdio_test.c @@ -30,7 +30,9 @@ static size_t mock_in_cb(char* buffer, size_t size, FuriWait wait, void* context } void test_stdin(void) { - FuriThreadStdinReadCallback in_cb = furi_thread_get_stdin_callback(); + FuriThreadStdinReadCallback in_cb; + void* in_ctx; + furi_thread_get_stdin_callback(&in_cb, &in_ctx); furi_thread_set_stdin_callback(mock_in_cb, CONTEXT_MAGIC); char buf[256]; @@ -63,13 +65,14 @@ void test_stdin(void) { fgets(buf, sizeof(buf), stdin); mu_assert_string_eq(" World!\n", buf); - furi_thread_set_stdin_callback(in_cb, CONTEXT_MAGIC); + furi_thread_set_stdin_callback(in_cb, in_ctx); } // stdout static FuriString* mock_out; -FuriThreadStdoutWriteCallback original_out_cb; +static FuriThreadStdoutWriteCallback original_out_cb; +static void* original_out_ctx; static void mock_out_cb(const char* data, size_t size, void* context) { furi_check(context == CONTEXT_MAGIC); @@ -83,7 +86,7 @@ static void assert_and_clear_mock_out(const char* expected) { // return the original stdout callback for the duration of the check // if the check fails, we don't want the error to end up in our buffer, // we want to be able to see it! - furi_thread_set_stdout_callback(original_out_cb, CONTEXT_MAGIC); + furi_thread_set_stdout_callback(original_out_cb, original_out_ctx); mu_assert_string_eq(expected, furi_string_get_cstr(mock_out)); furi_thread_set_stdout_callback(mock_out_cb, CONTEXT_MAGIC); @@ -91,7 +94,7 @@ static void assert_and_clear_mock_out(const char* expected) { } void test_stdout(void) { - original_out_cb = furi_thread_get_stdout_callback(); + furi_thread_get_stdout_callback(&original_out_cb, &original_out_ctx); furi_thread_set_stdout_callback(mock_out_cb, CONTEXT_MAGIC); mock_out = furi_string_alloc(); @@ -104,5 +107,5 @@ void test_stdout(void) { assert_and_clear_mock_out("Hello!"); furi_string_free(mock_out); - furi_thread_set_stdout_callback(original_out_cb, CONTEXT_MAGIC); + furi_thread_set_stdout_callback(original_out_cb, original_out_ctx); } diff --git a/furi/core/thread.c b/furi/core/thread.c index f6cfa1d363a..d6132cdc23f 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -758,16 +758,22 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread) { return 0; } -FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void) { +void furi_thread_get_stdout_callback(FuriThreadStdoutWriteCallback* callback, void** context) { FuriThread* thread = furi_thread_get_current(); furi_check(thread); - return thread->output.write_callback; + furi_check(callback); + furi_check(context); + *callback = thread->output.write_callback; + *context = thread->output.context; } -FuriThreadStdinReadCallback furi_thread_get_stdin_callback(void) { +void furi_thread_get_stdin_callback(FuriThreadStdinReadCallback* callback, void** context) { FuriThread* thread = furi_thread_get_current(); furi_check(thread); - return thread->input.read_callback; + furi_check(callback); + furi_check(context); + *callback = thread->input.read_callback; + *context = thread->input.context; } void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback, void* context) { diff --git a/furi/core/thread.h b/furi/core/thread.h index 9abfde5cd85..c1f615d1648 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -479,16 +479,18 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id); /** * @brief Get the standard output callback for the current thead. * - * @return pointer to the standard out callback function + * @param[out] callback where to store the stdout callback + * @param[out] context where to store the context */ -FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void); +void furi_thread_get_stdout_callback(FuriThreadStdoutWriteCallback* callback, void** context); /** * @brief Get the standard input callback for the current thead. * - * @return pointer to the standard in callback function + * @param[out] callback where to store the stdin callback + * @param[out] context where to store the context */ -FuriThreadStdinReadCallback furi_thread_get_stdin_callback(void); +void furi_thread_get_stdin_callback(FuriThreadStdinReadCallback* callback, void** context); /** Set standard output callback for the current thread. * diff --git a/lib/toolbox/pipe.c b/lib/toolbox/pipe.c index 9dc1d368e21..c6a45abf1dc 100644 --- a/lib/toolbox/pipe.c +++ b/lib/toolbox/pipe.c @@ -23,6 +23,7 @@ struct PipeSide { PipeSideDataArrivedCallback on_data_arrived; PipeSideSpaceFreedCallback on_space_freed; PipeSideBrokenCallback on_pipe_broken; + FuriWait stdout_timeout; }; PipeSideBundle pipe_alloc(size_t capacity, size_t trigger_level) { @@ -52,12 +53,14 @@ PipeSideBundle pipe_alloc_ex(PipeSideReceiveSettings alice, PipeSideReceiveSetti .shared = shared, .sending = alice_to_bob, .receiving = bob_to_alice, + .stdout_timeout = FuriWaitForever, }; *bobs_side = (PipeSide){ .role = PipeRoleBob, .shared = shared, .sending = bob_to_alice, .receiving = alice_to_bob, + .stdout_timeout = FuriWaitForever, }; return (PipeSideBundle){.alices_side = alices_side, .bobs_side = bobs_side}; @@ -100,7 +103,8 @@ static void _pipe_stdout_cb(const char* data, size_t size, void* context) { furi_assert(context); PipeSide* pipe = context; while(size) { - size_t sent = pipe_send(pipe, data, size, FuriWaitForever); + size_t sent = pipe_send(pipe, data, size, pipe->stdout_timeout); + if(!sent) break; data += sent; size -= sent; } @@ -118,6 +122,11 @@ void pipe_install_as_stdio(PipeSide* pipe) { furi_thread_set_stdin_callback(_pipe_stdin_cb, pipe); } +void pipe_set_stdout_timeout(PipeSide* pipe, FuriWait timeout) { + furi_check(pipe); + pipe->stdout_timeout = timeout; +} + size_t pipe_receive(PipeSide* pipe, void* data, size_t length, FuriWait timeout) { furi_check(pipe); return furi_stream_buffer_receive(pipe->receiving, data, length, timeout); diff --git a/lib/toolbox/pipe.h b/lib/toolbox/pipe.h index df75f4c48de..c05e60d0c52 100644 --- a/lib/toolbox/pipe.h +++ b/lib/toolbox/pipe.h @@ -147,6 +147,16 @@ void pipe_free(PipeSide* pipe); */ void pipe_install_as_stdio(PipeSide* pipe); +/** + * @brief Sets the timeout for `stdout` write operations + * + * @note This value is set to `FuriWaitForever` when the pipe is created + * + * @param [in] pipe Pipe side to set the timeout of + * @param [in] timeout Timeout value in ticks + */ +void pipe_set_stdout_timeout(PipeSide* pipe, FuriWait timeout); + /** * @brief Receives data from the pipe. * diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 4aeef3c9de3..3da638149ef 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,81.1,, +Version,+,82.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, @@ -1666,8 +1666,8 @@ Function,+,furi_thread_get_return_code,int32_t,FuriThread* Function,+,furi_thread_get_signal_callback,FuriThreadSignalCallback,const FuriThread* Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId Function,+,furi_thread_get_state,FuriThreadState,FuriThread* -Function,+,furi_thread_get_stdin_callback,FuriThreadStdinReadCallback, -Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, +Function,+,furi_thread_get_stdin_callback,void,"FuriThreadStdinReadCallback*, void**" +Function,+,furi_thread_get_stdout_callback,void,"FuriThreadStdoutWriteCallback*, void**" Function,+,furi_thread_is_suspended,_Bool,FuriThreadId Function,+,furi_thread_join,_Bool,FuriThread* Function,+,furi_thread_list_alloc,FuriThreadList*, @@ -2333,6 +2333,7 @@ Function,+,pipe_set_broken_callback,void,"PipeSide*, PipeSideBrokenCallback, Fur Function,+,pipe_set_callback_context,void,"PipeSide*, void*" Function,+,pipe_set_data_arrived_callback,void,"PipeSide*, PipeSideDataArrivedCallback, FuriEventLoopEvent" Function,+,pipe_set_space_freed_callback,void,"PipeSide*, PipeSideSpaceFreedCallback, FuriEventLoopEvent" +Function,+,pipe_set_stdout_timeout,void,"PipeSide*, FuriWait" Function,+,pipe_spaces_available,size_t,PipeSide* Function,+,pipe_state,PipeState,PipeSide* Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 14eaa3142f0..9a87f08d430 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,81.1,, +Version,+,82.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -1886,8 +1886,8 @@ Function,+,furi_thread_get_return_code,int32_t,FuriThread* Function,+,furi_thread_get_signal_callback,FuriThreadSignalCallback,const FuriThread* Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId Function,+,furi_thread_get_state,FuriThreadState,FuriThread* -Function,+,furi_thread_get_stdin_callback,FuriThreadStdinReadCallback, -Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, +Function,+,furi_thread_get_stdin_callback,void,"FuriThreadStdinReadCallback*, void**" +Function,+,furi_thread_get_stdout_callback,void,"FuriThreadStdoutWriteCallback*, void**" Function,+,furi_thread_is_suspended,_Bool,FuriThreadId Function,+,furi_thread_join,_Bool,FuriThread* Function,+,furi_thread_list_alloc,FuriThreadList*, @@ -2970,6 +2970,7 @@ Function,+,pipe_set_broken_callback,void,"PipeSide*, PipeSideBrokenCallback, Fur Function,+,pipe_set_callback_context,void,"PipeSide*, void*" Function,+,pipe_set_data_arrived_callback,void,"PipeSide*, PipeSideDataArrivedCallback, FuriEventLoopEvent" Function,+,pipe_set_space_freed_callback,void,"PipeSide*, PipeSideSpaceFreedCallback, FuriEventLoopEvent" +Function,+,pipe_set_stdout_timeout,void,"PipeSide*, FuriWait" Function,+,pipe_spaces_available,size_t,PipeSide* Function,+,pipe_state,PipeState,PipeSide* Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*"