### Install concurrencpp via vcpkg Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Installs stable releases of concurrencpp using the vcpkg package manager. ```shell $ vcpkg install concurrencpp ``` -------------------------------- ### generator example Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Example of creating and consuming a generator that yields the sequence S(n) = 1 + 2 + ... + n for n <= 100. ```APIDOC ## [EXAMPLE] generator usage ### Description Demonstrates how to define and use a generator that yields the n-th member of the sequence S(n) = 1 + 2 + 3 + ... + n for n <= 100. ### Code ```cpp concurrencpp::generator sequence() { int i = 1; int sum = 0; while (i <= 100) { sum += i; ++i; co_yield sum; } } int main() { for (auto value : sequence()) { std::cout << value << std::endl; } return 0; } ``` ``` -------------------------------- ### Regular timer example using make_timer Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Creates a regular timer with a 1.5 second initial delay and 2 second frequency, running the callable on the thread pool executor. The example sleeps for 12 seconds, allowing the timer to fire multiple times. ```cpp #include "concurrencpp/concurrencpp.h" #include using namespace std::chrono_literals; int main() { concurrencpp::runtime runtime; std::atomic_size_t counter = 1; concurrencpp::timer timer = runtime.timer_queue()->make_timer( 1500ms, 2000ms, runtime.thread_pool_executor(), [&] { const auto c = counter.fetch_add(1); std::cout << "timer was invoked for the " << c << "th time" << std::endl; }); std::this_thread::sleep_for(12s); return 0; } ``` -------------------------------- ### Build library on *nix platforms (release mode) Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Clones the repository and configures a release build on *nix, with an optional install command. ```cmake $ git clone https://github.com/David-Haim/concurrencpp.git $ cd concurrencpp $ cmake -DCMAKE_BUILD_TYPE=Release -S . -B build/lib $ cmake --build build/lib #optional, install the library: sudo cmake --install build/lib ``` -------------------------------- ### CMake configuration for 1_hello_world example Source: https://github.com/david-haim/concurrencpp/blob/master/example/1_hello_world/CMakeLists.txt Sets up the project with C++20, fetches the concurrencpp library from the local source directory, and links the executable to the concurrencpp target with coroutine options enabled. ```cmake cmake_minimum_required(VERSION 3.16) project(1_hello_world LANGUAGES CXX) include(FetchContent) FetchContent_Declare(concurrencpp SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../..") FetchContent_MakeAvailable(concurrencpp) include(../../cmake/coroutineOptions.cmake) add_executable(1_hello_world source/main.cpp) target_compile_features(1_hello_world PRIVATE cxx_std_20) target_link_libraries(1_hello_world PRIVATE concurrencpp::concurrencpp) target_coroutine_options(1_hello_world) ``` -------------------------------- ### Regular timer example Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Example of creating a regular timer using the timer queue, scheduling a callable to run after 1.5 seconds and then every 2 seconds on the threadpool executor. ```APIDOC ## [EXAMPLE] Regular timer ### Description Creates a regular timer using the timer queue. The timer schedules its callable to run after 1.5 seconds, then fires every 2 seconds on the threadpool executor. ### Code ```cpp #include "concurrencpp/concurrencpp.h" #include using namespace std::chrono_literals; int main() { concurrencpp::runtime runtime; std::atomic_size_t counter = 1; concurrencpp::timer timer = runtime.timer_queue()->make_timer( 1500ms, 2000ms, runtime.thread_pool_executor(), [&] { const auto c = counter.fetch_add(1); std::cout << "timer was invoked for the " << c << "th time" << std::endl; }); std::this_thread::sleep_for(12s); return 0; } ``` ``` -------------------------------- ### async_condition_variable usage example Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Complete example demonstrating a concurrent queue using async_lock and async_condition_variable with producer and consumer tasks. ```APIDOC ## [EXAMPLE] Concurrent queue using async_condition_variable ### Description This example shows how async_lock and async_condition_variable work together to implement a thread-safe queue for passing integers between tasks. ### Code ```cpp #include "concurrencpp/concurrencpp.h" #include #include using namespace concurrencpp; class concurrent_queue { private: async_lock _lock; async_condition_variable _cv; std::queue _queue; bool _abort = false; public: concurrent_queue() = default; result shutdown(std::shared_ptr resume_executor) { { auto guard = co_await _lock.lock(resume_executor); _abort = true; } _cv.notify_all(); } lazy_result push(std::shared_ptr resume_executor, int i) { { auto guard = co_await _lock.lock(resume_executor); _queue.push(i); } _cv.notify_one(); } lazy_result pop(std::shared_ptr resume_executor) { auto guard = co_await _lock.lock(resume_executor); co_await _cv.await(resume_executor, guard, [this] { return _abort || !_queue.empty(); }); if (!_queue.empty()) { auto result = _queue.front(); _queue.pop(); co_return result; } assert(_abort); throw std::runtime_error("queue has been shut down."); } }; result producer_loop(executor_tag, std::shared_ptr tpe, concurrent_queue& queue, int range_start, int range_end) { for (; range_start < range_end; ++range_start) { co_await queue.push(tpe, range_start); } } result consumer_loop(executor_tag, std::shared_ptr tpe, concurrent_queue& queue) { try { while (true) { std::cout << co_await queue.pop(tpe) << std::endl; } } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } } int main() { runtime runtime; const auto thread_pool_executor = runtime.thread_pool_executor(); concurrent_queue queue; result producers[4]; result consumers[4]; for (int i = 0; i < 4; i++) { producers[i] = producer_loop({}, thread_pool_executor, queue, i * 5, (i + 1) * 5); } for (int i = 0; i < 4; i++) { consumers[i] = consumer_loop({}, thread_pool_executor, queue); } for (int i = 0; i < 4; i++) { producers[i].get(); } queue.shutdown(thread_pool_executor).get(); for (int i = 0; i < 4; i++) { consumers[i].get(); } return 0; } ``` ``` -------------------------------- ### Oneshot timer example Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Example of creating a oneshot timer that runs only once after a specified delay on a new thread of execution. ```APIDOC ## [EXAMPLE] Oneshot timer ### Description Creates a oneshot timer that runs only once after 3 seconds from its creation, scheduling its callable to run on a new thread of execution using `thread_executor`. ### Code ```cpp #include "concurrencpp/concurrencpp.h" #include using namespace std::chrono_literals; int main() { concurrencpp::runtime runtime; concurrencpp::timer timer = runtime.timer_queue()->make_one_shot_timer( 3000ms, runtime.thread_executor(), [&] { std::cout << "hello and goodbye" << std::endl; }); std::this_thread::sleep_for(4s); return 0; } ``` ``` -------------------------------- ### shared_result example: convert result to shared_result and consume with multiple coroutines Source: https://github.com/david-haim/concurrencpp/blob/master/README.md This example shows how to convert a result to a shared_result and have multiple tasks await the same asynchronous value. It uses thread_executor and thread_pool_executor to spawn consumers, and when_all to wait for all consumers to finish. The output demonstrates that all consumers receive the same value and memory address. ```cpp #include "concurrencpp/concurrencpp.h" #include #include concurrencpp::result consume_shared_result(concurrencpp::shared_result shared_result, std::shared_ptr resume_executor) { std::cout << "Awaiting shared_result to have a value" << std::endl; const auto& async_value = co_await shared_result; concurrencpp::resume_on(resume_executor); std::cout << "In thread id " << std::this_thread::get_id() << ", got: " << async_value << ", memory address: " << &async_value << std::endl; } int main() { concurrencpp::runtime runtime; auto result = runtime.background_executor()->submit([] { std::this_thread::sleep_for(std::chrono::seconds(1)); return 100; }); concurrencpp::shared_result shared_result(std::move(result)); concurrencpp::result results[8]; for (size_t i = 0; i < 8; i++) { results[i] = consume_shared_result(shared_result, runtime.thread_pool_executor()); } std::cout << "Main thread waiting for all consumers to finish" << std::endl; auto tpe = runtime.thread_pool_executor(); auto all_consumed = concurrencpp::when_all(tpe, std::begin(results), std::end(results)).run(); all_consumed.get(); std::cout << "All consumers are done, exiting" << std::endl; return 0; } ``` -------------------------------- ### Hello world using concurrencpp runtime and thread_executor Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Creates a concurrencpp runtime, submits a lambda to the thread executor, and blocks on the result. The lambda prints 'hello world' and returns void, so the result is a result. Calling get() blocks until the task completes and re-throws any exception thrown by the task. ```cpp #include "concurrencpp/concurrencpp.h" #include int main() { concurrencpp::runtime runtime; auto result = runtime.thread_executor()->submit([] { std::cout << "hello world" << std::endl; }); result.get(); return 0; } ``` -------------------------------- ### async_lock example: concurrent vector push Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Pushes 10,000,000 integers to a shared std::vector from 4 concurrent tasks using async_lock to prevent data races. The example verifies the vector state after completion and prints an error if corrupted. Requires a concurrencpp runtime and a thread pool executor. ```cpp #include "concurrencpp/concurrencpp.h" #include #include std::vector numbers; concurrencpp::async_lock lock; concurrencpp::result add_numbers(concurrencpp::executor_tag, std::shared_ptr executor, size_t begin, size_t end) { for (auto i = begin; i < end; i++) { concurrencpp::scoped_async_lock raii_wrapper = co_await lock.lock(executor); numbers.push_back(i); } } int main() { concurrencpp::runtime runtime; constexpr size_t range = 10'000'000; constexpr size_t sections = 4; concurrencpp::result results[sections]; for (size_t i = 0; i < 4; i++) { const auto range_start = i * range / sections; const auto range_end = (i + 1) * range / sections; results[i] = add_numbers({}, runtime.thread_pool_executor(), range_start, range_end); } for (auto& result : results) { result.get(); } std::cout << "vector size is " << numbers.size() << std::endl; // make sure the vector state has not been corrupted by unprotected concurrent accesses std::sort(numbers.begin(), numbers.end()); for (size_t i = 0; i < range; i++) { if (numbers[i] != i) { std::cerr << "vector state is corrupted." << std::endl; return -1; } } std::cout << "succeeded pushing range [0 - 10,000,000] concurrently to the vector!" << std::endl; return 0; } ``` -------------------------------- ### Background executor usage with coroutine Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Shows how to submit a blocking task to a background executor, await its result, and then resume on a CPU-bound executor to process the result. The example demonstrates the recommended pattern of switching execution to a CPU-bound executor after consuming a background task's result. ```cpp auto result = background_executor.submit([] { /* some blocking action */ }); auto done_result = co_await result.resolve(); co_await resume_on(some_cpu_executor); auto val = co_await done_result; // runs inside some_cpu_executor ``` -------------------------------- ### generator example: sequence S(n) Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Shows a generator that yields the cumulative sum of integers from 1 to 100. The main function iterates over the generator in a range-for loop and prints each value. Note: the example uses std::end instead of std::endl, which is a typo in the source. ```cpp concurrencpp::generator sequence() { int i = 1; int sum = 0; while (i <= 100) { sum += i; ++i; co_yield sum; } } int main() { for (auto value : sequence()) { std::cout << value << std::end; } return 0; } ``` -------------------------------- ### Concurrent queue using async_condition_variable and async_lock Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Shows how async_lock and async_condition_variable implement a concurrent queue for sending integers between tasks. The example defines a concurrent_queue class with shutdown, push, and pop methods, then uses producer_loop and consumer_loop functions with a thread_pool_executor and concurrent_queue in main. It demonstrates mixing result and lazy_result return types, and uses queue.shutdown to abort waiting consumers. ```cpp #include "concurrencpp/concurrencpp.h" #include #include using namespace concurrencpp; class concurrent_queue { private: async_lock _lock; async_condition_variable _cv; std::queue _queue; bool _abort = false; public: concurrent_queue() = default; result shutdown(std::shared_ptr resume_executor) { { auto guard = co_await _lock.lock(resume_executor); _abort = true; } _cv.notify_all(); } lazy_result push(std::shared_ptr resume_executor, int i) { { auto guard = co_await _lock.lock(resume_executor); _queue.push(i); } _cv.notify_one(); } lazy_result pop(std::shared_ptr resume_executor) { auto guard = co_await _lock.lock(resume_executor); co_await _cv.await(resume_executor, guard, [this] { return _abort || !_queue.empty(); }); if (!_queue.empty()) { auto result = _queue.front(); _queue.pop(); co_return result; } assert(_abort); throw std::runtime_error("queue has been shut down."); } }; result producer_loop(executor_tag, std::shared_ptr tpe, concurrent_queue& queue, int range_start, int range_end) { for (; range_start < range_end; ++range_start) { co_await queue.push(tpe, range_start); } } result consumer_loop(executor_tag, std::shared_ptr tpe, concurrent_queue& queue) { try { while (true) { std::cout << co_await queue.pop(tpe) << std::endl; } } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } } int main() { runtime runtime; const auto thread_pool_executor = runtime.thread_pool_executor(); concurrent_queue queue; result producers[4]; result consumers[4]; for (int i = 0; i < 4; i++) { producers[i] = producer_loop({}, thread_pool_executor, queue, i * 5, (i + 1) * 5); } for (int i = 0; i < 4; i++) { consumers[i] = consumer_loop({}, thread_pool_executor, queue); } for (int i = 0; i < 4; i++) { producers[i].get(); } queue.shutdown(thread_pool_executor).get(); for (int i = 0; i < 4; i++) { consumers[i].get(); } return 0; } ``` -------------------------------- ### Delay object example with timer_queue and thread_pool_executor Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Spawns a task that delays itself in a loop by co_awaiting on a delay object created from a timer_queue. The task runs indefinitely, printing a message each time it is invoked. The main thread sleeps for 10 seconds before the program exits. ```cpp #include "concurrencpp/concurrencpp.h" #include using namespace std::chrono_literals; concurrencpp::null_result delayed_task( std::shared_ptr tq, std::shared_ptr ex) { size_t counter = 1; while(true) { std::cout << "task was invoked " << counter << " times." << std::endl; counter++; co_await tq->make_delay_object(1500ms, ex); } } int main() { concurrencpp::runtime runtime; delayed_task(runtime.timer_queue(), runtime.thread_pool_executor()); std::this_thread::sleep_for(10s); return 0; } ``` -------------------------------- ### Parallel Fibonacci using parallel coroutines Source: https://github.com/david-haim/concurrencpp/blob/master/README.md This example calculates the 30th Fibonacci number using parallel coroutines. It defines a recursive fibonacci coroutine that spawns parallel tasks for values greater than 10 and uses co_await to combine results. The main function creates a runtime and calls the coroutine with a thread pool executor. ```cpp #include "concurrencpp/concurrencpp.h" #include using namespace concurrencpp; int fibonacci_sync(int i) { if (i == 0) { return 0; } if (i == 1) { return 1; } return fibonacci_sync(i - 1) + fibonacci_sync(i - 2); } result fibonacci(executor_tag, std::shared_ptr tpe, const int curr) { if (curr <= 10) { co_return fibonacci_sync(curr); } auto fib_1 = fibonacci({}, tpe, curr - 1); auto fib_2 = fibonacci({}, tpe, curr - 2); co_return co_await co_await fib_1 + co_await co_await fib_2; } int main() { concurrencpp::runtime runtime; auto fibb_30 = fibonacci(runtime.thread_pool_executor(), 30).get(); std::cout << "fibonacci(30) = " << fibb_30 << std::endl; return 0; } ``` -------------------------------- ### result_promise with std::thread example Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Shows how to use result_promise to push a string from a std::thread and retrieve it from the main thread. The promise is moved into the thread, the result is extracted before passing the promise, and the main thread blocks on result.get() until the value is ready. The thread sleeps for one second to imitate real work before setting the result. ```cpp #include "concurrencpp/concurrencpp.h" #include int main() { concurrencpp::result_promise promise; auto result = promise.get_result(); std::thread my_3_party_executor([promise = std::move(promise)] () mutable { std::this_thread::sleep_for(std::chrono::seconds(1)); //Imitate real work promise.set_result("hello world"); }); auto asynchronous_string = result.get(); std::cout << "result promise returned string: " << asynchronous_string << std::endl; my_3_party_executor.join(); } ``` -------------------------------- ### make_exceptional_result(std::exception_ptr) Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Creates a ready result object from an exception pointer. The returned result will re-throw the exception when get or await is called. ```APIDOC ## make_exceptional_result(std::exception_ptr) ### Description Creates a ready result object from an exception pointer. The returned result object will re-throw the exception when calling get or await. ### Method Function ### Signature ```cpp template result make_exceptional_result(std::exception_ptr exception_ptr); ``` ### Parameters - **exception_ptr** (std::exception_ptr) - Required - The exception pointer to store in the result. ### Return Value - **result** - A ready result object that will re-throw the given exception when awaited or retrieved. ### Exceptions - Throws std::invalid_argument if exception_ptr is null. - Might throw std::bad_alloc exception if fails to allocate memory. ``` -------------------------------- ### runtime class API Source: https://github.com/david-haim/concurrencpp/blob/master/README.md The runtime class is the central manager for executors and the timer queue. It must be created as a value type at the start of main. Its destructor shuts down all executors and the timer queue, throwing runtime_shutdown on subsequent task scheduling. The API includes constructors, executor accessors, executor factory methods, and a version function. ```cpp class runtime { /* Creates a runtime object with default options. */ runtime(); /* Creates a runtime object with user defined options. */ runtime(const concurrencpp::runtime_options& options); /* Destroys this runtime object. Calls executor::shutdown on each monitored executor. Calls timer_queue::shutdown on the global timer queue. */ ~runtime() noexcept; /* Returns this runtime timer queue used to create new times. */ std::shared_ptr timer_queue() const noexcept; /* Returns this runtime concurrencpp::inline_executor */ std::shared_ptr inline_executor() const noexcept; /* Returns this runtime concurrencpp::thread_pool_executor */ std::shared_ptr thread_pool_executor() const noexcept; /* Returns this runtime concurrencpp::background_executor */ std::shared_ptr background_executor() const noexcept; /* Returns this runtime concurrencpp::thread_executor */ std::shared_ptr thread_executor() const noexcept; /* Creates a new concurrencpp::worker_thread_executor and registers it in this runtime. Might throw std::bad_alloc or std::system_error if any underlying memory or system resource could not have been acquired. */ std::shared_ptr make_worker_thread_executor(); /* Creates a new concurrencpp::manual_executor and registers it in this runtime. Might throw std::bad_alloc or std::system_error if any underlying memory or system resource could not have been acquired. */ std::shared_ptr make_manual_executor(); /* Creates a new user defined executor and registers it in this runtime. executor_type must be a valid class of concurrencpp::executor. Might throw std::bad_alloc if no memory is available. Might throw any exception that the constructor of <> might throw. */ template std::shared_ptr make_executor(argument_types&& ... arguments); /* returns the version of concurrencpp that the library was built with. */ static std::tuple version() noexcept; }; ``` -------------------------------- ### Count even numbers concurrently with concurrencpp Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Complete example that creates a random vector, partitions it across the thread pool executor's workers, and counts even numbers using coroutines. The main thread blocks on result.get() until the coroutine finishes. Requires a C++20 compiler with coroutine support and the concurrencpp library. ```cpp #include "concurrencpp/concurrencpp.h" #include #include #include #include using namespace concurrencpp; std::vector make_random_vector() { std::vector vec(64 * 1'024); std::srand(std::time(nullptr)); for (auto& i : vec) { i = ::rand(); } return vec; } result count_even(std::shared_ptr tpe, const std::vector& vector) { const auto vecor_size = vector.size(); const auto concurrency_level = tpe->max_concurrency_level(); const auto chunk_size = vecor_size / concurrency_level; std::vector> chunk_count; for (auto i = 0; i < concurrency_level; i++) { const auto chunk_begin = i * chunk_size; const auto chunk_end = chunk_begin + chunk_size; auto result = tpe->submit([&vector, chunk_begin, chunk_end]() -> size_t { return std::count_if(vector.begin() + chunk_begin, vector.begin() + chunk_end, [](auto i) { return i % 2 == 0; }); }); chunk_count.emplace_back(std::move(result)); } size_t total_count = 0; for (auto& result : chunk_count) { total_count += co_await result; } co_return total_count; } int main() { concurrencpp::runtime runtime; const auto vector = make_random_vector(); auto result = count_even(runtime.thread_pool_executor(), vector); const auto total_count = result.get(); std::cout << "there are " << total_count << " even numbers in the vector" << std::endl; return 0; } ``` -------------------------------- ### lazy_result::run() Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Runs the associated task inline and returns a result object that monitors the newly started task. After this call, *this is empty. Throws errors::empty_result if *this is empty. Might throw std::bad_alloc if fails to allocate memory. ```APIDOC ## [METHOD] lazy_result::run() ### Description Runs the associated task inline and returns a result object that monitors the newly started task. After this call, *this is empty. Throws errors::empty_result if *this is empty. Might throw std::bad_alloc if fails to allocate memory. ### Method run ### Endpoint lazy_result::run() ### Parameters None ### Request Example N/A ### Response #### Success Response (200) - **return** (result) - A result object that monitors the newly started task. #### Error Response (400) - **errors::empty_result** - If *this is empty. #### Error Response (500) - **std::bad_alloc** - If fails to allocate memory. #### Response Example N/A ``` -------------------------------- ### make_exceptional_result overloads Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Creates a ready result object from an exception pointer or exception object. The returned result re-throws the exception when get or await is called. Throws std::invalid_argument if the exception pointer is null, and may throw std::bad_alloc on memory allocation failure. ```cpp /* Creates a ready result object from an exception pointer. The returned result object will re-throw exception_ptr when calling get or await. Throws std::invalid_argument if exception_ptr is null. Might throw std::bad_alloc exception if fails to allocate memory. */ template result make_exceptional_result(std::exception_ptr exception_ptr); ``` ```cpp /* Overload. Similar to make_exceptional_result(std::exception_ptr), but gets an exception object directly. Might throw any exception that the constructor of exception_type(std::move(exception)) might throw. Might throw std::bad_alloc exception if fails to allocate memory. */ template result make_exceptional_result(exception_type exception); ``` -------------------------------- ### Build and run sandbox on *nix platforms Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Builds and runs the built-in sandbox program on *nix, with an optional release mode configuration. ```cmake $ cmake -S sandbox -B build/sandbox #for release mode: cmake -DCMAKE_BUILD_TYPE=Release -S sandbox -B build/sandbox $ cmake --build build/sandbox $ ./build/sandbox #runs the sandbox ``` -------------------------------- ### Monitoring thread creation and termination with runtime_options callbacks Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Sets thread_started_callback and thread_terminated_callback on runtime_options to log thread events. Creates a timer that fires every 500ms for 3 seconds. The callbacks are copied to each worker and must be copiable. Output shows thread start/termination messages and timer executions. ```cpp #include "concurrencpp/concurrencpp.h" #include int main() { concurrencpp::runtime_options options; options.thread_started_callback = [](std::string_view thread_name) { std::cout << "A new thread is starting to run, name: " << thread_name << ", thread id: " << std::this_thread::get_id() << std::endl; }; options.thread_terminated_callback = [](std::string_view thread_name) { std::cout << "A thread is terminating, name: " << thread_name << ", thread id: " << std::this_thread::get_id() << std::endl; }; concurrencpp::runtime runtime(options); const auto timer_queue = runtime.timer_queue(); const auto thread_pool_executor = runtime.thread_pool_executor(); concurrencpp::timer timer = timer_queue->make_timer(std::chrono::milliseconds(100), std::chrono::milliseconds(500), thread_pool_executor, [] { std::cout << "A timer callable is executing" << std::endl; }); std::this_thread::sleep_for(std::chrono::seconds(3)); return 0; } ``` ```text A new thread is starting to run, name: concurrencpp::timer_queue worker, thread id: 7496 A new thread is starting to run, name: concurrencpp::thread_pool_executor worker, thread id: 21620 A timer callable is executing A timer callable is executing A timer callable is executing A timer callable is executing A timer callable is executing A timer callable is executing A thread is terminating, name: concurrencpp::timer_queue worker, thread id: 7496 A thread is terminating, name: concurrencpp::thread_pool_executor worker, thread id: 21620 ``` -------------------------------- ### Build and run sandbox on Windows Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Builds and runs the built-in sandbox program on Windows, with an optional release mode build. ```cmake $ cmake -S sandbox -B build/sandbox $ cmake --build build/sandbox <# for release mode: cmake --build build/sandbox --config Release #> $ ./build/sandbox <# runs the sandbox> ``` -------------------------------- ### result_promise::get_result Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Gets the associated result object. Throws errors::result_already_retrieved if called more than once. ```APIDOC ## [METHOD] result get_result() ### Description Gets the associated result object. Throws errors::result_already_retrieved if called more than once. ### Method get_result ### Endpoint result get_result() ### Parameters None ### Request Example ```cpp result_promise promise; auto result = promise.get_result(); ``` ### Response #### Success Response (200) - **return** (result) - The associated result object. #### Response Example ```cpp // No response body ``` ``` -------------------------------- ### Run tests on *nix platforms (with TSAN support) Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Builds and runs the test suite on *nix, with options for release mode and TSAN (thread sanitizer) support. ```cmake $ git clone https://github.com/David-Haim/concurrencpp.git $ cd concurrencpp $ cmake -S test -B build/test #for release mode: cmake -DCMAKE_BUILD_TYPE=Release -S test -B build/test #for TSAN mode: cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_THREAD_SANITIZER=Yes -S test -B build/test $ cmake --build build/test $ cd build/test $ ctest . -V ``` -------------------------------- ### Build library on Windows (release mode) Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Clones the repository and configures a release build using CMake on Windows. ```cmake $ git clone https://github.com/David-Haim/concurrencpp.git $ cd concurrencpp $ cmake -S . -B build/lib $ cmake --build build/lib --config Release ``` -------------------------------- ### std::add_lvalue_reference_t get() Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Blocks the current thread of execution until this shared-result is ready, when status() != result_status::idle. If the result is a valid value, a reference to it is returned, otherwise, get rethrows the asynchronous exception. Throws errors::empty_result if *this is empty. Might throw std::system_error if one of the underlying synchronization primitives throws. ```APIDOC ## [METHOD] std::add_lvalue_reference_t get() ### Description Blocks the current thread of execution until this shared-result is ready, when status() != result_status::idle. If the result is a valid value, a reference to it is returned, otherwise, get rethrows the asynchronous exception. Throws errors::empty_result if *this is empty. Might throw std::system_error if one of the underlying synchronization primitives throws. ### Method get ### Endpoint get() ### Parameters None ### Request Example ```cpp shared_result sr = ...; int& value = sr.get(); ``` ### Response #### Success Response - **return** (std::add_lvalue_reference_t) - A reference to the result value. #### Response Example ```cpp // Returns a reference to the value ``` ``` -------------------------------- ### lazy_result type Source: https://github.com/david-haim/concurrencpp/blob/master/README.md A lazy result object represents the result of a deferred lazy task. It has the responsibility of both starting the associated lazy task and passing its deferred result back to its consumer. When awaited or resolved, the lazy result suspends the current coroutine and starts the associated lazy task. When the associated task completes, its asynchronous value is passed to the caller task, which is then resumed. Sometimes, an API might return a lazy result, but applications need its associated task to run eagerly. In this case, lazy tasks can be converted to eager tasks by calling run on its associated lazy result. The original lazy result is emptied and a valid result object that monitors the newly started task will be returned instead. ```APIDOC ## [TYPE] lazy_result ### Description A lazy result object represents the result of a deferred lazy task. It has the responsibility of both starting the associated lazy task and passing its deferred result back to its consumer. When awaited or resolved, the lazy result suspends the current coroutine and starts the associated lazy task. When the associated task completes, its asynchronous value is passed to the caller task, which is then resumed. Sometimes, an API might return a lazy result, but applications need its associated task to run eagerly. In this case, lazy tasks can be converted to eager tasks by calling run on its associated lazy result. The original lazy result is emptied and a valid result object that monitors the newly started task will be returned instead. ### Method run ### Endpoint lazy_result::run() ### Parameters None ### Request Example ```cpp lazy_result lr = co_await some_lazy_task(); result r = lr.run(); ``` ### Response #### Success Response (200) - **result** (object) - A valid result object that monitors the newly started task. #### Response Example ```cpp lazy_result lr = co_await some_lazy_task(); result r = lr.run(); ``` ``` -------------------------------- ### runtime::runtime() Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Creates a runtime object with default options. ```APIDOC ## [CONSTRUCTOR] runtime::runtime() ### Description Creates a runtime object with default options. ### Method Constructor ### Endpoint runtime::runtime() ### Parameters None ### Request Example ```cpp concurrencpp::runtime runtime; ``` ### Response #### Success Response - **runtime** (runtime) - A new runtime object with default options. #### Response Example ```cpp concurrencpp::runtime runtime; ``` ``` -------------------------------- ### runtime::runtime(const runtime_options& options) Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Creates a runtime object with user-defined options. ```APIDOC ## [CONSTRUCTOR] runtime::runtime(const concurrencpp::runtime_options& options) ### Description Creates a runtime object with user-defined options. ### Method Constructor ### Endpoint runtime::runtime(const concurrencpp::runtime_options& options) ### Parameters #### Path Parameters - **options** (const concurrencpp::runtime_options&) - Required - User-defined options for the runtime. ### Request Example ```cpp concurrencpp::runtime_options options; concurrencpp::runtime runtime(options); ``` ### Response #### Success Response - **runtime** (runtime) - A new runtime object with the specified options. #### Response Example ```cpp concurrencpp::runtime runtime(options); ``` ``` -------------------------------- ### shared_result class definition Source: https://github.com/david-haim/concurrencpp/blob/master/README.md The complete class definition of shared_result, including constructors, assignment operators, status query, wait methods, get, and co_await support. It is used to share an asynchronous result among multiple consumers. ```cpp class share_result { /* Creates an empty shared-result that isn't associated with any task. */ shared_result() noexcept = default; /* Destroys the shared-result. Associated tasks are not cancelled. The destructor does not block waiting for the asynchronous result to become ready. */ ~shared_result() noexcept = default; /* Converts a regular result object to a shared-result object. After this call, rhs is empty. Might throw std::bad_alloc if fails to allocate memory. */ shared_result(result rhs); /* Copy constructor. Creates a copy of the shared result object that monitors the same task. */ shared_result(const shared_result&) noexcept = default; /* Move constructor. Moves rhs to *this. After this call, rhs is empty. */ shared_result(shared_result&& rhs) noexcept = default; /* Copy assignment operator. Copies rhs to *this and monitors the same task that rhs monitors. */ shared_result& operator=(const shared_result& rhs) noexcept; /* Move assignment operator. Moves rhs to *this. After this call, rhs is empty. */ shared_result& operator=(shared_result&& rhs) noexcept; /* Returns true if this is a non-empty shared-result. Applications must not use this object if this->operator bool() is false. */ explicit operator bool() const noexcept; /* Queries the status of *this. The return value is any of result_status::idle, result_status::value or result_status::exception. Throws errors::empty_result if *this is empty. */ result_status status() const; /* Blocks the current thread of execution until this shared-result is ready, when status() != result_status::idle. Throws errors::empty_result if *this is empty. Might throw std::system_error if one of the underlying synchronization primitives throws. */ void wait(); /* Blocks until this shared-result is ready or duration has passed. Returns the status of this shared-result after unblocking. Throws errors::empty_result if *this is empty. Might throw std::system_error if one of the underlying synchronization primitives throws. */ template result_status wait_for(std::chrono::duration duration); /* Blocks until this shared-result is ready or timeout_time has reached. Returns the status of this result after unblocking. Throws errors::empty_result if *this is empty. Might throw std::system_error if one of the underlying synchronization primitives throws. */ template result_status wait_until(std::chrono::time_point timeout_time); /* Blocks the current thread of execution until this shared-result is ready, when status() != result_status::idle. If the result is a valid value, a reference to it is returned, otherwise, get rethrows the asynchronous exception. Throws errors::empty_result if *this is empty. Might throw std::system_error if one of the underlying synchronization primitives throws. */ std::add_lvalue_reference_t get(); /* Returns an awaitable used to await this shared-result. If the shared-result is already ready - the current coroutine resumes immediately in the calling thread of execution. If the shared-result is not ready yet, the current coroutine is suspended and resumed when the asynchronous result is ready, by the thread which had set the asynchronous value or exception. In either way, after resuming, if the result is a valid value, a reference to it is returned. Otherwise, operator co_await rethrows the asynchronous exception. Throws errors::empty_result if *this is empty. */ auto operator co_await(); /* Returns an awaitable used to resolve this shared-result. After co_await expression finishes, *this is returned in a non-empty form, in a ready state. Throws errors::empty_result if *this is empty. */ auto resolve(); }; ``` -------------------------------- ### CMake configuration for 4_async_file_processing_version_2 Source: https://github.com/david-haim/concurrencpp/blob/master/example/4_async_file_processing_version_2/CMakeLists.txt Configures the project with C++20, fetches concurrencpp from the parent directory, and links the executable. Requires CMake 3.16 or higher. ```cmake cmake_minimum_required(VERSION 3.16) project(4_async_file_processing_version_2 LANGUAGES CXX) include(FetchContent) FetchContent_Declare(concurrencpp SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../..") FetchContent_MakeAvailable(concurrencpp) include(../../cmake/coroutineOptions.cmake) add_executable(4_async_file_processing_version_2 source/main.cpp) target_compile_features(4_async_file_processing_version_2 PRIVATE cxx_std_20) target_link_libraries(4_async_file_processing_version_2 PRIVATE concurrencpp::concurrencpp) target_coroutine_options(4_async_file_processing_version_2) ``` -------------------------------- ### runtime::make_manual_executor() Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Creates a new manual executor and registers it in the runtime. ```APIDOC ## [METHOD] runtime::make_manual_executor() ### Description Creates a new concurrencpp::manual_executor and registers it in this runtime. ### Method Factory ### Endpoint std::shared_ptr make_manual_executor() ### Parameters None ### Request Example ```cpp auto executor = runtime.make_manual_executor(); ``` ### Response #### Success Response - **manual_executor** (std::shared_ptr) - A new manual executor. #### Error Response - **std::bad_alloc** - If memory allocation fails. - **std::system_error** - If a system resource cannot be acquired. #### Response Example ```cpp auto executor = runtime.make_manual_executor(); ``` ``` -------------------------------- ### runtime::version() Source: https://github.com/david-haim/concurrencpp/blob/master/README.md Returns the version of concurrencpp that the library was built with. ```APIDOC ## [METHOD] runtime::version() ### Description Returns the version of concurrencpp that the library was built with. ### Method Static Getter ### Endpoint static std::tuple version() noexcept ### Parameters None ### Request Example ```cpp auto version = concurrencpp::runtime::version(); ``` ### Response #### Success Response - **version** (std::tuple) - The version as a tuple of major, minor, and patch numbers. #### Response Example ```cpp auto version = concurrencpp::runtime::version(); ``` ```