From 5608eab8eb85397204a0fbbafd9b7f5d6335a08f Mon Sep 17 00:00:00 2001 From: marcelb Date: Sat, 29 Mar 2025 08:57:30 +0100 Subject: [PATCH] Add await_ to run on runtime and await_ - short code --- README.md | 77 +++++++++++++++++++++++++++++++++++++------------- lib/asynco.hpp | 29 ++++++++++++++++++- test/main.cpp | 62 ++++++++++++++++++++++++++++++++-------- 3 files changed, 137 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 8b67d29..6d36554 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # Asynco -A C++ library for event-driven asynchronous multi-threaded programming. +A C++ library for event-driven asynchronous multi-threaded programming that serves as a runtime for asynchronous operations. It acts as a wrapper around the Boost.Asio library, providing a cleaner way to write asynchronous, concurrent, and parallel code utilizing a set of threads and an event loops. It offers features for event-driven programming, timers, and coroutine support. ## Motivation -The original concept was to create an interface capable of asynchronously calling any function. It has since evolved into a library that incorporates a thread pool, each with its own event loop, event-driven programming, and functions inherently designed for asynchronous operation (including periodic and delayed functions). +The initial goal was to create an interface that makes it easy and clean to asynchronously invoke any function in C++ without resorting to complex calls. Initially, the library was built around a custom implementation of a scheduling loop for queuing functions. However, this part was later replaced with Boost.Asio, mainly for its timer functionality. As the library evolved, it expanded to include a thread pool, each with its own event loop, and adopted event-driven programming. This enhancement also introduced functions specifically designed for asynchronous operations, including periodic and delayed execution. -The asynchronous filesystem is provided solely to guide users on how to wrap any time- or IO-intensive function for asynchronous execution. +The asynchronous filesystem was included solely to demonstrate how users can wrap any time- or I/O-intensive functions for asynchronous execution. ## Features @@ -16,7 +16,7 @@ The asynchronous filesystem is provided solely to guide users on how to wrap any - Header only - Asynchronous programming - Multithread -- Asynchronous timer functions: periodic, delayed (like setInterval and setTimeout from JS) +- Asynchronous timer functions: Periodic, Delayed (like setInterval and setTimeout from JS) - Typed events (on, tick, off) (like EventEmitter from JS: on, emit, etc) - Event loops - Multiple parallel execution loops @@ -31,9 +31,10 @@ Just download the latest release and unzip it into your project. #define NUM_OF_RUNNERS 8 // To change the number of threads used by asynco, without this it runs according to the number of cores #include "asynco/lib/asynco.hpp" // async_ (), await_() -#include "asynco/lib/triggers.hpp" // trigger (event emitter) -#include "asynco/lib/timers.hpp" // periodic, delayed (like setInterval and setTimeout from JS) +#include "asynco/lib/triggers.hpp" // Trigger (event emitter) +#include "asynco/lib/timers.hpp" // Periodic, Delayed (like setInterval and setTimeout from JS) #include "asynco/lib/filesystem.hpp" // for async read and write files +#include "asynco/lib/define.hpp" // async_, await_, asyncable_ defines using namespace marcelb; using namespace asynco; @@ -46,7 +47,11 @@ return 0; ## Usage -Time asynchronous functions +In the following sections, we will explore timers, function execution via the runtime, asynchronous invocation, and waiting for results. We will cover essential use cases involving triggers, file handling, and coroutines. + +### Timers + +We have two timer classes, Periodic (which runs a callback function periodically), and Delayed (delayed runs a callback function only once). ```c++ // start periodic @@ -78,7 +83,9 @@ int t = time1.expired(); bool stoped = time1.stoped(); ``` -Make functions asynchronous +### Make functions asynchronous + +Running functions at runtime, asynchronous execution, uses the `async_` call and its return type is `std::future` ```c++ /** @@ -117,12 +124,11 @@ clm classes; async_ ( [&classes] () { classes.classMethode(); }); +``` +To wait for the result (blocking the flow) use `await_` (basically nothing more than a `.get()` call on a future object) - -/** -* await_ after runned as async -*/ +```c++ auto a = async_ ( []() { sleep_for(2s); // only for simulating long duration function @@ -142,10 +148,20 @@ cout << await_(async_ ( [] () { return 4; })) << endl; +``` -/** - * Await all - **/ +If you want to run asynchronously but need the result immediately, you can use a shorter notation + +```c++ + +await_ ([]() { + cout << "Hello" << endl; +}); + +``` +If multiple function calls do not depend on each other, you can call them and wait for the results later, better concurrency. + +```c++ auto a = async_ ( []() { cout << "A" << endl; @@ -196,9 +212,14 @@ auto await_all = [&] () { } }; +``` +Just an example: + +```c++ + /** -* Sleep with delayed sleep implement -*/ + * Sleep with delayed sleep implement + **/ void sleep_to (int _time) { promise _promise; @@ -236,7 +257,10 @@ try { cout<< err.what() << endl; } ``` -Events + +### Triggers + +The library implements Triggers, which are basically typed Events. ```c++ /** @@ -355,7 +379,7 @@ mt.tick("string", string("Hello world")); ``` - +Another example: Asynchronous file IO ```c++ @@ -434,6 +458,21 @@ int r = await_( )); ``` + +If you need the result immediately, you can use a shorter notation + +```c++ + +auto a = await_ ( c2(3)); +cout << a << endl; + +await_ ([]() -> asyncable { + cout << "Hello" << endl; + co_return; +}()); + +``` + Timers and triggers work the same with coroutines; it is important to call the coroutine with `async_` in the callback, and to call `async_`, wrap it with a lambda expression: ```c++ diff --git a/lib/asynco.hpp b/lib/asynco.hpp index 8ab5253..dbe0de1 100644 --- a/lib/asynco.hpp +++ b/lib/asynco.hpp @@ -26,7 +26,7 @@ auto async_(F&& f, Args&&... args) -> future::typ #if __cplusplus >= 202002L /** - * Run the coroutine asynchronously + * Run the coroutine */ template std::future async_(boost::asio::awaitable _coroutine) { @@ -68,6 +68,33 @@ T await_(future&& r) { return move(r).get(); } +/** + * Run the function asynchronously an block until completes +*/ +template +auto await_(F&& f, Args&&... args) -> typename result_of::type { + return await_( + async_(f, args...) + ); +} + + +#if __cplusplus >= 202002L + +/** + * Run the coruotine and wait + */ +template +T await_(boost::asio::awaitable _coroutine) { + return await_( + async_( + move(_coroutine) + )); +} + +#endif + + /** * Block until the asynchronous call completes or time expired */ diff --git a/test/main.cpp b/test/main.cpp index d6a7c4b..ecc5fa5 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -21,6 +21,11 @@ asyncable c2 (int a) { co_return a*2; } +asyncable sleep_co (int a) { + sleep(a); + cout << "Gotov" << endl; + co_return; +} asyncable c () { @@ -483,7 +488,7 @@ int main () { // Trigger ev2int; // Trigger evintString; - Trigger<> evoid; + // Trigger<> evoid; // ev2int.on("sum", [](int a, int b) -> asyncable { // cout << "Sum " << a+b << endl; @@ -497,10 +502,10 @@ int main () { // cout << "Substract " << a-stoi(b) << endl; // }); - evoid.on("void", []() { - auto a = await_ (async_ (c2(34))); - cout << "A " << a << endl; - }); + // evoid.on("void", []() { + // auto a = await_ (async_ (c2(34))); + // cout << "A " << a << endl; + // }); // auto c1 = []() -> asyncable { @@ -509,15 +514,51 @@ int main () { // }; - async_ ( c2(3)); + // auto a = await_ ( c2(3)); + // cout << a << endl; + // await_ ([]() -> asyncable { + // cout << "Hello" << endl; + // co_await c2(4); + // co_return; + // }()); + async_ ([]() -> asyncable { - cout << "Hello" << endl; - co_await c2(4); + cout << "1" << endl; + co_await sleep_co(1); co_return; }()); + async_ ([]() -> asyncable { + cout << "2" << endl; + co_await sleep_co(1); + co_return; + }()); + + async_ ([]() -> asyncable { + cout << "3" << endl; + co_await sleep_co(1); + co_return; + }()); + + async_ ([]() -> asyncable { + cout << "4" << endl; + co_await sleep_co(1); + co_return; + }()); + + async_ ([]() -> asyncable { + cout << "5" << endl; + co_await sleep_co(1); + co_return; + }()); + + + // await_ ([]() { + // cout << "Hello" << endl; + // }); + // Periodic p( []() { // async_ ( // c2(34) @@ -535,11 +576,10 @@ int main () { // cout << "Void emited " << emited2 << endl; // }); - evoid.tick("void"); + // evoid.tick("void"); - cout << "Run" << endl; + cout << "-------------end main-------------" << endl; _asynco_engine.run(); - return 0; }