From d7c930605eb50fe47733241ffca4d61641df4e28 Mon Sep 17 00:00:00 2001 From: Katze719 Date: Fri, 11 Jul 2025 19:21:33 +0200 Subject: [PATCH 01/18] Add sequential execution support for serial interface functions - Introduced a new dispatch mechanism for executing serial interface functions sequentially across threads, ensuring that calls are processed in the order they are made. - Added corresponding sequential variants for multiple serial functions, including `serialAbortRead`, `serialRead`, `serialWrite`, and others, to enhance thread safety and execution consistency. - Updated the `serial.h` header to include the new sequential interface headers. This update improves the robustness of the serial API by providing a clear and reliable way to handle function calls in a sequential manner. --- include/cpp_core/detail/sequential_dispatch.h | 115 ++++++++++++++++++ include/cpp_core/interface/sequential/all.h | 24 ++++ .../sequential/get_version_sequential.h | 23 ++++ .../sequential/serial_abort_read_sequential.h | 26 ++++ .../serial_abort_write_sequential.h | 26 ++++ .../serial_clear_buffer_in_sequential.h | 26 ++++ .../serial_clear_buffer_out_sequential.h | 26 ++++ .../sequential/serial_close_sequential.h | 26 ++++ .../sequential/serial_drain_sequential.h | 26 ++++ .../serial_get_ports_info_sequential.h | 35 ++++++ .../serial_in_bytes_total_sequential.h | 26 ++++ .../serial_in_bytes_waiting_sequential.h | 26 ++++ .../sequential/serial_open_sequential.h | 32 +++++ .../serial_out_bytes_total_sequential.h | 26 ++++ .../serial_out_bytes_waiting_sequential.h | 26 ++++ .../sequential/serial_read_line_sequential.h | 32 +++++ .../sequential/serial_read_sequential.h | 32 +++++ .../serial_read_until_sequence_sequential.h | 35 ++++++ .../sequential/serial_read_until_sequential.h | 33 +++++ .../serial_set_error_callback_sequential.h | 23 ++++ .../serial_set_read_callback_sequential.h | 22 ++++ .../serial_set_write_callback_sequential.h | 22 ++++ .../sequential/serial_write_sequential.h | 32 +++++ include/cpp_core/serial.h | 1 + 24 files changed, 721 insertions(+) create mode 100644 include/cpp_core/detail/sequential_dispatch.h create mode 100644 include/cpp_core/interface/sequential/all.h create mode 100644 include/cpp_core/interface/sequential/get_version_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_abort_read_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_abort_write_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_close_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_drain_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_get_ports_info_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_open_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_read_line_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_read_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_read_until_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_set_error_callback_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_set_read_callback_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_set_write_callback_sequential.h create mode 100644 include/cpp_core/interface/sequential/serial_write_sequential.h diff --git a/include/cpp_core/detail/sequential_dispatch.h b/include/cpp_core/detail/sequential_dispatch.h new file mode 100644 index 0000000..5763116 --- /dev/null +++ b/include/cpp_core/detail/sequential_dispatch.h @@ -0,0 +1,115 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cpp_core::detail::seq +{ + +struct DispatchState +{ + std::mutex mtx; + std::condition_variable cv; + std::queue> queue; + std::once_flag worker_started; +}; + +/** + * @brief Access the global dispatcher state (singleton). + * + * Implements the C++11 *Meyers-Singleton* idiom: the static local variable is + * initialised on first use in a thread-safe manner and subsequently reused. + * This ensures exactly one queue / mutex / condition_variable exists per + * process while avoiding the static-initialisation-order problem. + * + * @return Reference to the sole ::DispatchState instance. + */ +inline auto state() -> DispatchState & +{ + static DispatchState instance; + return instance; +} + +/** + * @brief Worker thread main loop. + * + * Waits for new jobs in `state().queue` and executes them sequentially. + * The loop never terminates, the thread lives for the entire lifetime of + * the process. + */ +inline void worker() +{ + for (;;) + { + std::function job; + { + std::unique_lock lock(state().mtx); + state().cv.wait(lock, [] { return !state().queue.empty(); }); + job = std::move(state().queue.front()); + state().queue.pop(); + } + job(); + } +} + +/** + * @brief Starts the background worker thread (called once). + * + * Invoked internally by ::call via `std::call_once`. On the first invocation a + * detached thread is spawned; subsequent calls are no-ops. + */ +inline void startWorker() +{ + std::thread(worker).detach(); +} + +/** + * @brief Execute a callable sequentially on the background thread. + * + * The function/lambda is enqueued in FIFO order. The calling thread then + * blocks until completion and returns the result (or propagates an exception). + * + * Steps: + * 1. Wrap the callable in `std::packaged_task` to obtain an associated + * `std::future`. + * 2. Ensure the worker thread is running (`std::call_once`). + * 3. Push a copyable thunk into the queue and wake the worker via + * `notify_one()`. + * 4. Wait on `future.get()` for completion and forward the return value. + * + * @tparam FunctionT Callable type with no parameters. + * @param function Function object to be executed sequentially. + * @return The callable's return value (or `void`). + */ +template auto call(FunctionT &&function) -> decltype(function()) +{ + using FunctionReturnT = decltype(function()); + + auto task_ptr = std::make_shared>(std::forward(function)); + auto future = task_ptr->get_future(); + + std::call_once(state().worker_started, startWorker); + + { + std::lock_guard lock(state().mtx); + state().queue.emplace([task_ptr]() { (*task_ptr)(); }); + } + state().cv.notify_one(); + + if constexpr (std::is_void_v) + { + future.get(); + } + else + { + return future.get(); + } +} + +} // namespace cpp_core::detail::seq diff --git a/include/cpp_core/interface/sequential/all.h b/include/cpp_core/interface/sequential/all.h new file mode 100644 index 0000000..890437e --- /dev/null +++ b/include/cpp_core/interface/sequential/all.h @@ -0,0 +1,24 @@ +#pragma once + +// Aggregates all *Sequential headers +#include "get_version_sequential.h" +#include "serial_abort_read_sequential.h" +#include "serial_abort_write_sequential.h" +#include "serial_clear_buffer_in_sequential.h" +#include "serial_clear_buffer_out_sequential.h" +#include "serial_close_sequential.h" +#include "serial_drain_sequential.h" +#include "serial_get_ports_info_sequential.h" +#include "serial_in_bytes_total_sequential.h" +#include "serial_in_bytes_waiting_sequential.h" +#include "serial_open_sequential.h" +#include "serial_out_bytes_total_sequential.h" +#include "serial_out_bytes_waiting_sequential.h" +#include "serial_read_line_sequential.h" +#include "serial_read_sequential.h" +#include "serial_read_until_sequence_sequential.h" +#include "serial_read_until_sequential.h" +#include "serial_set_error_callback_sequential.h" +#include "serial_set_read_callback_sequential.h" +#include "serial_set_write_callback_sequential.h" +#include "serial_write_sequential.h" diff --git a/include/cpp_core/interface/sequential/get_version_sequential.h b/include/cpp_core/interface/sequential/get_version_sequential.h new file mode 100644 index 0000000..30af24c --- /dev/null +++ b/include/cpp_core/interface/sequential/get_version_sequential.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../version.h" +#include "../get_version.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc getVersion + * @note Sequential variant: call executes strictly in the order it was enqueued across threads. + */ + inline MODULE_API void getVersionSequential(cpp_core::Version *out) + { + cpp_core::detail::seq::call([=] { getVersion(out); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_abort_read_sequential.h b/include/cpp_core/interface/sequential/serial_abort_read_sequential.h new file mode 100644 index 0000000..5d52658 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_abort_read_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_abort_read.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialAbortRead + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialAbortReadSequential( + int64_t handle, + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { return serialAbortRead(handle, error_callback); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_abort_write_sequential.h b/include/cpp_core/interface/sequential/serial_abort_write_sequential.h new file mode 100644 index 0000000..8c54637 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_abort_write_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_abort_write.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialAbortWrite + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialAbortWriteSequential( + int64_t handle, + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { return serialAbortWrite(handle, error_callback); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h b/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h new file mode 100644 index 0000000..5d93d51 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_clear_buffer_in.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialClearBufferIn + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialClearBufferInSequential( + int64_t handle, + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { return serialClearBufferIn(handle, error_callback); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h b/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h new file mode 100644 index 0000000..a525903 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_clear_buffer_out.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialClearBufferOut + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialClearBufferOutSequential( + int64_t handle, + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { return serialClearBufferOut(handle, error_callback); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_close_sequential.h b/include/cpp_core/interface/sequential/serial_close_sequential.h new file mode 100644 index 0000000..088cf6c --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_close_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_close.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialClose + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialCloseSequential( + int64_t handle, + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { return serialClose(handle, error_callback); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_drain_sequential.h b/include/cpp_core/interface/sequential/serial_drain_sequential.h new file mode 100644 index 0000000..6e24a6b --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_drain_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_drain.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialDrain + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialDrainSequential( + int64_t handle, + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { return serialDrain(handle, error_callback); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_get_ports_info_sequential.h b/include/cpp_core/interface/sequential/serial_get_ports_info_sequential.h new file mode 100644 index 0000000..5927ffe --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_get_ports_info_sequential.h @@ -0,0 +1,35 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_get_ports_info.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialGetPortsInfo + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialGetPortsInfoSequential( + void (*callback_fn)( + const char *port, + const char *path, + const char *manufacturer, + const char *serial_number, + const char *pnp_id, + const char *location_id, + const char *product_id, + const char *vendor_id + ), + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { return serialGetPortsInfo(callback_fn, error_callback); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h b/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h new file mode 100644 index 0000000..cdb934e --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_in_bytes_total.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialInBytesTotal + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialInBytesTotalSequential( + int64_t handle, + ErrorCallbackT error_callback = nullptr + ) -> int64_t + { + return cpp_core::detail::seq::call([=] { return serialInBytesTotal(handle, error_callback); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h b/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h new file mode 100644 index 0000000..6fa7b21 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_in_bytes_waiting.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialInBytesWaiting + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialInBytesWaitingSequential( + int64_t handle, + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { return serialInBytesWaiting(handle, error_callback); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_open_sequential.h b/include/cpp_core/interface/sequential/serial_open_sequential.h new file mode 100644 index 0000000..a418cc3 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_open_sequential.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_open.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialOpen + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialOpenSequential( + void *port, + int baudrate, + int data_bits, + int parity = 0, + int stop_bits = 0, + ErrorCallbackT error_callback = nullptr + ) -> intptr_t + { + return cpp_core::detail::seq::call([=] { + return serialOpen(port, baudrate, data_bits, parity, stop_bits, error_callback); + }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h b/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h new file mode 100644 index 0000000..e67cb9e --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_out_bytes_total.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialOutBytesTotal + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialOutBytesTotalSequential( + int64_t handle, + ErrorCallbackT error_callback = nullptr + ) -> int64_t + { + return cpp_core::detail::seq::call([=] { return serialOutBytesTotal(handle, error_callback); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h b/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h new file mode 100644 index 0000000..8e9be41 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_out_bytes_waiting.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialOutBytesWaiting + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialOutBytesWaitingSequential( + int64_t handle, + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { return serialOutBytesWaiting(handle, error_callback); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_read_line_sequential.h b/include/cpp_core/interface/sequential/serial_read_line_sequential.h new file mode 100644 index 0000000..935ea23 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_read_line_sequential.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_read_line.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialReadLine + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialReadLineSequential( + int64_t handle, + void *buffer, + int buffer_size, + int timeout_ms, + int multiplier, + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { + return serialReadLine(handle, buffer, buffer_size, timeout_ms, multiplier, error_callback); + }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_read_sequential.h b/include/cpp_core/interface/sequential/serial_read_sequential.h new file mode 100644 index 0000000..a66ee9c --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_read_sequential.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_read.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialRead + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialReadSequential( + int64_t handle, + void *buffer, + int buffer_size, + int timeout_ms, + int multiplier, + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { + return serialRead(handle, buffer, buffer_size, timeout_ms, multiplier, error_callback); + }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h b/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h new file mode 100644 index 0000000..282e95c --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h @@ -0,0 +1,35 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_read_until_sequence.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialReadUntilSequence + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialReadUntilSequenceSequential( + int64_t handle, + void *buffer, + int buffer_size, + int timeout_ms, + int multiplier, + void *sequence, + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { + return serialReadUntilSequence( + handle, buffer, buffer_size, timeout_ms, multiplier, sequence, error_callback + ); + }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_read_until_sequential.h b/include/cpp_core/interface/sequential/serial_read_until_sequential.h new file mode 100644 index 0000000..ec6a60f --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_read_until_sequential.h @@ -0,0 +1,33 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_read_until.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialReadUntil + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialReadUntilSequential( + int64_t handle, + void *buffer, + int buffer_size, + int timeout_ms, + int multiplier, + void *until_char, + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { + return serialReadUntil(handle, buffer, buffer_size, timeout_ms, multiplier, until_char, error_callback); + }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_set_error_callback_sequential.h b/include/cpp_core/interface/sequential/serial_set_error_callback_sequential.h new file mode 100644 index 0000000..4dc85bf --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_set_error_callback_sequential.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_set_error_callback.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialSetErrorCallback + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API void serialSetErrorCallbackSequential(ErrorCallbackT error_callback) + { + cpp_core::detail::seq::call([=] { serialSetErrorCallback(error_callback); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_set_read_callback_sequential.h b/include/cpp_core/interface/sequential/serial_set_read_callback_sequential.h new file mode 100644 index 0000000..4043b32 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_set_read_callback_sequential.h @@ -0,0 +1,22 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../serial_set_read_callback.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialSetReadCallback + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API void serialSetReadCallbackSequential(void (*callback_fn)(int bytes_read)) + { + cpp_core::detail::seq::call([=] { serialSetReadCallback(callback_fn); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_set_write_callback_sequential.h b/include/cpp_core/interface/sequential/serial_set_write_callback_sequential.h new file mode 100644 index 0000000..07b9506 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_set_write_callback_sequential.h @@ -0,0 +1,22 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../serial_set_write_callback.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialSetWriteCallback + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API void serialSetWriteCallbackSequential(void (*callback_fn)(int bytes_written)) + { + cpp_core::detail::seq::call([=] { serialSetWriteCallback(callback_fn); }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/interface/sequential/serial_write_sequential.h b/include/cpp_core/interface/sequential/serial_write_sequential.h new file mode 100644 index 0000000..6207834 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_write_sequential.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../error_callback.h" +#include "../serial_write.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @copydoc serialWrite + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. + */ + inline MODULE_API auto serialWriteSequential( + int64_t handle, + const void *buffer, + int buffer_size, + int timeout_ms, + int multiplier, + ErrorCallbackT error_callback = nullptr + ) -> int + { + return cpp_core::detail::seq::call([=] { + return serialWrite(handle, buffer, buffer_size, timeout_ms, multiplier, error_callback); + }); + } + +#ifdef __cplusplus +} +#endif diff --git a/include/cpp_core/serial.h b/include/cpp_core/serial.h index e7e142a..2ed4530 100644 --- a/include/cpp_core/serial.h +++ b/include/cpp_core/serial.h @@ -5,6 +5,7 @@ // Aggregated interface headers #include "interface/get_version.h" +#include "interface/sequential/all.h" #include "interface/serial_abort_read.h" #include "interface/serial_abort_write.h" #include "interface/serial_clear_buffer_in.h" From f4cba5b95350e2ebbee213df4edbd250ce7cedfb Mon Sep 17 00:00:00 2001 From: Katze719 Date: Fri, 11 Jul 2025 19:28:54 +0200 Subject: [PATCH 02/18] Clarify documentation for sequential version retrieval - Updated the note in the `getVersionSequential` function documentation to specify that it guarantees execution in the exact order the calls were made across threads. This change enhances the clarity of the function's behavior in a multi-threaded context. --- include/cpp_core/interface/sequential/get_version_sequential.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cpp_core/interface/sequential/get_version_sequential.h b/include/cpp_core/interface/sequential/get_version_sequential.h index 30af24c..1d8f30c 100644 --- a/include/cpp_core/interface/sequential/get_version_sequential.h +++ b/include/cpp_core/interface/sequential/get_version_sequential.h @@ -11,7 +11,7 @@ extern "C" /** * @copydoc getVersion - * @note Sequential variant: call executes strictly in the order it was enqueued across threads. + * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. */ inline MODULE_API void getVersionSequential(cpp_core::Version *out) { From 6c01efca844bbdecec3ed44ef2d2474f495bfde3 Mon Sep 17 00:00:00 2001 From: Katze719 Date: Fri, 11 Jul 2025 19:51:38 +0200 Subject: [PATCH 03/18] Add Doxygen configuration and documentation workflow - Introduced a new Doxyfile for generating documentation for the cpp-core project, specifying project details and output settings. - Removed the outdated GitHub Actions workflow for building WASM binaries. - Added a new GitHub Actions workflow for generating and deploying Doxygen documentation to GitHub Pages, including steps for installation and documentation generation. These changes enhance project documentation and streamline the deployment process for generated docs. --- .github/workflows/build.yml | 40 ---------------------------- .github/workflows/doxygen.yml | 49 +++++++++++++++++++++++++++++++++++ Doxyfile | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 40 deletions(-) delete mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/doxygen.yml create mode 100644 Doxyfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index e13cbeb..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: 'Build WASM binary' - -on: - push: - branches: - - 'main' - - paths: - - '.github/workflows/build.yml' - - workflow_dispatch: - inputs: - build: - description: 'Build WASM binary' - required: true - default: 'RELEASE' - type: choice - options: - - 'RELEASE' - - 'PRE_REPEASE' - - -env: - BUILD_TYPE: 'RELEASE' - -jobs: - build: - name: 'Build WASM binary' - runs-on: ubuntu-latest - - steps: - - name: 'Set BUILD_TYPE' - run: | - if ([ -n "${{ github.event.inputs.build }}" ]); then - echo "Using input value: ${{ github.event.inputs.build }}" - echo "::set-env name=BUILD_TYPE::${{ github.event.inputs.build }}" - else - echo "Using default value: ${{ env.BUILD_TYPE }}" - echo "::set-env name=BUILD_TYPE::${{ env.BUILD_TYPE }}" - fi diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml new file mode 100644 index 0000000..cb21fc9 --- /dev/null +++ b/.github/workflows/doxygen.yml @@ -0,0 +1,49 @@ +name: 'Generate & Deploy Doxygen Docs' + +on: + push: + branches: [ main ] + paths: + - 'include/**' + - 'Doxyfile' + - '.github/workflows/doxygen.yml' + workflow_dispatch: + +# Required for GitHub Pages deployment +permissions: + contents: write # to push gh-pages for classic page + pages: write # to deploy using actions/deploy-pages@v1 + id-token: write # to authenticate deployment + +jobs: + build-docs: + runs-on: ubuntu-latest + name: 'Build Doxygen HTML' + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Doxygen + Graphviz + run: | + sudo apt-get update -qq + sudo apt-get install -y doxygen graphviz + + - name: Generate documentation + run: | + doxygen -v + doxygen Doxyfile + + # Upload the generated site as an artifact for the deploy job + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v1 + with: + path: docs/html + + deploy: + needs: build-docs + runs-on: ubuntu-latest + name: 'Deploy to GitHub Pages' + steps: + - name: Deploy + id: deployment + uses: actions/deploy-pages@v1 diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..7dd8112 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,45 @@ +#-------------------------------------------------------------------------- +# Doxyfile – generated for cpp-core +#-------------------------------------------------------------------------- + +# Project related -------------------------------------------------------- +PROJECT_NAME = "cpp-core" +PROJECT_BRIEF = "Header-only C++ helper library" +PROJECT_NUMBER = "1.0" +OUTPUT_DIRECTORY = docs/html +CREATE_SUBDIRS = NO + +# Build options ---------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_ANON_NSPACES = YES + +# Source files ----------------------------------------------------------- +INPUT = include README.md LICENSE +FILE_PATTERNS = *.h *.hpp *.md +RECURSIVE = YES + +# HTML output ------------------------------------------------------------ +GENERATE_HTML = YES +HTML_OUTPUT = . +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 + +# Disable unwanted output ----------------------------------------------- +GENERATE_LATEX = NO +GENERATE_MAN = NO +GENERATE_RTF = NO +GENERATE_XML = NO + +# Diagrams -------------------------------------------------------------- +HAVE_DOT = YES +DOT_IMAGE_FORMAT = svg +CALL_GRAPH = YES +CALLER_GRAPH = YES + +# Misc ------------------------------------------------------------------ +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = include +GENERATE_TREEVIEW = YES From a4ec5c7c87d9fe85c49e5c8f962f844aab6aa09c Mon Sep 17 00:00:00 2001 From: Katze719 Date: Fri, 11 Jul 2025 19:52:23 +0200 Subject: [PATCH 04/18] test action --- .github/workflows/doxygen.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index cb21fc9..bf0be46 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -11,9 +11,9 @@ on: # Required for GitHub Pages deployment permissions: - contents: write # to push gh-pages for classic page - pages: write # to deploy using actions/deploy-pages@v1 - id-token: write # to authenticate deployment + contents: write + pages: write + id-token: write jobs: build-docs: @@ -33,9 +33,8 @@ jobs: doxygen -v doxygen Doxyfile - # Upload the generated site as an artifact for the deploy job - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 with: path: docs/html @@ -46,4 +45,4 @@ jobs: steps: - name: Deploy id: deployment - uses: actions/deploy-pages@v1 + uses: actions/deploy-pages@v4 From 297ae7011484188e2dcb278246ead5c85e2a80b2 Mon Sep 17 00:00:00 2001 From: Katze719 Date: Sat, 12 Jul 2025 01:27:51 +0200 Subject: [PATCH 05/18] Refactor mutex and condition variable naming in sequential dispatch - Renamed `mtx` to `mutex` and `cv` to `condition_variable` in the `DispatchState` struct for improved clarity and consistency. - Updated corresponding references in the worker function and call method to reflect the new naming conventions. These changes enhance code readability and maintainability in the sequential dispatch implementation. --- include/cpp_core/detail/sequential_dispatch.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/cpp_core/detail/sequential_dispatch.h b/include/cpp_core/detail/sequential_dispatch.h index 5763116..df2ec8d 100644 --- a/include/cpp_core/detail/sequential_dispatch.h +++ b/include/cpp_core/detail/sequential_dispatch.h @@ -14,8 +14,8 @@ namespace cpp_core::detail::seq struct DispatchState { - std::mutex mtx; - std::condition_variable cv; + std::mutex mutex; + std::condition_variable condition_variable; std::queue> queue; std::once_flag worker_started; }; @@ -49,8 +49,8 @@ inline void worker() { std::function job; { - std::unique_lock lock(state().mtx); - state().cv.wait(lock, [] { return !state().queue.empty(); }); + std::unique_lock lock(state().mutex); + state().condition_variable.wait(lock, [] { return !state().queue.empty(); }); job = std::move(state().queue.front()); state().queue.pop(); } @@ -97,10 +97,10 @@ template auto call(FunctionT &&function) -> decltype(functi std::call_once(state().worker_started, startWorker); { - std::lock_guard lock(state().mtx); + std::lock_guard lock(state().mutex); state().queue.emplace([task_ptr]() { (*task_ptr)(); }); } - state().cv.notify_one(); + state().condition_variable.notify_one(); if constexpr (std::is_void_v) { From d9283b12e8acf4830a186148ed5b290ad46fc086 Mon Sep 17 00:00:00 2001 From: Katze719 Date: Sat, 12 Jul 2025 21:22:38 +0200 Subject: [PATCH 06/18] Add support for per-handle queue mode in sequential dispatch - Introduced a new `setQueueMode` function to allow selection between global and per-handle queue modes for sequential execution. - Updated existing serial interface functions to utilize the new per-handle mode, ensuring that calls can be executed concurrently across different handles while maintaining order within the same handle. - Added a new header file for the `setQueueMode` function and updated the main sequential interface header to include it. These changes enhance the flexibility and performance of the serial API by enabling concurrent execution across multiple serial handles. --- include/cpp_core/detail/sequential_dispatch.h | 223 +++++++++++++++--- include/cpp_core/interface/sequential/all.h | 1 + .../sequential/serial_abort_read_sequential.h | 2 +- .../serial_abort_write_sequential.h | 2 +- .../serial_clear_buffer_in_sequential.h | 2 +- .../serial_clear_buffer_out_sequential.h | 2 +- .../sequential/serial_close_sequential.h | 2 +- .../sequential/serial_drain_sequential.h | 2 +- .../serial_in_bytes_total_sequential.h | 2 +- .../serial_in_bytes_waiting_sequential.h | 2 +- .../serial_out_bytes_total_sequential.h | 2 +- .../serial_out_bytes_waiting_sequential.h | 2 +- .../sequential/serial_read_line_sequential.h | 2 +- .../sequential/serial_read_sequential.h | 2 +- .../serial_read_until_sequence_sequential.h | 2 +- .../sequential/serial_read_until_sequential.h | 2 +- .../sequential/serial_write_sequential.h | 2 +- .../sequential/set_queue_mode_sequential.h | 29 +++ 18 files changed, 229 insertions(+), 54 deletions(-) create mode 100644 include/cpp_core/interface/sequential/set_queue_mode_sequential.h diff --git a/include/cpp_core/detail/sequential_dispatch.h b/include/cpp_core/detail/sequential_dispatch.h index df2ec8d..18f24a1 100644 --- a/include/cpp_core/detail/sequential_dispatch.h +++ b/include/cpp_core/detail/sequential_dispatch.h @@ -1,6 +1,8 @@ #pragma once +#include #include +#include #include #include #include @@ -8,65 +10,124 @@ #include #include #include +#include namespace cpp_core::detail::seq { struct DispatchState { - std::mutex mutex; - std::condition_variable condition_variable; + /** Mutex protecting queue + condition_variable. */ + std::mutex mutex; + + /** Condition variable to wake the worker thread when new jobs arrive. */ + std::condition_variable condition_variable; + + /** FIFO containing packaged thunks (jobs). */ std::queue> queue; - std::once_flag worker_started; + + /** Ensures the worker thread is launched exactly once. */ + std::once_flag worker_started; +}; + +enum class QueueMode +{ + kGlobal, + kPerHandle }; /** - * @brief Access the global dispatcher state (singleton). + * @brief Determines how sequential dispatching is applied. * - * Implements the C++11 *Meyers-Singleton* idiom: the static local variable is - * initialised on first use in a thread-safe manner and subsequently reused. - * This ensures exactly one queue / mutex / condition_variable exists per - * process while avoiding the static-initialisation-order problem. + * @li kGlobal — All API calls are executed through a single process-wide + * queue. Guarantees strict ordering across every handle + * (legacy behaviour). + * @li kPerHandle — Each serial handle owns its own queue/worker thread. Calls + * on different handles may run in parallel while calls on + * the same handle remain strictly ordered. + */ +// Holds the current mode – defaults to the former behaviour (global queue) +inline auto queueMode() -> std::atomic & +{ + static std::atomic mode{QueueMode::kGlobal}; + return mode; +} + +/** + * @brief Thread-safe accessor to the currently selected ::QueueMode. * - * @return Reference to the sole ::DispatchState instance. + * The `std::atomic` wrapper allows lock-free loads/stores from any thread. + * The variable is a Meyers-singleton to sidestep static initialisation order + * issues. + * + * @return Reference to the internal atomic holding the active mode. */ -inline auto state() -> DispatchState & +inline void setQueueMode(QueueMode mode) { - static DispatchState instance; - return instance; + queueMode().store(mode, std::memory_order_relaxed); +} + +/** + * @brief Retrieve the currently active sequential dispatch mode. + */ +inline auto getQueueMode() -> QueueMode +{ + return queueMode().load(std::memory_order_relaxed); +} + +namespace detail +{ + +/** + * @brief Global map that stores a dedicated ::DispatchState for each serial + * handle encountered during runtime. The map is lazily populated on + * first use of a handle. + */ +inline auto handleStates() -> std::unordered_map< + int64_t, + DispatchState> & +{ + static std::unordered_map states; + return states; +} + +/** + * @brief Mutex guarding access to ::handleStates() (adds/removes look-ups). + */ +inline auto handleStatesMutex() -> std::mutex & +{ + static std::mutex mutex; + return mutex; } /** - * @brief Worker thread main loop. + * @brief Get (or create) the ::DispatchState associated with @p handle. * - * Waits for new jobs in `state().queue` and executes them sequentially. - * The loop never terminates, the thread lives for the entire lifetime of - * the process. + * @param handle Serial handle used as key. + * @return Reference to the corresponding state object. */ -inline void worker() +inline auto stateForHandle(int64_t handle) -> DispatchState & { - for (;;) - { - std::function job; - { - std::unique_lock lock(state().mutex); - state().condition_variable.wait(lock, [] { return !state().queue.empty(); }); - job = std::move(state().queue.front()); - state().queue.pop(); - } - job(); - } + std::lock_guard lock(handleStatesMutex()); + return handleStates()[handle]; } +} // namespace detail + /** - * @brief Starts the background worker thread (called once). + * @brief Access the global dispatcher state (singleton). + * + * Implements the C++11 *Meyers-Singleton* idiom: the static local variable is + * initialised on first use in a thread-safe manner and subsequently reused. + * This ensures exactly one queue / mutex / condition_variable exists per + * process while avoiding the static-initialisation-order problem. * - * Invoked internally by ::call via `std::call_once`. On the first invocation a - * detached thread is spawned; subsequent calls are no-ops. + * @return Reference to the sole ::DispatchState instance. */ -inline void startWorker() +inline auto state() -> DispatchState & { - std::thread(worker).detach(); + static DispatchState instance; + return instance; } /** @@ -84,23 +145,107 @@ inline void startWorker() * 4. Wait on `future.get()` for completion and forward the return value. * * @tparam FunctionT Callable type with no parameters. - * @param function Function object to be executed sequentially. - * @return The callable's return value (or `void`). + * @param function Function object to be executed sequentially. + * @return The callable's return value (or `void`). */ template auto call(FunctionT &&function) -> decltype(function()) +{ + return executeInQueue(state(), std::forward(function)); +} + +/** + * @brief Execute @p function on the *handle-local* worker thread. + * + * Helper used by the public `call(handle, fn)` overload when + * ::QueueMode::kPerHandle is active. All semantics are identical to the global + * variant but the queue/worker are scoped to the given handle. + * + * @tparam FunctionT Callable type with no parameters. + * @param handle Serial handle used to select the queue. + * @param function Callable object to execute. + * @return Callable’s return value (or `void`). + */ +template +auto callPerHandle( + int64_t handle, + FunctionT &&function +) -> decltype(function()) +{ + return executeInQueue(detail::stateForHandle(handle), std::forward(function)); +} + +/** + * @brief Dispatch @p function via the global queue or per-handle queue + * depending on ::getQueueMode(). + * + * This is the entry point used by all *Sequential* API wrappers that accept + * a serial handle. + * + * @tparam FunctionT Callable type with no parameters. + * @param handle Serial handle identifying the per-handle queue. + * @param function Callable to execute. + * @return Callable’s return value (or `void`). + */ +template +auto call( + int64_t handle, + FunctionT &&function +) -> decltype(function()) +{ + if (getQueueMode() == QueueMode::kPerHandle) + { + return callPerHandle(handle, std::forward(function)); + } + return call(std::forward(function)); +} + +/** + * @brief Worker thread loop – processes @p state until program termination. + */ +inline void workerLoop(DispatchState &state) +{ + for (;;) + { + std::function job; + { + std::unique_lock lock(state.mutex); + state.condition_variable.wait(lock, [&state] { return !state.queue.empty(); }); + job = std::move(state.queue.front()); + state.queue.pop(); + } + job(); + } +} + +/** + * @brief Ensure a dedicated worker thread exists for the given @p state. + */ +inline void ensureWorkerRunning(DispatchState &state) +{ + std::call_once(state.worker_started, [&state] { std::thread([&state] { workerLoop(state); }).detach(); }); +} + +/** + * @brief Enqueue @p function in @p state and wait for completion. + */ +template +auto executeInQueue( + DispatchState &state, + FunctionT &&function +) -> decltype(function()) { using FunctionReturnT = decltype(function()); auto task_ptr = std::make_shared>(std::forward(function)); auto future = task_ptr->get_future(); - std::call_once(state().worker_started, startWorker); + ensureWorkerRunning(state); { - std::lock_guard lock(state().mutex); - state().queue.emplace([task_ptr]() { (*task_ptr)(); }); + std::lock_guard lock(state.mutex); + state.queue.emplace([task_ptr]() { (*task_ptr)(); }); } - state().condition_variable.notify_one(); + state.condition_variable.notify_one(); if constexpr (std::is_void_v) { diff --git a/include/cpp_core/interface/sequential/all.h b/include/cpp_core/interface/sequential/all.h index 890437e..84147a4 100644 --- a/include/cpp_core/interface/sequential/all.h +++ b/include/cpp_core/interface/sequential/all.h @@ -22,3 +22,4 @@ #include "serial_set_read_callback_sequential.h" #include "serial_set_write_callback_sequential.h" #include "serial_write_sequential.h" +#include "set_queue_mode_sequential.h" diff --git a/include/cpp_core/interface/sequential/serial_abort_read_sequential.h b/include/cpp_core/interface/sequential/serial_abort_read_sequential.h index 5d52658..c080270 100644 --- a/include/cpp_core/interface/sequential/serial_abort_read_sequential.h +++ b/include/cpp_core/interface/sequential/serial_abort_read_sequential.h @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call([=] { return serialAbortRead(handle, error_callback); }); + return cpp_core::detail::seq::call(handle, [=] { return serialAbortRead(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_abort_write_sequential.h b/include/cpp_core/interface/sequential/serial_abort_write_sequential.h index 8c54637..17a0f38 100644 --- a/include/cpp_core/interface/sequential/serial_abort_write_sequential.h +++ b/include/cpp_core/interface/sequential/serial_abort_write_sequential.h @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call([=] { return serialAbortWrite(handle, error_callback); }); + return cpp_core::detail::seq::call(handle, [=] { return serialAbortWrite(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h b/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h index 5d93d51..28d59c5 100644 --- a/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h +++ b/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call([=] { return serialClearBufferIn(handle, error_callback); }); + return cpp_core::detail::seq::call(handle, [=] { return serialClearBufferIn(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h b/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h index a525903..785a8aa 100644 --- a/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h +++ b/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call([=] { return serialClearBufferOut(handle, error_callback); }); + return cpp_core::detail::seq::call(handle, [=] { return serialClearBufferOut(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_close_sequential.h b/include/cpp_core/interface/sequential/serial_close_sequential.h index 088cf6c..9f38242 100644 --- a/include/cpp_core/interface/sequential/serial_close_sequential.h +++ b/include/cpp_core/interface/sequential/serial_close_sequential.h @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call([=] { return serialClose(handle, error_callback); }); + return cpp_core::detail::seq::call(handle, [=] { return serialClose(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_drain_sequential.h b/include/cpp_core/interface/sequential/serial_drain_sequential.h index 6e24a6b..9801115 100644 --- a/include/cpp_core/interface/sequential/serial_drain_sequential.h +++ b/include/cpp_core/interface/sequential/serial_drain_sequential.h @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call([=] { return serialDrain(handle, error_callback); }); + return cpp_core::detail::seq::call(handle, [=] { return serialDrain(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h b/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h index cdb934e..9b9469b 100644 --- a/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h +++ b/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int64_t { - return cpp_core::detail::seq::call([=] { return serialInBytesTotal(handle, error_callback); }); + return cpp_core::detail::seq::call(handle, [=] { return serialInBytesTotal(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h b/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h index 6fa7b21..093a4be 100644 --- a/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h +++ b/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call([=] { return serialInBytesWaiting(handle, error_callback); }); + return cpp_core::detail::seq::call(handle, [=] { return serialInBytesWaiting(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h b/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h index e67cb9e..66f151f 100644 --- a/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h +++ b/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int64_t { - return cpp_core::detail::seq::call([=] { return serialOutBytesTotal(handle, error_callback); }); + return cpp_core::detail::seq::call(handle, [=] { return serialOutBytesTotal(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h b/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h index 8e9be41..7f5b498 100644 --- a/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h +++ b/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call([=] { return serialOutBytesWaiting(handle, error_callback); }); + return cpp_core::detail::seq::call(handle, [=] { return serialOutBytesWaiting(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_read_line_sequential.h b/include/cpp_core/interface/sequential/serial_read_line_sequential.h index 935ea23..5697659 100644 --- a/include/cpp_core/interface/sequential/serial_read_line_sequential.h +++ b/include/cpp_core/interface/sequential/serial_read_line_sequential.h @@ -22,7 +22,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call([=] { + return cpp_core::detail::seq::call(handle, [=] { return serialReadLine(handle, buffer, buffer_size, timeout_ms, multiplier, error_callback); }); } diff --git a/include/cpp_core/interface/sequential/serial_read_sequential.h b/include/cpp_core/interface/sequential/serial_read_sequential.h index a66ee9c..37bb12e 100644 --- a/include/cpp_core/interface/sequential/serial_read_sequential.h +++ b/include/cpp_core/interface/sequential/serial_read_sequential.h @@ -22,7 +22,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call([=] { + return cpp_core::detail::seq::call(handle, [=] { return serialRead(handle, buffer, buffer_size, timeout_ms, multiplier, error_callback); }); } diff --git a/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h b/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h index 282e95c..683e3cd 100644 --- a/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h +++ b/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h @@ -23,7 +23,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call([=] { + return cpp_core::detail::seq::call(handle, [=] { return serialReadUntilSequence( handle, buffer, buffer_size, timeout_ms, multiplier, sequence, error_callback ); diff --git a/include/cpp_core/interface/sequential/serial_read_until_sequential.h b/include/cpp_core/interface/sequential/serial_read_until_sequential.h index ec6a60f..331a6cd 100644 --- a/include/cpp_core/interface/sequential/serial_read_until_sequential.h +++ b/include/cpp_core/interface/sequential/serial_read_until_sequential.h @@ -23,7 +23,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call([=] { + return cpp_core::detail::seq::call(handle, [=] { return serialReadUntil(handle, buffer, buffer_size, timeout_ms, multiplier, until_char, error_callback); }); } diff --git a/include/cpp_core/interface/sequential/serial_write_sequential.h b/include/cpp_core/interface/sequential/serial_write_sequential.h index 6207834..e8f07d5 100644 --- a/include/cpp_core/interface/sequential/serial_write_sequential.h +++ b/include/cpp_core/interface/sequential/serial_write_sequential.h @@ -22,7 +22,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call([=] { + return cpp_core::detail::seq::call(handle, [=] { return serialWrite(handle, buffer, buffer_size, timeout_ms, multiplier, error_callback); }); } diff --git a/include/cpp_core/interface/sequential/set_queue_mode_sequential.h b/include/cpp_core/interface/sequential/set_queue_mode_sequential.h new file mode 100644 index 0000000..c75b582 --- /dev/null +++ b/include/cpp_core/interface/sequential/set_queue_mode_sequential.h @@ -0,0 +1,29 @@ +#pragma once + +#include "../../detail/sequential_dispatch.h" +#include "../../module_api.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief Select the sequential execution mode. + * + * Passing `1` enables a dedicated queue per serial handle (concurrent + * execution across ports). Passing `0` restores the legacy behavior of a + * single global queue (strict sequential execution across all ports). + * + * @param per_handle Non-zero -> per-handle mode, 0 -> global queue. + */ + inline MODULE_API void serialSetSequentialQueueMode(int per_handle) + { + cpp_core::detail::seq::setQueueMode( + (per_handle != 0) ? cpp_core::detail::seq::QueueMode::kPerHandle : cpp_core::detail::seq::QueueMode::kGlobal + ); + } + +#ifdef __cplusplus +} // extern "C" +#endif From 23b87aa3ecdc634a77b1c10fa2ef25ba82d4b4e4 Mon Sep 17 00:00:00 2001 From: Katze719 Date: Sat, 12 Jul 2025 21:38:56 +0200 Subject: [PATCH 07/18] Enhance documentation for sequential dispatch functions - Updated comments and documentation in `sequential_dispatch.h` to clarify the behavior and usage of the sequential dispatching mechanism. - Improved descriptions for functions such as `queueMode`, `setQueueMode`, and `call`, emphasizing thread safety and the distinction between global and per-handle queue modes. - Added detailed explanations of the steps involved in executing callables and managing worker threads, enhancing overall clarity for future developers. These changes improve the comprehensibility of the sequential dispatch API, aiding developers in understanding its functionality and usage. --- include/cpp_core/detail/sequential_dispatch.h | 105 ++++++++++++------ 1 file changed, 69 insertions(+), 36 deletions(-) diff --git a/include/cpp_core/detail/sequential_dispatch.h b/include/cpp_core/detail/sequential_dispatch.h index 18f24a1..162e11b 100644 --- a/include/cpp_core/detail/sequential_dispatch.h +++ b/include/cpp_core/detail/sequential_dispatch.h @@ -37,16 +37,19 @@ enum class QueueMode }; /** - * @brief Determines how sequential dispatching is applied. + * @brief Thread-safe singleton storing the process-wide ::QueueMode flag. * - * @li kGlobal — All API calls are executed through a single process-wide - * queue. Guarantees strict ordering across every handle - * (legacy behaviour). - * @li kPerHandle — Each serial handle owns its own queue/worker thread. Calls - * on different handles may run in parallel while calls on - * the same handle remain strictly ordered. + * The underlying variable is declared as a local static `std::atomic` to avoid + * the static-initialisation-order fiasco. Accessing the flag via this + * function guarantees that the object is initialised on first use in a + * thread-safe manner (C++11 and later). + * + * The returned reference enables lock-free loads and stores from any thread. + * All public helpers such as ::setQueueMode() and ::getQueueMode() act on the + * object obtained here. + * + * @return Reference to the `std::atomic` that tracks the active sequential dispatch policy. */ -// Holds the current mode – defaults to the former behaviour (global queue) inline auto queueMode() -> std::atomic & { static std::atomic mode{QueueMode::kGlobal}; @@ -54,13 +57,13 @@ inline auto queueMode() -> std::atomic & } /** - * @brief Thread-safe accessor to the currently selected ::QueueMode. + * @brief Select the sequential dispatch policy at runtime. * - * The `std::atomic` wrapper allows lock-free loads/stores from any thread. - * The variable is a Meyers-singleton to sidestep static initialisation order - * issues. + * Performs a relaxed atomic store on the flag returned by ::queueMode(). The + * operation is lock-free and therefore safe to call from any thread at any + * time during program execution. * - * @return Reference to the internal atomic holding the active mode. + * @param mode The ::QueueMode to activate for subsequent sequential API calls. */ inline void setQueueMode(QueueMode mode) { @@ -79,9 +82,8 @@ namespace detail { /** - * @brief Global map that stores a dedicated ::DispatchState for each serial - * handle encountered during runtime. The map is lazily populated on - * first use of a handle. + * @brief Global map that stores a dedicated ::DispatchState for each serial handle encountered during runtime. The map + * is lazily populated on first use of a handle. */ inline auto handleStates() -> std::unordered_map< int64_t, @@ -131,22 +133,28 @@ inline auto state() -> DispatchState & } /** - * @brief Execute a callable sequentially on the background thread. + * @brief Execute a callable on the *process-global* sequential queue. * - * The function/lambda is enqueued in FIFO order. The calling thread then - * blocks until completion and returns the result (or propagates an exception). + * The callable is enqueued in FIFO order on the dispatcher returned by + * ::state(). The calling thread blocks until the job finishes and forwards + * the return value or rethrows any exception. + * + * This overload is primarily used by API wrappers that do not take an explicit + * serial handle. When ::QueueMode::kPerHandle is enabled the global queue + * continues to exist; calls routed through this function therefore still run + * sequentially with respect to one another but *not* with respect to jobs + * dispatched via the per-handle overload. * * Steps: * 1. Wrap the callable in `std::packaged_task` to obtain an associated * `std::future`. - * 2. Ensure the worker thread is running (`std::call_once`). - * 3. Push a copyable thunk into the queue and wake the worker via - * `notify_one()`. - * 4. Wait on `future.get()` for completion and forward the return value. + * 2. Ensure the background worker servicing the global queue is running. + * 3. Enqueue the packaged task and notify the worker. + * 4. Wait on the future and return (or propagate) the result. * - * @tparam FunctionT Callable type with no parameters. - * @param function Function object to be executed sequentially. - * @return The callable's return value (or `void`). + * @tparam FunctionT Zero-argument callable type. + * @param function Callable object to execute. + * @return Callable’s return value, or `void` if the callable is `void`-returning. */ template auto call(FunctionT &&function) -> decltype(function()) { @@ -175,16 +183,13 @@ auto callPerHandle( } /** - * @brief Dispatch @p function via the global queue or per-handle queue - * depending on ::getQueueMode(). + * @copydoc call(FunctionT &&) * - * This is the entry point used by all *Sequential* API wrappers that accept - * a serial handle. + * Dispatches the callable either through the global queue or, when + * ::QueueMode::kPerHandle is active, through the queue dedicated to + * @p handle. * - * @tparam FunctionT Callable type with no parameters. * @param handle Serial handle identifying the per-handle queue. - * @param function Callable to execute. - * @return Callable’s return value (or `void`). */ template auto call( @@ -200,7 +205,15 @@ auto call( } /** - * @brief Worker thread loop – processes @p state until program termination. + * @brief Worker thread loop. + * + * Processes jobs from @p state.queue until program termination. The function + * blocks inside `state.condition_variable.wait()` whenever the queue is empty + * and wakes up once new jobs become available. Each invocation of the + * returned thunk is executed in FIFO order and exceptions propagate to the + * worker thread (terminating the program if uncaught). + * + * @param state Dispatcher state whose queue and synchronisation primitives are serviced by this loop. */ inline void workerLoop(DispatchState &state) { @@ -218,7 +231,14 @@ inline void workerLoop(DispatchState &state) } /** - * @brief Ensure a dedicated worker thread exists for the given @p state. + * @brief Launch the background worker for @p state exactly once. + * + * Internally relies on `std::call_once` to guarantee that a single detached + * worker thread is created for a given ::DispatchState instance, independent + * of how many times this function is called or from which thread. If the + * worker is already running, the call becomes a no-op. + * + * @param state Dispatcher state that owns the queue/condition variable which the spawned worker operates on. */ inline void ensureWorkerRunning(DispatchState &state) { @@ -226,7 +246,20 @@ inline void ensureWorkerRunning(DispatchState &state) } /** - * @brief Enqueue @p function in @p state and wait for completion. + * @brief Execute a callable sequentially via the queue stored in @p state. + * + * The function performs the following steps: + * 1. Wrap the provided callable in `std::packaged_task` to obtain a future. + * 2. Ensure a worker thread is servicing the queue (lazy initialisation). + * 3. Enqueue a thunk that invokes the packaged task. + * 4. Notify the worker and block on the associated `std::future`. + * + * @tparam FunctionT Type of the zero-argument callable. + * @param state Dispatcher state backing the queue. + * @param function Callable object to execute. + * @return Callable’s return value if the callable returns a non-void type. `void` otherwise. + * + * @note Exceptions thrown by @p function are rethrown in the calling thread when `future.get()` is invoked. */ template auto executeInQueue( From 719cc5f1c040236a088422fd0e5172c435a5a3ab Mon Sep 17 00:00:00 2001 From: Katze719 Date: Sat, 12 Jul 2025 22:38:57 +0200 Subject: [PATCH 08/18] Refactor state and mutex variable names in sequential dispatch - Renamed the static variables in `handleStates` and `handleStatesMutex` from `states` and `mutex` to `instance` for improved clarity and consistency. - This change enhances code readability and aligns with the naming conventions established in previous commits. --- include/cpp_core/detail/sequential_dispatch.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/cpp_core/detail/sequential_dispatch.h b/include/cpp_core/detail/sequential_dispatch.h index 162e11b..bbb928e 100644 --- a/include/cpp_core/detail/sequential_dispatch.h +++ b/include/cpp_core/detail/sequential_dispatch.h @@ -89,8 +89,8 @@ inline auto handleStates() -> std::unordered_map< int64_t, DispatchState> & { - static std::unordered_map states; - return states; + static std::unordered_map instance; + return instance; } /** @@ -98,8 +98,8 @@ inline auto handleStates() -> std::unordered_map< */ inline auto handleStatesMutex() -> std::mutex & { - static std::mutex mutex; - return mutex; + static std::mutex instance; + return instance; } /** From 4aba221117175e0a4e4c532817b6b5d60edceb00 Mon Sep 17 00:00:00 2001 From: Katze719 <38188106+Katze719@users.noreply.github.com> Date: Mon, 14 Jul 2025 07:37:40 +0200 Subject: [PATCH 09/18] Apply suggestion from @Mqxx Co-authored-by: Mqx <62719703+Mqxx@users.noreply.github.com> --- include/cpp_core/detail/sequential_dispatch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cpp_core/detail/sequential_dispatch.h b/include/cpp_core/detail/sequential_dispatch.h index bbb928e..54878d8 100644 --- a/include/cpp_core/detail/sequential_dispatch.h +++ b/include/cpp_core/detail/sequential_dispatch.h @@ -26,7 +26,7 @@ struct DispatchState /** FIFO containing packaged thunks (jobs). */ std::queue> queue; - /** Ensures the worker thread is launched exactly once. */ + /** Indicates if the worker thread is already launched. */ std::once_flag worker_started; }; From af259f2c0a802f466a47e59708751d47c60340ab Mon Sep 17 00:00:00 2001 From: Katze719 Date: Mon, 14 Jul 2025 20:00:37 +0200 Subject: [PATCH 10/18] Remove deprecated sequential interface headers and related functions - Deleted several sequential interface headers, including `get_version_sequential.h`, `serial_get_ports_info_sequential.h`, `serial_open_sequential.h`, and others, to streamline the codebase and eliminate unused functionality. - Removed associated inline functions that provided sequential variants for various serial operations, enhancing code clarity and maintainability. - This cleanup aligns with recent changes to the sequential dispatch mechanism, focusing on a more efficient and cohesive API design. --- include/cpp_core/detail/sequential_dispatch.h | 131 ++---------------- include/cpp_core/interface/sequential/all.h | 7 - .../sequential/get_version_sequential.h | 23 --- .../serial_get_ports_info_sequential.h | 35 ----- .../sequential/serial_open_sequential.h | 32 ----- .../serial_set_error_callback_sequential.h | 23 --- .../serial_set_read_callback_sequential.h | 22 --- .../serial_set_write_callback_sequential.h | 22 --- .../sequential/set_queue_mode_sequential.h | 29 ---- 9 files changed, 8 insertions(+), 316 deletions(-) delete mode 100644 include/cpp_core/interface/sequential/get_version_sequential.h delete mode 100644 include/cpp_core/interface/sequential/serial_get_ports_info_sequential.h delete mode 100644 include/cpp_core/interface/sequential/serial_open_sequential.h delete mode 100644 include/cpp_core/interface/sequential/serial_set_error_callback_sequential.h delete mode 100644 include/cpp_core/interface/sequential/serial_set_read_callback_sequential.h delete mode 100644 include/cpp_core/interface/sequential/serial_set_write_callback_sequential.h delete mode 100644 include/cpp_core/interface/sequential/set_queue_mode_sequential.h diff --git a/include/cpp_core/detail/sequential_dispatch.h b/include/cpp_core/detail/sequential_dispatch.h index 54878d8..0a1b239 100644 --- a/include/cpp_core/detail/sequential_dispatch.h +++ b/include/cpp_core/detail/sequential_dispatch.h @@ -30,54 +30,6 @@ struct DispatchState std::once_flag worker_started; }; -enum class QueueMode -{ - kGlobal, - kPerHandle -}; - -/** - * @brief Thread-safe singleton storing the process-wide ::QueueMode flag. - * - * The underlying variable is declared as a local static `std::atomic` to avoid - * the static-initialisation-order fiasco. Accessing the flag via this - * function guarantees that the object is initialised on first use in a - * thread-safe manner (C++11 and later). - * - * The returned reference enables lock-free loads and stores from any thread. - * All public helpers such as ::setQueueMode() and ::getQueueMode() act on the - * object obtained here. - * - * @return Reference to the `std::atomic` that tracks the active sequential dispatch policy. - */ -inline auto queueMode() -> std::atomic & -{ - static std::atomic mode{QueueMode::kGlobal}; - return mode; -} - -/** - * @brief Select the sequential dispatch policy at runtime. - * - * Performs a relaxed atomic store on the flag returned by ::queueMode(). The - * operation is lock-free and therefore safe to call from any thread at any - * time during program execution. - * - * @param mode The ::QueueMode to activate for subsequent sequential API calls. - */ -inline void setQueueMode(QueueMode mode) -{ - queueMode().store(mode, std::memory_order_relaxed); -} - -/** - * @brief Retrieve the currently active sequential dispatch mode. - */ -inline auto getQueueMode() -> QueueMode -{ - return queueMode().load(std::memory_order_relaxed); -} - namespace detail { @@ -117,79 +69,16 @@ inline auto stateForHandle(int64_t handle) -> DispatchState & } // namespace detail /** - * @brief Access the global dispatcher state (singleton). + * @brief Execute @p function sequentially on the queue dedicated to @p handle. * - * Implements the C++11 *Meyers-Singleton* idiom: the static local variable is - * initialised on first use in a thread-safe manner and subsequently reused. - * This ensures exactly one queue / mutex / condition_variable exists per - * process while avoiding the static-initialisation-order problem. - * - * @return Reference to the sole ::DispatchState instance. - */ -inline auto state() -> DispatchState & -{ - static DispatchState instance; - return instance; -} - -/** - * @brief Execute a callable on the *process-global* sequential queue. - * - * The callable is enqueued in FIFO order on the dispatcher returned by - * ::state(). The calling thread blocks until the job finishes and forwards - * the return value or rethrows any exception. - * - * This overload is primarily used by API wrappers that do not take an explicit - * serial handle. When ::QueueMode::kPerHandle is enabled the global queue - * continues to exist; calls routed through this function therefore still run - * sequentially with respect to one another but *not* with respect to jobs - * dispatched via the per-handle overload. - * - * Steps: - * 1. Wrap the callable in `std::packaged_task` to obtain an associated - * `std::future`. - * 2. Ensure the background worker servicing the global queue is running. - * 3. Enqueue the packaged task and notify the worker. - * 4. Wait on the future and return (or propagate) the result. + * Each serial handle owns its own FIFO queue and background worker thread. + * Calls executed on the same handle are strictly ordered, while calls on + * different handles may run concurrently. * * @tparam FunctionT Zero-argument callable type. - * @param function Callable object to execute. - * @return Callable’s return value, or `void` if the callable is `void`-returning. - */ -template auto call(FunctionT &&function) -> decltype(function()) -{ - return executeInQueue(state(), std::forward(function)); -} - -/** - * @brief Execute @p function on the *handle-local* worker thread. - * - * Helper used by the public `call(handle, fn)` overload when - * ::QueueMode::kPerHandle is active. All semantics are identical to the global - * variant but the queue/worker are scoped to the given handle. - * - * @tparam FunctionT Callable type with no parameters. - * @param handle Serial handle used to select the queue. - * @param function Callable object to execute. - * @return Callable’s return value (or `void`). - */ -template -auto callPerHandle( - int64_t handle, - FunctionT &&function -) -> decltype(function()) -{ - return executeInQueue(detail::stateForHandle(handle), std::forward(function)); -} - -/** - * @copydoc call(FunctionT &&) - * - * Dispatches the callable either through the global queue or, when - * ::QueueMode::kPerHandle is active, through the queue dedicated to - * @p handle. - * - * @param handle Serial handle identifying the per-handle queue. + * @param handle Serial handle identifying the queue/worker. + * @param function Callable object to execute. + * @return Callable’s return value (or `void`). */ template auto call( @@ -197,11 +86,7 @@ auto call( FunctionT &&function ) -> decltype(function()) { - if (getQueueMode() == QueueMode::kPerHandle) - { - return callPerHandle(handle, std::forward(function)); - } - return call(std::forward(function)); + return executeInQueue(detail::stateForHandle(handle), std::forward(function)); } /** diff --git a/include/cpp_core/interface/sequential/all.h b/include/cpp_core/interface/sequential/all.h index 84147a4..7180876 100644 --- a/include/cpp_core/interface/sequential/all.h +++ b/include/cpp_core/interface/sequential/all.h @@ -1,25 +1,18 @@ #pragma once // Aggregates all *Sequential headers -#include "get_version_sequential.h" #include "serial_abort_read_sequential.h" #include "serial_abort_write_sequential.h" #include "serial_clear_buffer_in_sequential.h" #include "serial_clear_buffer_out_sequential.h" #include "serial_close_sequential.h" #include "serial_drain_sequential.h" -#include "serial_get_ports_info_sequential.h" #include "serial_in_bytes_total_sequential.h" #include "serial_in_bytes_waiting_sequential.h" -#include "serial_open_sequential.h" #include "serial_out_bytes_total_sequential.h" #include "serial_out_bytes_waiting_sequential.h" #include "serial_read_line_sequential.h" #include "serial_read_sequential.h" #include "serial_read_until_sequence_sequential.h" #include "serial_read_until_sequential.h" -#include "serial_set_error_callback_sequential.h" -#include "serial_set_read_callback_sequential.h" -#include "serial_set_write_callback_sequential.h" #include "serial_write_sequential.h" -#include "set_queue_mode_sequential.h" diff --git a/include/cpp_core/interface/sequential/get_version_sequential.h b/include/cpp_core/interface/sequential/get_version_sequential.h deleted file mode 100644 index 1d8f30c..0000000 --- a/include/cpp_core/interface/sequential/get_version_sequential.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "../../detail/sequential_dispatch.h" -#include "../../version.h" -#include "../get_version.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @copydoc getVersion - * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. - */ - inline MODULE_API void getVersionSequential(cpp_core::Version *out) - { - cpp_core::detail::seq::call([=] { getVersion(out); }); - } - -#ifdef __cplusplus -} -#endif diff --git a/include/cpp_core/interface/sequential/serial_get_ports_info_sequential.h b/include/cpp_core/interface/sequential/serial_get_ports_info_sequential.h deleted file mode 100644 index 5927ffe..0000000 --- a/include/cpp_core/interface/sequential/serial_get_ports_info_sequential.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "../../detail/sequential_dispatch.h" -#include "../../error_callback.h" -#include "../serial_get_ports_info.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @copydoc serialGetPortsInfo - * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. - */ - inline MODULE_API auto serialGetPortsInfoSequential( - void (*callback_fn)( - const char *port, - const char *path, - const char *manufacturer, - const char *serial_number, - const char *pnp_id, - const char *location_id, - const char *product_id, - const char *vendor_id - ), - ErrorCallbackT error_callback = nullptr - ) -> int - { - return cpp_core::detail::seq::call([=] { return serialGetPortsInfo(callback_fn, error_callback); }); - } - -#ifdef __cplusplus -} -#endif diff --git a/include/cpp_core/interface/sequential/serial_open_sequential.h b/include/cpp_core/interface/sequential/serial_open_sequential.h deleted file mode 100644 index a418cc3..0000000 --- a/include/cpp_core/interface/sequential/serial_open_sequential.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "../../detail/sequential_dispatch.h" -#include "../../error_callback.h" -#include "../serial_open.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @copydoc serialOpen - * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. - */ - inline MODULE_API auto serialOpenSequential( - void *port, - int baudrate, - int data_bits, - int parity = 0, - int stop_bits = 0, - ErrorCallbackT error_callback = nullptr - ) -> intptr_t - { - return cpp_core::detail::seq::call([=] { - return serialOpen(port, baudrate, data_bits, parity, stop_bits, error_callback); - }); - } - -#ifdef __cplusplus -} -#endif diff --git a/include/cpp_core/interface/sequential/serial_set_error_callback_sequential.h b/include/cpp_core/interface/sequential/serial_set_error_callback_sequential.h deleted file mode 100644 index 4dc85bf..0000000 --- a/include/cpp_core/interface/sequential/serial_set_error_callback_sequential.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "../../detail/sequential_dispatch.h" -#include "../../error_callback.h" -#include "../serial_set_error_callback.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @copydoc serialSetErrorCallback - * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. - */ - inline MODULE_API void serialSetErrorCallbackSequential(ErrorCallbackT error_callback) - { - cpp_core::detail::seq::call([=] { serialSetErrorCallback(error_callback); }); - } - -#ifdef __cplusplus -} -#endif diff --git a/include/cpp_core/interface/sequential/serial_set_read_callback_sequential.h b/include/cpp_core/interface/sequential/serial_set_read_callback_sequential.h deleted file mode 100644 index 4043b32..0000000 --- a/include/cpp_core/interface/sequential/serial_set_read_callback_sequential.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "../../detail/sequential_dispatch.h" -#include "../serial_set_read_callback.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @copydoc serialSetReadCallback - * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. - */ - inline MODULE_API void serialSetReadCallbackSequential(void (*callback_fn)(int bytes_read)) - { - cpp_core::detail::seq::call([=] { serialSetReadCallback(callback_fn); }); - } - -#ifdef __cplusplus -} -#endif diff --git a/include/cpp_core/interface/sequential/serial_set_write_callback_sequential.h b/include/cpp_core/interface/sequential/serial_set_write_callback_sequential.h deleted file mode 100644 index 07b9506..0000000 --- a/include/cpp_core/interface/sequential/serial_set_write_callback_sequential.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "../../detail/sequential_dispatch.h" -#include "../serial_set_write_callback.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @copydoc serialSetWriteCallback - * @note Sequential variant: guarantees execution in the exact order the calls were made across threads. - */ - inline MODULE_API void serialSetWriteCallbackSequential(void (*callback_fn)(int bytes_written)) - { - cpp_core::detail::seq::call([=] { serialSetWriteCallback(callback_fn); }); - } - -#ifdef __cplusplus -} -#endif diff --git a/include/cpp_core/interface/sequential/set_queue_mode_sequential.h b/include/cpp_core/interface/sequential/set_queue_mode_sequential.h deleted file mode 100644 index c75b582..0000000 --- a/include/cpp_core/interface/sequential/set_queue_mode_sequential.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "../../detail/sequential_dispatch.h" -#include "../../module_api.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Select the sequential execution mode. - * - * Passing `1` enables a dedicated queue per serial handle (concurrent - * execution across ports). Passing `0` restores the legacy behavior of a - * single global queue (strict sequential execution across all ports). - * - * @param per_handle Non-zero -> per-handle mode, 0 -> global queue. - */ - inline MODULE_API void serialSetSequentialQueueMode(int per_handle) - { - cpp_core::detail::seq::setQueueMode( - (per_handle != 0) ? cpp_core::detail::seq::QueueMode::kPerHandle : cpp_core::detail::seq::QueueMode::kGlobal - ); - } - -#ifdef __cplusplus -} // extern "C" -#endif From 870d0271dab0b913e72daa31c26973ca47b5176b Mon Sep 17 00:00:00 2001 From: Katze719 Date: Mon, 14 Jul 2025 20:05:16 +0200 Subject: [PATCH 11/18] Add sequential header aggregation for serial interface - Introduced a new header file `squential.h` that aggregates all sequential interface headers for improved organization and accessibility. - This change streamlines the inclusion of sequential functionalities, enhancing code maintainability and clarity for developers working with the serial interface. --- include/cpp_core/interface/sequential/{all.h => squential.h} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename include/cpp_core/interface/sequential/{all.h => squential.h} (100%) diff --git a/include/cpp_core/interface/sequential/all.h b/include/cpp_core/interface/sequential/squential.h similarity index 100% rename from include/cpp_core/interface/sequential/all.h rename to include/cpp_core/interface/sequential/squential.h From ed6e9606c1705a213d15d6e00db334b0bc974abb Mon Sep 17 00:00:00 2001 From: Katze719 Date: Mon, 14 Jul 2025 21:25:08 +0200 Subject: [PATCH 12/18] Enhance documentation in sequential_dispatch.h - Added return type descriptions for the `handleStates` and `handleStatesMutex` functions to clarify their purpose and usage. - Improved formatting of parameter descriptions in the `call` and `ensureWorkerRunning` functions for better readability. These updates aim to improve the comprehensibility of the sequential dispatch API documentation, aiding developers in understanding its functionality. --- include/cpp_core/detail/sequential_dispatch.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/cpp_core/detail/sequential_dispatch.h b/include/cpp_core/detail/sequential_dispatch.h index 0a1b239..7e6bde8 100644 --- a/include/cpp_core/detail/sequential_dispatch.h +++ b/include/cpp_core/detail/sequential_dispatch.h @@ -36,6 +36,7 @@ namespace detail /** * @brief Global map that stores a dedicated ::DispatchState for each serial handle encountered during runtime. The map * is lazily populated on first use of a handle. + * @return Reference to the global map instance mapping handles to their dispatch states */ inline auto handleStates() -> std::unordered_map< int64_t, @@ -47,6 +48,7 @@ inline auto handleStates() -> std::unordered_map< /** * @brief Mutex guarding access to ::handleStates() (adds/removes look-ups). + * @return Reference to the global mutex instance protecting handleStates() */ inline auto handleStatesMutex() -> std::mutex & { @@ -76,9 +78,9 @@ inline auto stateForHandle(int64_t handle) -> DispatchState & * different handles may run concurrently. * * @tparam FunctionT Zero-argument callable type. - * @param handle Serial handle identifying the queue/worker. - * @param function Callable object to execute. - * @return Callable’s return value (or `void`). + * @param handle Serial handle identifying the queue/worker. + * @param function Callable object to execute. + * @return Callable's return value (or `void`). */ template auto call( @@ -142,7 +144,7 @@ inline void ensureWorkerRunning(DispatchState &state) * @tparam FunctionT Type of the zero-argument callable. * @param state Dispatcher state backing the queue. * @param function Callable object to execute. - * @return Callable’s return value if the callable returns a non-void type. `void` otherwise. + * @return Callable's return value if the callable returns a non-void type. `void` otherwise. * * @note Exceptions thrown by @p function are rethrown in the calling thread when `future.get()` is invoked. */ From 653bedfec6a78d335887821600f72283b0d2b237 Mon Sep 17 00:00:00 2001 From: Katze719 Date: Mon, 14 Jul 2025 22:48:53 +0200 Subject: [PATCH 13/18] Update serial interface headers for improved organization - Replaced the inclusion of the deprecated `all.h` header with the new `sequential.h` header in `serial.h` to streamline header management. - Introduced `sequential.h`, which aggregates all sequential interface headers, enhancing code maintainability and accessibility for developers working with the serial interface. --- .../cpp_core/interface/sequential/{squential.h => sequential.h} | 0 include/cpp_core/serial.h | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename include/cpp_core/interface/sequential/{squential.h => sequential.h} (100%) diff --git a/include/cpp_core/interface/sequential/squential.h b/include/cpp_core/interface/sequential/sequential.h similarity index 100% rename from include/cpp_core/interface/sequential/squential.h rename to include/cpp_core/interface/sequential/sequential.h diff --git a/include/cpp_core/serial.h b/include/cpp_core/serial.h index 2ed4530..51d90cc 100644 --- a/include/cpp_core/serial.h +++ b/include/cpp_core/serial.h @@ -5,7 +5,7 @@ // Aggregated interface headers #include "interface/get_version.h" -#include "interface/sequential/all.h" +#include "interface/sequential/sequential.h" #include "interface/serial_abort_read.h" #include "interface/serial_abort_write.h" #include "interface/serial_clear_buffer_in.h" From 7adaed4f13a5c09dc200b340f9c89049e3963a28 Mon Sep 17 00:00:00 2001 From: Katze719 Date: Tue, 15 Jul 2025 19:00:05 +0200 Subject: [PATCH 14/18] Add serial_sequential header and refactor serial interface - Introduced a new `serial_sequential.h` header that aggregates all sequential interface headers, improving organization and accessibility. - Removed the deprecated `sequential.h` header from `serial.h` to streamline header management. - Updated several sequential interface files to include the new `internal/sequential_dispatch.h` for better encapsulation of dispatch logic. These changes enhance the maintainability and clarity of the serial interface, aligning with recent refactoring efforts. --- include/cpp_core.h | 1 + .../interface/sequential/sequential.h | 18 ------------ .../sequential/serial_abort_read_sequential.h | 4 +-- .../serial_abort_write_sequential.h | 4 +-- .../serial_clear_buffer_in_sequential.h | 4 +-- .../serial_clear_buffer_out_sequential.h | 4 +-- .../sequential/serial_close_sequential.h | 4 +-- .../sequential/serial_drain_sequential.h | 4 +-- .../serial_in_bytes_total_sequential.h | 4 +-- .../serial_in_bytes_waiting_sequential.h | 4 +-- .../serial_out_bytes_total_sequential.h | 4 +-- .../serial_out_bytes_waiting_sequential.h | 4 +-- .../sequential/serial_read_line_sequential.h | 4 +-- .../sequential/serial_read_sequential.h | 4 +-- .../serial_read_until_sequence_sequential.h | 4 +-- .../sequential/serial_read_until_sequential.h | 4 +-- .../sequential/serial_write_sequential.h | 4 +-- .../sequential_dispatch.h | 4 +-- include/cpp_core/serial.h | 1 - include/cpp_core/serial_sequential.h | 29 +++++++++++++++++++ 20 files changed, 62 insertions(+), 51 deletions(-) delete mode 100644 include/cpp_core/interface/sequential/sequential.h rename include/cpp_core/{detail => internal}/sequential_dispatch.h (98%) create mode 100644 include/cpp_core/serial_sequential.h diff --git a/include/cpp_core.h b/include/cpp_core.h index 1f50732..16d7573 100644 --- a/include/cpp_core.h +++ b/include/cpp_core.h @@ -14,5 +14,6 @@ #include "cpp_core/error_callback.h" #include "cpp_core/serial.h" +#include "cpp_core/serial_sequential.h" #include "cpp_core/status_codes.h" #include "cpp_core/version.h" diff --git a/include/cpp_core/interface/sequential/sequential.h b/include/cpp_core/interface/sequential/sequential.h deleted file mode 100644 index 7180876..0000000 --- a/include/cpp_core/interface/sequential/sequential.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -// Aggregates all *Sequential headers -#include "serial_abort_read_sequential.h" -#include "serial_abort_write_sequential.h" -#include "serial_clear_buffer_in_sequential.h" -#include "serial_clear_buffer_out_sequential.h" -#include "serial_close_sequential.h" -#include "serial_drain_sequential.h" -#include "serial_in_bytes_total_sequential.h" -#include "serial_in_bytes_waiting_sequential.h" -#include "serial_out_bytes_total_sequential.h" -#include "serial_out_bytes_waiting_sequential.h" -#include "serial_read_line_sequential.h" -#include "serial_read_sequential.h" -#include "serial_read_until_sequence_sequential.h" -#include "serial_read_until_sequential.h" -#include "serial_write_sequential.h" diff --git a/include/cpp_core/interface/sequential/serial_abort_read_sequential.h b/include/cpp_core/interface/sequential/serial_abort_read_sequential.h index c080270..e94ab3c 100644 --- a/include/cpp_core/interface/sequential/serial_abort_read_sequential.h +++ b/include/cpp_core/interface/sequential/serial_abort_read_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_abort_read.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call(handle, [=] { return serialAbortRead(handle, error_callback); }); + return cpp_core::internal::seq::call(handle, [=] { return serialAbortRead(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_abort_write_sequential.h b/include/cpp_core/interface/sequential/serial_abort_write_sequential.h index 17a0f38..9c22db6 100644 --- a/include/cpp_core/interface/sequential/serial_abort_write_sequential.h +++ b/include/cpp_core/interface/sequential/serial_abort_write_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_abort_write.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call(handle, [=] { return serialAbortWrite(handle, error_callback); }); + return cpp_core::internal::seq::call(handle, [=] { return serialAbortWrite(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h b/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h index 28d59c5..7d764c5 100644 --- a/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h +++ b/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_clear_buffer_in.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call(handle, [=] { return serialClearBufferIn(handle, error_callback); }); + return cpp_core::internal::seq::call(handle, [=] { return serialClearBufferIn(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h b/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h index 785a8aa..6c7a1c3 100644 --- a/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h +++ b/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_clear_buffer_out.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call(handle, [=] { return serialClearBufferOut(handle, error_callback); }); + return cpp_core::internal::seq::call(handle, [=] { return serialClearBufferOut(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_close_sequential.h b/include/cpp_core/interface/sequential/serial_close_sequential.h index 9f38242..cddcd1a 100644 --- a/include/cpp_core/interface/sequential/serial_close_sequential.h +++ b/include/cpp_core/interface/sequential/serial_close_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_close.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call(handle, [=] { return serialClose(handle, error_callback); }); + return cpp_core::internal::seq::call(handle, [=] { return serialClose(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_drain_sequential.h b/include/cpp_core/interface/sequential/serial_drain_sequential.h index 9801115..3386379 100644 --- a/include/cpp_core/interface/sequential/serial_drain_sequential.h +++ b/include/cpp_core/interface/sequential/serial_drain_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_drain.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call(handle, [=] { return serialDrain(handle, error_callback); }); + return cpp_core::internal::seq::call(handle, [=] { return serialDrain(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h b/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h index 9b9469b..853ea5d 100644 --- a/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h +++ b/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_in_bytes_total.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int64_t { - return cpp_core::detail::seq::call(handle, [=] { return serialInBytesTotal(handle, error_callback); }); + return cpp_core::internal::seq::call(handle, [=] { return serialInBytesTotal(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h b/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h index 093a4be..50d9fb3 100644 --- a/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h +++ b/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_in_bytes_waiting.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call(handle, [=] { return serialInBytesWaiting(handle, error_callback); }); + return cpp_core::internal::seq::call(handle, [=] { return serialInBytesWaiting(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h b/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h index 66f151f..655deb7 100644 --- a/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h +++ b/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_out_bytes_total.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int64_t { - return cpp_core::detail::seq::call(handle, [=] { return serialOutBytesTotal(handle, error_callback); }); + return cpp_core::internal::seq::call(handle, [=] { return serialOutBytesTotal(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h b/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h index 7f5b498..edbbd89 100644 --- a/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h +++ b/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_out_bytes_waiting.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call(handle, [=] { return serialOutBytesWaiting(handle, error_callback); }); + return cpp_core::internal::seq::call(handle, [=] { return serialOutBytesWaiting(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_read_line_sequential.h b/include/cpp_core/interface/sequential/serial_read_line_sequential.h index 5697659..4997234 100644 --- a/include/cpp_core/interface/sequential/serial_read_line_sequential.h +++ b/include/cpp_core/interface/sequential/serial_read_line_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_read_line.h" #ifdef __cplusplus @@ -22,7 +22,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call(handle, [=] { + return cpp_core::internal::seq::call(handle, [=] { return serialReadLine(handle, buffer, buffer_size, timeout_ms, multiplier, error_callback); }); } diff --git a/include/cpp_core/interface/sequential/serial_read_sequential.h b/include/cpp_core/interface/sequential/serial_read_sequential.h index 37bb12e..e98ece8 100644 --- a/include/cpp_core/interface/sequential/serial_read_sequential.h +++ b/include/cpp_core/interface/sequential/serial_read_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_read.h" #ifdef __cplusplus @@ -22,7 +22,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call(handle, [=] { + return cpp_core::internal::seq::call(handle, [=] { return serialRead(handle, buffer, buffer_size, timeout_ms, multiplier, error_callback); }); } diff --git a/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h b/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h index 683e3cd..9191734 100644 --- a/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h +++ b/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_read_until_sequence.h" #ifdef __cplusplus @@ -23,7 +23,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call(handle, [=] { + return cpp_core::internal::seq::call(handle, [=] { return serialReadUntilSequence( handle, buffer, buffer_size, timeout_ms, multiplier, sequence, error_callback ); diff --git a/include/cpp_core/interface/sequential/serial_read_until_sequential.h b/include/cpp_core/interface/sequential/serial_read_until_sequential.h index 331a6cd..281ba70 100644 --- a/include/cpp_core/interface/sequential/serial_read_until_sequential.h +++ b/include/cpp_core/interface/sequential/serial_read_until_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_read_until.h" #ifdef __cplusplus @@ -23,7 +23,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call(handle, [=] { + return cpp_core::internal::seq::call(handle, [=] { return serialReadUntil(handle, buffer, buffer_size, timeout_ms, multiplier, until_char, error_callback); }); } diff --git a/include/cpp_core/interface/sequential/serial_write_sequential.h b/include/cpp_core/interface/sequential/serial_write_sequential.h index e8f07d5..24c024e 100644 --- a/include/cpp_core/interface/sequential/serial_write_sequential.h +++ b/include/cpp_core/interface/sequential/serial_write_sequential.h @@ -1,7 +1,7 @@ #pragma once -#include "../../detail/sequential_dispatch.h" #include "../../error_callback.h" +#include "../../internal/sequential_dispatch.h" #include "../serial_write.h" #ifdef __cplusplus @@ -22,7 +22,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::detail::seq::call(handle, [=] { + return cpp_core::internal::seq::call(handle, [=] { return serialWrite(handle, buffer, buffer_size, timeout_ms, multiplier, error_callback); }); } diff --git a/include/cpp_core/detail/sequential_dispatch.h b/include/cpp_core/internal/sequential_dispatch.h similarity index 98% rename from include/cpp_core/detail/sequential_dispatch.h rename to include/cpp_core/internal/sequential_dispatch.h index 7e6bde8..6104cec 100644 --- a/include/cpp_core/detail/sequential_dispatch.h +++ b/include/cpp_core/internal/sequential_dispatch.h @@ -12,7 +12,7 @@ #include #include -namespace cpp_core::detail::seq +namespace cpp_core::internal::seq { struct DispatchState @@ -177,4 +177,4 @@ auto executeInQueue( } } -} // namespace cpp_core::detail::seq +} // namespace cpp_core::internal::seq diff --git a/include/cpp_core/serial.h b/include/cpp_core/serial.h index 51d90cc..e7e142a 100644 --- a/include/cpp_core/serial.h +++ b/include/cpp_core/serial.h @@ -5,7 +5,6 @@ // Aggregated interface headers #include "interface/get_version.h" -#include "interface/sequential/sequential.h" #include "interface/serial_abort_read.h" #include "interface/serial_abort_write.h" #include "interface/serial_clear_buffer_in.h" diff --git a/include/cpp_core/serial_sequential.h b/include/cpp_core/serial_sequential.h new file mode 100644 index 0000000..233546f --- /dev/null +++ b/include/cpp_core/serial_sequential.h @@ -0,0 +1,29 @@ +#pragma once + +#include "module_api.h" +#include "version.h" + +// Missing from the sequential interface +#include "interface/get_version.h" +#include "interface/serial_get_ports_info.h" +#include "interface/serial_open.h" +#include "interface/serial_set_error_callback.h" +#include "interface/serial_set_read_callback.h" +#include "interface/serial_set_write_callback.h" + +// Aggregates all *Sequential headers +#include "interface/sequential/serial_abort_read_sequential.h" +#include "interface/sequential/serial_abort_write_sequential.h" +#include "interface/sequential/serial_clear_buffer_in_sequential.h" +#include "interface/sequential/serial_clear_buffer_out_sequential.h" +#include "interface/sequential/serial_close_sequential.h" +#include "interface/sequential/serial_drain_sequential.h" +#include "interface/sequential/serial_in_bytes_total_sequential.h" +#include "interface/sequential/serial_in_bytes_waiting_sequential.h" +#include "interface/sequential/serial_out_bytes_total_sequential.h" +#include "interface/sequential/serial_out_bytes_waiting_sequential.h" +#include "interface/sequential/serial_read_line_sequential.h" +#include "interface/sequential/serial_read_sequential.h" +#include "interface/sequential/serial_read_until_sequence_sequential.h" +#include "interface/sequential/serial_read_until_sequential.h" +#include "interface/sequential/serial_write_sequential.h" From 4f1c41370eedf76f6fe1aa62630e9dfbc0937e3e Mon Sep 17 00:00:00 2001 From: Katze719 Date: Wed, 16 Jul 2025 21:18:04 +0200 Subject: [PATCH 15/18] Refactor sequential interface to use new call mechanism - Replaced instances of `sequential_dispatch.h` with `call.h` in multiple sequential interface headers to streamline the dispatch logic. - Updated function calls to utilize the new `cpp_core::internal::sequential::call` method, ensuring consistent handling of sequential execution across the interface. - Improved formatting in several function implementations for better readability. These changes enhance the maintainability and clarity of the serial interface, aligning with recent refactoring efforts. --- .../sequential/serial_abort_read_sequential.h | 4 +- .../serial_abort_write_sequential.h | 4 +- .../serial_clear_buffer_in_sequential.h | 6 +- .../serial_clear_buffer_out_sequential.h | 6 +- .../sequential/serial_close_sequential.h | 4 +- .../sequential/serial_drain_sequential.h | 4 +- .../serial_in_bytes_total_sequential.h | 4 +- .../serial_in_bytes_waiting_sequential.h | 6 +- .../serial_out_bytes_total_sequential.h | 6 +- .../serial_out_bytes_waiting_sequential.h | 6 +- .../sequential/serial_read_line_sequential.h | 4 +- .../sequential/serial_read_sequential.h | 4 +- .../serial_read_until_sequence_sequential.h | 4 +- .../sequential/serial_read_until_sequential.h | 4 +- .../sequential/serial_write_sequential.h | 4 +- include/cpp_core/internal/sequential/call.h | 32 ++++ .../internal/sequential/dispatch_state.h | 29 +++ .../sequential/ensure_worker_running.h | 23 +++ .../internal/sequential/execute_in_queue.h | 53 ++++++ .../internal/sequential/handle_states.h | 25 +++ .../internal/sequential/handle_states_mutex.h | 17 ++ .../internal/sequential/state_for_handle.h | 23 +++ .../internal/sequential/worker_loop.h | 34 ++++ .../cpp_core/internal/sequential_dispatch.h | 180 ------------------ 24 files changed, 276 insertions(+), 210 deletions(-) create mode 100644 include/cpp_core/internal/sequential/call.h create mode 100644 include/cpp_core/internal/sequential/dispatch_state.h create mode 100644 include/cpp_core/internal/sequential/ensure_worker_running.h create mode 100644 include/cpp_core/internal/sequential/execute_in_queue.h create mode 100644 include/cpp_core/internal/sequential/handle_states.h create mode 100644 include/cpp_core/internal/sequential/handle_states_mutex.h create mode 100644 include/cpp_core/internal/sequential/state_for_handle.h create mode 100644 include/cpp_core/internal/sequential/worker_loop.h delete mode 100644 include/cpp_core/internal/sequential_dispatch.h diff --git a/include/cpp_core/interface/sequential/serial_abort_read_sequential.h b/include/cpp_core/interface/sequential/serial_abort_read_sequential.h index e94ab3c..b9dd5a1 100644 --- a/include/cpp_core/interface/sequential/serial_abort_read_sequential.h +++ b/include/cpp_core/interface/sequential/serial_abort_read_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_abort_read.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::internal::seq::call(handle, [=] { return serialAbortRead(handle, error_callback); }); + return cpp_core::internal::sequential::call(handle, [=] { return serialAbortRead(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_abort_write_sequential.h b/include/cpp_core/interface/sequential/serial_abort_write_sequential.h index 9c22db6..c7e42cc 100644 --- a/include/cpp_core/interface/sequential/serial_abort_write_sequential.h +++ b/include/cpp_core/interface/sequential/serial_abort_write_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_abort_write.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::internal::seq::call(handle, [=] { return serialAbortWrite(handle, error_callback); }); + return cpp_core::internal::sequential::call(handle, [=] { return serialAbortWrite(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h b/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h index 7d764c5..6288caa 100644 --- a/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h +++ b/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_clear_buffer_in.h" #ifdef __cplusplus @@ -18,7 +18,9 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::internal::seq::call(handle, [=] { return serialClearBufferIn(handle, error_callback); }); + return cpp_core::internal::sequential::call(handle, [=] { + return serialClearBufferIn(handle, error_callback); + }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h b/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h index 6c7a1c3..3d58d98 100644 --- a/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h +++ b/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_clear_buffer_out.h" #ifdef __cplusplus @@ -18,7 +18,9 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::internal::seq::call(handle, [=] { return serialClearBufferOut(handle, error_callback); }); + return cpp_core::internal::sequential::call(handle, [=] { + return serialClearBufferOut(handle, error_callback); + }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_close_sequential.h b/include/cpp_core/interface/sequential/serial_close_sequential.h index cddcd1a..7c9aa0a 100644 --- a/include/cpp_core/interface/sequential/serial_close_sequential.h +++ b/include/cpp_core/interface/sequential/serial_close_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_close.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::internal::seq::call(handle, [=] { return serialClose(handle, error_callback); }); + return cpp_core::internal::sequential::call(handle, [=] { return serialClose(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_drain_sequential.h b/include/cpp_core/interface/sequential/serial_drain_sequential.h index 3386379..5216b39 100644 --- a/include/cpp_core/interface/sequential/serial_drain_sequential.h +++ b/include/cpp_core/interface/sequential/serial_drain_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_drain.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::internal::seq::call(handle, [=] { return serialDrain(handle, error_callback); }); + return cpp_core::internal::sequential::call(handle, [=] { return serialDrain(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h b/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h index 853ea5d..9f40ac7 100644 --- a/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h +++ b/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_in_bytes_total.h" #ifdef __cplusplus @@ -18,7 +18,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int64_t { - return cpp_core::internal::seq::call(handle, [=] { return serialInBytesTotal(handle, error_callback); }); + return cpp_core::internal::sequential::call(handle, [=] { return serialInBytesTotal(handle, error_callback); }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h b/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h index 50d9fb3..eb5ee6b 100644 --- a/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h +++ b/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_in_bytes_waiting.h" #ifdef __cplusplus @@ -18,7 +18,9 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::internal::seq::call(handle, [=] { return serialInBytesWaiting(handle, error_callback); }); + return cpp_core::internal::sequential::call(handle, [=] { + return serialInBytesWaiting(handle, error_callback); + }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h b/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h index 655deb7..6528871 100644 --- a/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h +++ b/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_out_bytes_total.h" #ifdef __cplusplus @@ -18,7 +18,9 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int64_t { - return cpp_core::internal::seq::call(handle, [=] { return serialOutBytesTotal(handle, error_callback); }); + return cpp_core::internal::sequential::call(handle, [=] { + return serialOutBytesTotal(handle, error_callback); + }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h b/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h index edbbd89..82abef2 100644 --- a/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h +++ b/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_out_bytes_waiting.h" #ifdef __cplusplus @@ -18,7 +18,9 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::internal::seq::call(handle, [=] { return serialOutBytesWaiting(handle, error_callback); }); + return cpp_core::internal::sequential::call(handle, [=] { + return serialOutBytesWaiting(handle, error_callback); + }); } #ifdef __cplusplus diff --git a/include/cpp_core/interface/sequential/serial_read_line_sequential.h b/include/cpp_core/interface/sequential/serial_read_line_sequential.h index 4997234..dae9685 100644 --- a/include/cpp_core/interface/sequential/serial_read_line_sequential.h +++ b/include/cpp_core/interface/sequential/serial_read_line_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_read_line.h" #ifdef __cplusplus @@ -22,7 +22,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::internal::seq::call(handle, [=] { + return cpp_core::internal::sequential::call(handle, [=] { return serialReadLine(handle, buffer, buffer_size, timeout_ms, multiplier, error_callback); }); } diff --git a/include/cpp_core/interface/sequential/serial_read_sequential.h b/include/cpp_core/interface/sequential/serial_read_sequential.h index e98ece8..88ca226 100644 --- a/include/cpp_core/interface/sequential/serial_read_sequential.h +++ b/include/cpp_core/interface/sequential/serial_read_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_read.h" #ifdef __cplusplus @@ -22,7 +22,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::internal::seq::call(handle, [=] { + return cpp_core::internal::sequential::call(handle, [=] { return serialRead(handle, buffer, buffer_size, timeout_ms, multiplier, error_callback); }); } diff --git a/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h b/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h index 9191734..cdcc6d4 100644 --- a/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h +++ b/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_read_until_sequence.h" #ifdef __cplusplus @@ -23,7 +23,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::internal::seq::call(handle, [=] { + return cpp_core::internal::sequential::call(handle, [=] { return serialReadUntilSequence( handle, buffer, buffer_size, timeout_ms, multiplier, sequence, error_callback ); diff --git a/include/cpp_core/interface/sequential/serial_read_until_sequential.h b/include/cpp_core/interface/sequential/serial_read_until_sequential.h index 281ba70..1b1780f 100644 --- a/include/cpp_core/interface/sequential/serial_read_until_sequential.h +++ b/include/cpp_core/interface/sequential/serial_read_until_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_read_until.h" #ifdef __cplusplus @@ -23,7 +23,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::internal::seq::call(handle, [=] { + return cpp_core::internal::sequential::call(handle, [=] { return serialReadUntil(handle, buffer, buffer_size, timeout_ms, multiplier, until_char, error_callback); }); } diff --git a/include/cpp_core/interface/sequential/serial_write_sequential.h b/include/cpp_core/interface/sequential/serial_write_sequential.h index 24c024e..be82826 100644 --- a/include/cpp_core/interface/sequential/serial_write_sequential.h +++ b/include/cpp_core/interface/sequential/serial_write_sequential.h @@ -1,7 +1,7 @@ #pragma once #include "../../error_callback.h" -#include "../../internal/sequential_dispatch.h" +#include "../../internal/sequential/call.h" #include "../serial_write.h" #ifdef __cplusplus @@ -22,7 +22,7 @@ extern "C" ErrorCallbackT error_callback = nullptr ) -> int { - return cpp_core::internal::seq::call(handle, [=] { + return cpp_core::internal::sequential::call(handle, [=] { return serialWrite(handle, buffer, buffer_size, timeout_ms, multiplier, error_callback); }); } diff --git a/include/cpp_core/internal/sequential/call.h b/include/cpp_core/internal/sequential/call.h new file mode 100644 index 0000000..3404f58 --- /dev/null +++ b/include/cpp_core/internal/sequential/call.h @@ -0,0 +1,32 @@ +#pragma once + +#include "execute_in_queue.h" +#include "state_for_handle.h" +#include +#include + +namespace cpp_core::internal::sequential +{ +/** + * @brief Executes the given callable in the sequential dispatch queue associated with the specified handle. + * + * This helper forwards the callable to executeInQueue() together with the + * DispatchState that belongs to @p handle, thereby ensuring that all calls + * targeting the same handle are processed strictly sequentially. + * + * @tparam FunctionT Type of the invocable object. + * @param handle Handle identifying the sequential queue. + * @param function Callable object to execute. Can return any type (including void). + * @return Whatever @p function returns. + */ +template +auto call( + int64_t handle, + FunctionT &&function +) -> decltype(function()) +{ + return executeInQueue( + ::cpp_core::internal::sequential::internal::stateForHandle(handle), std::forward(function) + ); +} +} // namespace cpp_core::internal::sequential diff --git a/include/cpp_core/internal/sequential/dispatch_state.h b/include/cpp_core/internal/sequential/dispatch_state.h new file mode 100644 index 0000000..0436427 --- /dev/null +++ b/include/cpp_core/internal/sequential/dispatch_state.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace cpp_core::internal::sequential +{ +/** + * @brief Holds the queue and synchronisation primitives used by the sequential dispatch system. + */ +struct DispatchState +{ + // Mutex protecting queue + condition_variable. + std::mutex mutex; + + // Condition variable to wake the worker thread when new jobs arrive. + std::condition_variable condition_variable; + + // FIFO containing packaged thunks (jobs). + std::queue> queue; + + // Indicates if the worker thread is already launched. + std::once_flag worker_started; +}; +} // namespace cpp_core::internal::sequential diff --git a/include/cpp_core/internal/sequential/ensure_worker_running.h b/include/cpp_core/internal/sequential/ensure_worker_running.h new file mode 100644 index 0000000..d82aea7 --- /dev/null +++ b/include/cpp_core/internal/sequential/ensure_worker_running.h @@ -0,0 +1,23 @@ +#pragma once + +#include "dispatch_state.h" +#include "worker_loop.h" +#include +#include + +namespace cpp_core::internal::sequential +{ +/** + * @brief Starts the worker thread for @p state if it is not already running. + * + * The worker thread executes workerLoop() on the given DispatchState. A + * call_once flag inside the state guarantees that the thread is only created + * the first time this function is invoked. + * + * @param state DispatchState whose queue should be processed by the worker. + */ +inline void ensureWorkerRunning(DispatchState &state) +{ + std::call_once(state.worker_started, [&state] { std::thread([&state] { workerLoop(state); }).detach(); }); +} +} // namespace cpp_core::internal::sequential diff --git a/include/cpp_core/internal/sequential/execute_in_queue.h b/include/cpp_core/internal/sequential/execute_in_queue.h new file mode 100644 index 0000000..19e9a03 --- /dev/null +++ b/include/cpp_core/internal/sequential/execute_in_queue.h @@ -0,0 +1,53 @@ +#pragma once + +#include "dispatch_state.h" +#include "ensure_worker_running.h" +#include +#include +#include +#include +#include + +namespace cpp_core::internal::sequential +{ +/** + * @brief Queues the given callable in @p state and returns its result. + * + * The function first makes sure a worker thread is active via + * ensureWorkerRunning(). It then enqueues the callable and blocks until the + * callable has completed, forwarding the return value (if any). + * + * @tparam FunctionT Type of the callable object. + * @param state DispatchState that owns the queue and synchronisation primitives. + * @param function Callable object to execute. Can return any type. + * @return Result produced by @p function or void if it returns void. + */ +template +auto executeInQueue( + DispatchState &state, + FunctionT &&function +) -> decltype(function()) +{ + using FunctionReturnT = decltype(function()); + + auto task_ptr = std::make_shared>(std::forward(function)); + auto future = task_ptr->get_future(); + + ensureWorkerRunning(state); + + { + std::lock_guard lock(state.mutex); + state.queue.emplace([task_ptr]() { (*task_ptr)(); }); + } + state.condition_variable.notify_one(); + + if constexpr (std::is_void_v) + { + future.get(); + } + else + { + return future.get(); + } +} +} // namespace cpp_core::internal::sequential diff --git a/include/cpp_core/internal/sequential/handle_states.h b/include/cpp_core/internal/sequential/handle_states.h new file mode 100644 index 0000000..0741a4e --- /dev/null +++ b/include/cpp_core/internal/sequential/handle_states.h @@ -0,0 +1,25 @@ +#pragma once + +#include "dispatch_state.h" +#include +#include +#include + +namespace cpp_core::internal::sequential::internal +{ +/** + * @brief Gives access to the global handle -> DispatchState map. + * + * The map is lazily initialised on first access and is intended to be + * protected by handleStatesMutex(). + * + * @return Reference to the singleton unordered_map storing all DispatchStates. + */ +inline auto handleStates() -> std::unordered_map< + int64_t, + cpp_core::internal::sequential::DispatchState> & +{ + static std::unordered_map instance; + return instance; +} +} // namespace cpp_core::internal::sequential::internal diff --git a/include/cpp_core/internal/sequential/handle_states_mutex.h b/include/cpp_core/internal/sequential/handle_states_mutex.h new file mode 100644 index 0000000..5d2b6a8 --- /dev/null +++ b/include/cpp_core/internal/sequential/handle_states_mutex.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace cpp_core::internal::sequential::internal +{ +/** + * @brief Returns the mutex guarding access to handleStates(). + * + * @return Reference to the singleton std::mutex. + */ +inline auto handleStatesMutex() -> std::mutex & +{ + static std::mutex instance; + return instance; +} +} // namespace cpp_core::internal::sequential::internal diff --git a/include/cpp_core/internal/sequential/state_for_handle.h b/include/cpp_core/internal/sequential/state_for_handle.h new file mode 100644 index 0000000..c0e62cb --- /dev/null +++ b/include/cpp_core/internal/sequential/state_for_handle.h @@ -0,0 +1,23 @@ +#pragma once + +#include "handle_states.h" +#include "handle_states_mutex.h" +#include +#include + +namespace cpp_core::internal::sequential::internal +{ +/** + * @brief Retrieves the DispatchState associated with the specified handle. + * + * The global map is protected by a mutex to make this operation thread-safe. + * + * @param handle Identifier of the sequential dispatch queue. + * @return Reference to the corresponding DispatchState object. + */ +inline auto stateForHandle(int64_t handle) -> cpp_core::internal::sequential::DispatchState & +{ + std::lock_guard lock(handleStatesMutex()); + return handleStates()[handle]; +} +} // namespace cpp_core::internal::sequential::internal diff --git a/include/cpp_core/internal/sequential/worker_loop.h b/include/cpp_core/internal/sequential/worker_loop.h new file mode 100644 index 0000000..6961ae6 --- /dev/null +++ b/include/cpp_core/internal/sequential/worker_loop.h @@ -0,0 +1,34 @@ +#pragma once + +#include "dispatch_state.h" +#include +#include +#include +#include + +namespace cpp_core::internal::sequential +{ +/** + * @brief Blocking loop that continuously processes jobs from @p state. + * + * Intended to run on a background thread created by ensureWorkerRunning(). + * The loop waits on a condition variable until a job is available, executes + * it and then repeats indefinitely. + * + * @param state DispatchState providing the job queue and synchronisation primitives. + */ +inline void workerLoop(DispatchState &state) +{ + for (;;) + { + std::function job; + { + std::unique_lock lock(state.mutex); + state.condition_variable.wait(lock, [&state] { return !state.queue.empty(); }); + job = std::move(state.queue.front()); + state.queue.pop(); + } + job(); + } +} +} // namespace cpp_core::internal::sequential diff --git a/include/cpp_core/internal/sequential_dispatch.h b/include/cpp_core/internal/sequential_dispatch.h deleted file mode 100644 index 6104cec..0000000 --- a/include/cpp_core/internal/sequential_dispatch.h +++ /dev/null @@ -1,180 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cpp_core::internal::seq -{ - -struct DispatchState -{ - /** Mutex protecting queue + condition_variable. */ - std::mutex mutex; - - /** Condition variable to wake the worker thread when new jobs arrive. */ - std::condition_variable condition_variable; - - /** FIFO containing packaged thunks (jobs). */ - std::queue> queue; - - /** Indicates if the worker thread is already launched. */ - std::once_flag worker_started; -}; - -namespace detail -{ - -/** - * @brief Global map that stores a dedicated ::DispatchState for each serial handle encountered during runtime. The map - * is lazily populated on first use of a handle. - * @return Reference to the global map instance mapping handles to their dispatch states - */ -inline auto handleStates() -> std::unordered_map< - int64_t, - DispatchState> & -{ - static std::unordered_map instance; - return instance; -} - -/** - * @brief Mutex guarding access to ::handleStates() (adds/removes look-ups). - * @return Reference to the global mutex instance protecting handleStates() - */ -inline auto handleStatesMutex() -> std::mutex & -{ - static std::mutex instance; - return instance; -} - -/** - * @brief Get (or create) the ::DispatchState associated with @p handle. - * - * @param handle Serial handle used as key. - * @return Reference to the corresponding state object. - */ -inline auto stateForHandle(int64_t handle) -> DispatchState & -{ - std::lock_guard lock(handleStatesMutex()); - return handleStates()[handle]; -} - -} // namespace detail - -/** - * @brief Execute @p function sequentially on the queue dedicated to @p handle. - * - * Each serial handle owns its own FIFO queue and background worker thread. - * Calls executed on the same handle are strictly ordered, while calls on - * different handles may run concurrently. - * - * @tparam FunctionT Zero-argument callable type. - * @param handle Serial handle identifying the queue/worker. - * @param function Callable object to execute. - * @return Callable's return value (or `void`). - */ -template -auto call( - int64_t handle, - FunctionT &&function -) -> decltype(function()) -{ - return executeInQueue(detail::stateForHandle(handle), std::forward(function)); -} - -/** - * @brief Worker thread loop. - * - * Processes jobs from @p state.queue until program termination. The function - * blocks inside `state.condition_variable.wait()` whenever the queue is empty - * and wakes up once new jobs become available. Each invocation of the - * returned thunk is executed in FIFO order and exceptions propagate to the - * worker thread (terminating the program if uncaught). - * - * @param state Dispatcher state whose queue and synchronisation primitives are serviced by this loop. - */ -inline void workerLoop(DispatchState &state) -{ - for (;;) - { - std::function job; - { - std::unique_lock lock(state.mutex); - state.condition_variable.wait(lock, [&state] { return !state.queue.empty(); }); - job = std::move(state.queue.front()); - state.queue.pop(); - } - job(); - } -} - -/** - * @brief Launch the background worker for @p state exactly once. - * - * Internally relies on `std::call_once` to guarantee that a single detached - * worker thread is created for a given ::DispatchState instance, independent - * of how many times this function is called or from which thread. If the - * worker is already running, the call becomes a no-op. - * - * @param state Dispatcher state that owns the queue/condition variable which the spawned worker operates on. - */ -inline void ensureWorkerRunning(DispatchState &state) -{ - std::call_once(state.worker_started, [&state] { std::thread([&state] { workerLoop(state); }).detach(); }); -} - -/** - * @brief Execute a callable sequentially via the queue stored in @p state. - * - * The function performs the following steps: - * 1. Wrap the provided callable in `std::packaged_task` to obtain a future. - * 2. Ensure a worker thread is servicing the queue (lazy initialisation). - * 3. Enqueue a thunk that invokes the packaged task. - * 4. Notify the worker and block on the associated `std::future`. - * - * @tparam FunctionT Type of the zero-argument callable. - * @param state Dispatcher state backing the queue. - * @param function Callable object to execute. - * @return Callable's return value if the callable returns a non-void type. `void` otherwise. - * - * @note Exceptions thrown by @p function are rethrown in the calling thread when `future.get()` is invoked. - */ -template -auto executeInQueue( - DispatchState &state, - FunctionT &&function -) -> decltype(function()) -{ - using FunctionReturnT = decltype(function()); - - auto task_ptr = std::make_shared>(std::forward(function)); - auto future = task_ptr->get_future(); - - ensureWorkerRunning(state); - - { - std::lock_guard lock(state.mutex); - state.queue.emplace([task_ptr]() { (*task_ptr)(); }); - } - state.condition_variable.notify_one(); - - if constexpr (std::is_void_v) - { - future.get(); - } - else - { - return future.get(); - } -} - -} // namespace cpp_core::internal::seq From 9f12e63687287940a0b58366756af718ebcc2b22 Mon Sep 17 00:00:00 2001 From: Katze719 Date: Wed, 16 Jul 2025 21:27:21 +0200 Subject: [PATCH 16/18] Refactor serial_sequential header to aggregate sequential interface headers - Replaced individual sequential header inclusions in `serial_sequential.h` with a single inclusion of `sequential.h` for improved organization. - Introduced a new `sequential.h` header that consolidates all sequential interface headers, enhancing maintainability and accessibility. These changes streamline the header management in the serial interface, aligning with ongoing refactoring efforts. --- include/cpp_core/interface/sequential.h | 18 ++++++++++++++++++ include/cpp_core/serial_sequential.h | 18 ++---------------- 2 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 include/cpp_core/interface/sequential.h diff --git a/include/cpp_core/interface/sequential.h b/include/cpp_core/interface/sequential.h new file mode 100644 index 0000000..e4731c6 --- /dev/null +++ b/include/cpp_core/interface/sequential.h @@ -0,0 +1,18 @@ +#pragma once + +// Sequential wrappers +#include "sequential/serial_abort_read_sequential.h" +#include "sequential/serial_abort_write_sequential.h" +#include "sequential/serial_clear_buffer_in_sequential.h" +#include "sequential/serial_clear_buffer_out_sequential.h" +#include "sequential/serial_close_sequential.h" +#include "sequential/serial_drain_sequential.h" +#include "sequential/serial_in_bytes_total_sequential.h" +#include "sequential/serial_in_bytes_waiting_sequential.h" +#include "sequential/serial_out_bytes_total_sequential.h" +#include "sequential/serial_out_bytes_waiting_sequential.h" +#include "sequential/serial_read_line_sequential.h" +#include "sequential/serial_read_sequential.h" +#include "sequential/serial_read_until_sequence_sequential.h" +#include "sequential/serial_read_until_sequential.h" +#include "sequential/serial_write_sequential.h" diff --git a/include/cpp_core/serial_sequential.h b/include/cpp_core/serial_sequential.h index 233546f..afeaa69 100644 --- a/include/cpp_core/serial_sequential.h +++ b/include/cpp_core/serial_sequential.h @@ -11,19 +11,5 @@ #include "interface/serial_set_read_callback.h" #include "interface/serial_set_write_callback.h" -// Aggregates all *Sequential headers -#include "interface/sequential/serial_abort_read_sequential.h" -#include "interface/sequential/serial_abort_write_sequential.h" -#include "interface/sequential/serial_clear_buffer_in_sequential.h" -#include "interface/sequential/serial_clear_buffer_out_sequential.h" -#include "interface/sequential/serial_close_sequential.h" -#include "interface/sequential/serial_drain_sequential.h" -#include "interface/sequential/serial_in_bytes_total_sequential.h" -#include "interface/sequential/serial_in_bytes_waiting_sequential.h" -#include "interface/sequential/serial_out_bytes_total_sequential.h" -#include "interface/sequential/serial_out_bytes_waiting_sequential.h" -#include "interface/sequential/serial_read_line_sequential.h" -#include "interface/sequential/serial_read_sequential.h" -#include "interface/sequential/serial_read_until_sequence_sequential.h" -#include "interface/sequential/serial_read_until_sequential.h" -#include "interface/sequential/serial_write_sequential.h" +// Sequential wrappers +#include "interface/sequential.h" From ab890f3c23434df650adf11cc04ab61e996c22d8 Mon Sep 17 00:00:00 2001 From: Katze719 <38188106+Katze719@users.noreply.github.com> Date: Fri, 18 Jul 2025 11:09:09 +0200 Subject: [PATCH 17/18] Update include/cpp_core/serial_sequential.h Co-authored-by: Mqx <62719703+Mqxx@users.noreply.github.com> --- include/cpp_core/serial_sequential.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/cpp_core/serial_sequential.h b/include/cpp_core/serial_sequential.h index afeaa69..8ca0c62 100644 --- a/include/cpp_core/serial_sequential.h +++ b/include/cpp_core/serial_sequential.h @@ -1,7 +1,5 @@ #pragma once -#include "module_api.h" -#include "version.h" // Missing from the sequential interface #include "interface/get_version.h" From 5122982c6b520ac1235df985d9e64e5fc5f5b0b4 Mon Sep 17 00:00:00 2001 From: Katze719 Date: Fri, 18 Jul 2025 18:15:19 +0200 Subject: [PATCH 18/18] Remove deprecated `serial_sequential.h` header and update includes in `cpp_core.h` - Deleted the `serial_sequential.h` header to streamline the codebase and eliminate unused functionality. - Updated `cpp_core.h` to include the new `sequential.h` header, enhancing organization and maintainability of the serial interface. These changes align with ongoing refactoring efforts to improve header management and code clarity. --- include/cpp_core.h | 2 +- include/cpp_core/serial_sequential.h | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 include/cpp_core/serial_sequential.h diff --git a/include/cpp_core.h b/include/cpp_core.h index 16d7573..59edc64 100644 --- a/include/cpp_core.h +++ b/include/cpp_core.h @@ -13,7 +13,7 @@ */ #include "cpp_core/error_callback.h" +#include "cpp_core/interface/sequential.h" #include "cpp_core/serial.h" -#include "cpp_core/serial_sequential.h" #include "cpp_core/status_codes.h" #include "cpp_core/version.h" diff --git a/include/cpp_core/serial_sequential.h b/include/cpp_core/serial_sequential.h deleted file mode 100644 index 8ca0c62..0000000 --- a/include/cpp_core/serial_sequential.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - - -// Missing from the sequential interface -#include "interface/get_version.h" -#include "interface/serial_get_ports_info.h" -#include "interface/serial_open.h" -#include "interface/serial_set_error_callback.h" -#include "interface/serial_set_read_callback.h" -#include "interface/serial_set_write_callback.h" - -// Sequential wrappers -#include "interface/sequential.h"