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..bf0be46 --- /dev/null +++ b/.github/workflows/doxygen.yml @@ -0,0 +1,48 @@ +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 + pages: write + id-token: write + +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 + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + 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@v4 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 diff --git a/include/cpp_core.h b/include/cpp_core.h index 1f50732..59edc64 100644 --- a/include/cpp_core.h +++ b/include/cpp_core.h @@ -13,6 +13,7 @@ */ #include "cpp_core/error_callback.h" +#include "cpp_core/interface/sequential.h" #include "cpp_core/serial.h" #include "cpp_core/status_codes.h" #include "cpp_core/version.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/interface/sequential/serial_abort_read_sequential.h b/include/cpp_core/interface/sequential/serial_abort_read_sequential.h new file mode 100644 index 0000000..b9dd5a1 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_abort_read_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { 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..c7e42cc --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_abort_write_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { 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..6288caa --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_clear_buffer_in_sequential.h @@ -0,0 +1,28 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { + 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..3d58d98 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_clear_buffer_out_sequential.h @@ -0,0 +1,28 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { + 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..7c9aa0a --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_close_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { 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..5216b39 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_drain_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { return serialDrain(handle, 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..9f40ac7 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_in_bytes_total_sequential.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { 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..eb5ee6b --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_in_bytes_waiting_sequential.h @@ -0,0 +1,28 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { + return serialInBytesWaiting(handle, 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..6528871 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_out_bytes_total_sequential.h @@ -0,0 +1,28 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { + 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..82abef2 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_out_bytes_waiting_sequential.h @@ -0,0 +1,28 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { + 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..dae9685 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_read_line_sequential.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { + 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..88ca226 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_read_sequential.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { + 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..cdcc6d4 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_read_until_sequence_sequential.h @@ -0,0 +1,35 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { + 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..1b1780f --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_read_until_sequential.h @@ -0,0 +1,33 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { + 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_write_sequential.h b/include/cpp_core/interface/sequential/serial_write_sequential.h new file mode 100644 index 0000000..be82826 --- /dev/null +++ b/include/cpp_core/interface/sequential/serial_write_sequential.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../../error_callback.h" +#include "../../internal/sequential/call.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::internal::sequential::call(handle, [=] { + return serialWrite(handle, buffer, buffer_size, timeout_ms, multiplier, error_callback); + }); + } + +#ifdef __cplusplus +} +#endif 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