Compare commits
No commits in common. "dev" and "dev-own-engine" have entirely different histories.
dev
...
dev-own-en
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,3 +1,2 @@
|
||||
build
|
||||
.vscode
|
||||
example
|
||||
test/test
|
||||
test/*.txt
|
16
.vscode/c_cpp_properties.json
vendored
Normal file
16
.vscode/c_cpp_properties.json
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "gnu++17",
|
||||
"intelliSenseMode": "linux-gcc-x64"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
74
.vscode/settings.json
vendored
Normal file
74
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"iostream": "cpp",
|
||||
"functional": "cpp",
|
||||
"thread": "cpp",
|
||||
"chrono": "cpp",
|
||||
"ostream": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"deque": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"ratio": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"future": "cpp",
|
||||
"*.ipp": "cpp",
|
||||
"bitset": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"string": "cpp",
|
||||
"string_view": "cpp",
|
||||
"fstream": "cpp",
|
||||
"cctype": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"csignal": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"any": "cpp",
|
||||
"bit": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"codecvt": "cpp",
|
||||
"compare": "cpp",
|
||||
"complex": "cpp",
|
||||
"concepts": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"list": "cpp",
|
||||
"map": "cpp",
|
||||
"set": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"random": "cpp",
|
||||
"system_error": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"numbers": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"typeindex": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"variant": "cpp"
|
||||
}
|
||||
}
|
28
.vscode/tasks.json
vendored
Normal file
28
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"type": "cppbuild",
|
||||
"label": "C/C++: gcc build active file",
|
||||
"command": "/usr/bin/g++",
|
||||
"args": [
|
||||
"-fdiagnostics-color=always",
|
||||
"-g",
|
||||
"${file}",
|
||||
"-o",
|
||||
"${fileDirname}/${fileBasenameNoExtension}"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${fileDirname}"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$gcc"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"detail": "Task generated by Debugger."
|
||||
}
|
||||
],
|
||||
"version": "2.0.0"
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(Asynco)
|
||||
|
||||
# Postavi verziju projekta
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Pronađi Boost biblioteku (ako nije uobičajeni direktorijum, postavi put)
|
||||
find_package(Boost REQUIRED COMPONENTS system)
|
||||
|
||||
# Dodaj direktorijume sa zaglavljima
|
||||
include_directories(lib)
|
||||
|
||||
# Dodaj biblioteku
|
||||
add_library(asynco STATIC
|
||||
src/asynco.cpp
|
||||
src/asynco_default.cpp
|
||||
src/timers.cpp
|
||||
)
|
||||
|
||||
# Linkaj Asynco biblioteku sa Boost-om
|
||||
target_link_libraries(asynco Boost::system)
|
||||
|
||||
# Dodaj testove
|
||||
add_subdirectory(test)
|
||||
|
||||
add_compile_options(-w)
|
||||
|
||||
|
||||
|
||||
# Instaliraj biblioteku
|
||||
# install(TARGETS asynco DESTINATION lib)
|
||||
# install(FILES lib/asynco.hpp lib/define.hpp lib/engine.hpp lib/filesystem.hpp lib/timers.hpp lib/trigger.hpp DESTINATION include/asynco)
|
||||
#
|
327
README.md
327
README.md
@ -1,125 +1,70 @@
|
||||
|
||||
# Asynco
|
||||
|
||||
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 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 was included solely to demonstrate how users can wrap any time- or I/O-intensive functions for asynchronous execution.
|
||||
A C++ library for event-driven asynchronous multi-threaded programming.
|
||||
|
||||
## Features
|
||||
|
||||
- Object oriented
|
||||
- Small and easy to integrate
|
||||
- Header only
|
||||
- Asynchronous programming
|
||||
- Multithread
|
||||
- Asynchronous timer functions: Periodic, Delayed (like setInterval and setTimeout from JS)
|
||||
- Typed events (on, tick, off) (like EventEmitter from JS: on, emit, etc)
|
||||
- Asynchronous timer functions: interval, timeout
|
||||
- Typed events (on, emit, off)
|
||||
- Event loops
|
||||
- Multiple parallel execution loops
|
||||
- Based on ASIO (Boost Asio)
|
||||
- On C++20 support Boost.Asio coroutines
|
||||
- Asynchronous file IO
|
||||
## Installation
|
||||
|
||||
Just download the latest release and unzip it into your project.
|
||||
|
||||
```c++
|
||||
// for default global runtime
|
||||
|
||||
#include "asynco/lib/asynco_default.hpp"
|
||||
#define NUM_OF_RUNNERS 8 // To change the number of threads used by atask, without this it runs according to the number of cores
|
||||
|
||||
#include "asynco/lib/asynco.hpp" // atask(), wait()
|
||||
#include "asynco/lib/event.hpp" // event
|
||||
#include "asynco/lib/rotor.hpp" // interval, timeout
|
||||
#include "asynco/lib/runner.hpp" // for own loop
|
||||
#include "asynco/lib/filesystem.hpp" // for async read and write files
|
||||
|
||||
using namespace marcelb;
|
||||
using namespace asynco;
|
||||
|
||||
int main() {
|
||||
asynco_default_run();
|
||||
|
||||
// code
|
||||
|
||||
asynco_default_join()
|
||||
return 0;
|
||||
}
|
||||
|
||||
// own instace of runtime
|
||||
|
||||
#include "asynco/lib/asynco.hpp"
|
||||
|
||||
using namespace marcelb;
|
||||
using namespace asynco;
|
||||
|
||||
int main() {
|
||||
Asynco asynco;
|
||||
asynco.run(2);
|
||||
|
||||
// code
|
||||
|
||||
asynco.join();
|
||||
return 0;
|
||||
}
|
||||
using namespace events;
|
||||
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
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, and coroutines.
|
||||
|
||||
### Timers
|
||||
|
||||
We have one timer classes, int two mode Periodic (which runs a callback function periodically), and Delayed (delayed runs a callback function only once).
|
||||
Time asynchronous functions
|
||||
|
||||
```c++
|
||||
// start periodic
|
||||
Timer inter1 = periodic ([]() {
|
||||
// start interval
|
||||
interval inter1 ([]() {
|
||||
cout << "Interval 1" << endl;
|
||||
}, 1000);
|
||||
|
||||
// or usint own instance runtime
|
||||
/**
|
||||
* Asynco asynco;
|
||||
* asynco.run(2);
|
||||
* Timer inter1 = asynco.periodic ([]() {
|
||||
* cout << "Interval 1" << endl;
|
||||
* }, 1000);
|
||||
*/
|
||||
// stop periodic
|
||||
inter1.stop();
|
||||
// stop interval
|
||||
inter1.clear();
|
||||
|
||||
// how many times it has expired
|
||||
int t = inter1.ticks();
|
||||
|
||||
// is it stopped
|
||||
bool stoped = inter1.stoped();
|
||||
|
||||
// start delayed
|
||||
Timer time1 = delayed( [] () {
|
||||
// start timeout
|
||||
timeout time1 ( [] () {
|
||||
cout << "Timeout 1 " << endl;
|
||||
}, 10000);
|
||||
|
||||
// stop delayed
|
||||
time1.stop();
|
||||
|
||||
// is it expired
|
||||
int t = time1.expired();
|
||||
|
||||
// is it stopped
|
||||
bool stoped = time1.stoped();
|
||||
|
||||
// stop timeout
|
||||
time1.clear();
|
||||
```
|
||||
### Make functions asynchronous
|
||||
|
||||
Running functions at runtime, asynchronous execution, uses the `async_` call and its return type is `std::future<T>`
|
||||
Make functions asynchronous
|
||||
|
||||
```c++
|
||||
/**
|
||||
* Run an lambda function asynchronously
|
||||
*/
|
||||
|
||||
async_ ( []() {
|
||||
sleep(2); // only for simulating long duration function
|
||||
cout << "nonsync " << endl;
|
||||
atask( []() {
|
||||
sleep_for(2s); // only for simulating long duration function
|
||||
cout << "atask" << endl;
|
||||
return 5;
|
||||
});
|
||||
|
||||
@ -132,7 +77,7 @@ void notLambdaFunction() {
|
||||
cout << "Call to not lambda function" << endl;
|
||||
}
|
||||
|
||||
async_ (notLambdaFunction);
|
||||
atask (notLambdaFunction);
|
||||
|
||||
/**
|
||||
* Run class method
|
||||
@ -146,58 +91,84 @@ class clm {
|
||||
};
|
||||
|
||||
clm classes;
|
||||
async_ ( [&classes] () {
|
||||
atask( [&classes] () {
|
||||
classes.classMethode();
|
||||
});
|
||||
```
|
||||
|
||||
To wait for the result (blocking the flow) use `await_` (This does not block the event loop in principle. If the result is not ready for a short time, it starts another job in place while it waits.)
|
||||
|
||||
```c++
|
||||
|
||||
auto a = async_ ( []() {
|
||||
sleep(2); // only for simulating long duration function
|
||||
cout << "nonsync " << endl;
|
||||
/**
|
||||
* Wait after runned as async
|
||||
*/
|
||||
|
||||
auto a = atask( []() {
|
||||
sleep_for(2s); // only for simulating long duration function
|
||||
cout << "atask" << endl;
|
||||
return 5;
|
||||
});
|
||||
|
||||
cout << await_(a) << endl;
|
||||
cout << wait(a) << endl;
|
||||
|
||||
/**
|
||||
* await_ async function call and use i cout
|
||||
* Wait async function call and use i cout
|
||||
*/
|
||||
|
||||
cout << await_(async_ ( [] () {
|
||||
sleep(1); // only for simulating long duration function
|
||||
cout << "await_ end" << endl;
|
||||
cout << wait(atask( [] () {
|
||||
sleep_for(chrono::seconds(1)); // only for simulating long duration function
|
||||
cout << "wait end" << endl;
|
||||
return 4;
|
||||
})) << endl;
|
||||
|
||||
/**
|
||||
* Sleep with timeout sleep implement
|
||||
*/
|
||||
|
||||
void sleep_to (int _time) {
|
||||
promise<void> _promise;
|
||||
timeout t( [&]() {
|
||||
_promise.set_value();
|
||||
}, _time);
|
||||
|
||||
return _promise.get_future().get();
|
||||
}
|
||||
|
||||
sleep_to(3000);
|
||||
|
||||
/**
|
||||
* Catch promise reject
|
||||
*/
|
||||
|
||||
void promise_reject (int _time) {
|
||||
promise<void> _promise;
|
||||
timeout t( [&]() {
|
||||
try {
|
||||
// simulate except
|
||||
throw runtime_error("Error simulation");
|
||||
_promise.set_value();
|
||||
} catch (...) {
|
||||
_promise.set_exception(current_exception());
|
||||
}
|
||||
}, _time);
|
||||
|
||||
return _promise.get_future().get();
|
||||
}
|
||||
|
||||
try {
|
||||
promise_reject(3000);
|
||||
} catch (runtime_error err) {
|
||||
cout<< err.what() << endl;
|
||||
}
|
||||
```
|
||||
|
||||
If you want to run asynchronously but need the result immediately, you can use a shorter notation
|
||||
|
||||
```c++
|
||||
|
||||
await_ ([]() {
|
||||
cout << "Hello" << endl;
|
||||
});
|
||||
|
||||
```
|
||||
Here too you can use your own runtime instance, only the methods are `.async()` and `.await()`
|
||||
|
||||
### Triggers
|
||||
|
||||
The library implements Triggers, which are basically typed Events.
|
||||
Events
|
||||
|
||||
```c++
|
||||
/**
|
||||
* initialization of typed events
|
||||
*/
|
||||
|
||||
Trigger<int, int> ev2int = trigger<int, int>();
|
||||
Trigger<int, string> evintString = trigger<int, string>();
|
||||
Trigger<> evoid = trigger<>();
|
||||
event<int, int> ev2int;
|
||||
event<int, string> evintString;
|
||||
event<> evoid;
|
||||
|
||||
ev2int.on("sum", [](int a, int b) {
|
||||
cout << "Sum " << a+b << endl;
|
||||
@ -225,32 +196,32 @@ sleep(1);
|
||||
* Emit
|
||||
*/
|
||||
|
||||
ev2int.tick("sum", 5, 8);
|
||||
ev2int.emit("sum", 5, 8);
|
||||
|
||||
sleep(1);
|
||||
evintString.tick("substract", 3, to_string(2));
|
||||
evintString.emit("substract", 3, to_string(2));
|
||||
|
||||
sleep(1);
|
||||
evoid.tick("void");
|
||||
evoid.emit("void");
|
||||
|
||||
// Turn off the event listener
|
||||
|
||||
evoid.off("void");
|
||||
evoid.tick("void"); // nothing is happening
|
||||
evoid.emit("void"); // nothing is happening
|
||||
|
||||
```
|
||||
Extend own class whit events
|
||||
|
||||
```c++
|
||||
class myOwnClass : public Trigger<int> {
|
||||
class myOwnClass : public event<int> {
|
||||
public:
|
||||
myOwnClass() : Trigger(asynco_default_runtime()) {};
|
||||
myOwnClass() : event() {};
|
||||
};
|
||||
|
||||
myOwnClass myclass;
|
||||
|
||||
Delayed t( [&] {
|
||||
myclass.tick("constructed", 1);
|
||||
timeout t( [&] {
|
||||
myclass.emit("constructed", 1);
|
||||
}, 200);
|
||||
|
||||
myclass.on("constructed", [] (int i) {
|
||||
@ -259,110 +230,46 @@ myclass.on("constructed", [] (int i) {
|
||||
|
||||
```
|
||||
|
||||
Implementing a class with multiple triggers of different types
|
||||
Asynchronous file IO
|
||||
|
||||
```c++
|
||||
string data_;
|
||||
|
||||
class ClassWithTriggers {
|
||||
Trigger<int> emitter1;
|
||||
Trigger<string> emitter2;
|
||||
|
||||
public:
|
||||
ClassWithTriggers(): emitter1(asynco_default_runtime()), emitter2(asynco_default_runtime()) {}
|
||||
|
||||
template<typename... T>
|
||||
void on(const string& key, function<void(T...)> callback) {
|
||||
if constexpr (sizeof...(T) == 1 && is_same_v<tuple_element_t<0, tuple<T...>>, int>) {
|
||||
emitter1.on(key, callback);
|
||||
}
|
||||
else if constexpr (sizeof...(T) == 1 && is_same_v<tuple_element_t<0, tuple<T...>>, string>) {
|
||||
emitter2.on(key, callback);
|
||||
}
|
||||
fs::read("test.txt", [&data_] (string data, exception* error) {
|
||||
if (error) {
|
||||
cout << "Error " << error->what() << endl;
|
||||
} else {
|
||||
cout << "Data " << endl << data << endl;
|
||||
data_ = data;
|
||||
cout << "Data_" << data_ << endl;
|
||||
}
|
||||
});
|
||||
|
||||
template <typename... Args>
|
||||
void tick(const string& key, Args&&... args) {
|
||||
if constexpr (sizeof...(Args) == 1 && is_same_v<tuple_element_t<0, tuple<Args...>>, int>) {
|
||||
emitter1.tick(key, forward<Args>(args)...);
|
||||
}
|
||||
else if constexpr (sizeof...(Args) == 1 && is_same_v<tuple_element_t<0, tuple<Args...>>, string>) {
|
||||
emitter2.tick(key, forward<Args>(args)...);
|
||||
}
|
||||
else {
|
||||
static_assert(sizeof...(Args) == 0, "Unsupported number or types of arguments");
|
||||
}
|
||||
fs::write("test1.txt", "Hello world", [] (exception* error) {
|
||||
if (error) {
|
||||
cout << "Error " << error->what() << endl;
|
||||
} else {
|
||||
cout << "Write successfuly" << endl;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
ClassWithTriggers mt;
|
||||
auto future_data = fs::read("test.txt");
|
||||
|
||||
mt.on<int>("int", function<void(int)>([&](int i) {
|
||||
cout << "Emit int " << i << endl;
|
||||
}));
|
||||
try {
|
||||
string data = wait(future_data);
|
||||
} catch (exception& err) {
|
||||
cout << err.what() << endl;
|
||||
}
|
||||
|
||||
mt.on<string>("string", function<void(string)>([&](string s) {
|
||||
cout << "Emit string " << s << endl;
|
||||
}));
|
||||
auto future_status = fs::write("test.txt", "Hello world");
|
||||
|
||||
mt.tick("int", 5);
|
||||
mt.tick("string", string("Hello world"));
|
||||
|
||||
```
|
||||
|
||||
## Coroutine
|
||||
|
||||
If `define.hpp` is included, you can initialize coroutines with `boost::asio::awaitable<T>`.
|
||||
|
||||
```c++
|
||||
|
||||
awaitable<int> c2(int a) {
|
||||
co_return a * 2;
|
||||
try {
|
||||
wait(future_status);
|
||||
} catch (exception& err) {
|
||||
cout << err.what() << endl;
|
||||
}
|
||||
|
||||
```
|
||||
To run the coroutine at runtime, simply call:
|
||||
```c++
|
||||
|
||||
async_(c2(4));
|
||||
|
||||
```
|
||||
Or using a lambda expression:
|
||||
|
||||
```c++
|
||||
|
||||
async_([]() -> awaitable<void> {
|
||||
std::cout << "Hello" << std::endl;
|
||||
co_await c2(4);
|
||||
co_return;
|
||||
}());
|
||||
|
||||
```
|
||||
To retrieve results from coroutines, you can do so as you would from classical functions by calling `await_`:
|
||||
```c++
|
||||
|
||||
int r = await_(
|
||||
async_(
|
||||
c2(10)
|
||||
));
|
||||
|
||||
```
|
||||
|
||||
If you need the result immediately, you can use a shorter notation
|
||||
|
||||
```c++
|
||||
|
||||
auto a = await_ ( c2(3));
|
||||
cout << a << endl;
|
||||
|
||||
await_ ([]() -> awaitable<void> {
|
||||
cout << "Hello" << endl;
|
||||
co_return;
|
||||
}());
|
||||
|
||||
```
|
||||
|
||||
If you need a result, you can also retrieve it with `await_`.
|
||||
Here too you can use your own runtime instance, only the methods are `.async()` and `.await()`
|
||||
|
||||
## License
|
||||
|
||||
|
194
lib/asynco.hpp
194
lib/asynco.hpp
@ -1,187 +1,39 @@
|
||||
#ifndef _ASYNCO_
|
||||
#define _ASYNCO_
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <thread>
|
||||
#include <future>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include "runner.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#if __cplusplus >= 202002L
|
||||
#include <boost/asio/awaitable.hpp>
|
||||
#include <boost/asio/co_spawn.hpp>
|
||||
#include <boost/asio/use_awaitable.hpp>
|
||||
#endif
|
||||
using namespace boost::asio;
|
||||
|
||||
#include "timers.hpp"
|
||||
#include "trigger.hpp"
|
||||
|
||||
namespace marcelb {
|
||||
namespace asynco {
|
||||
|
||||
/**
|
||||
* Asynco runtime
|
||||
* Used for all asynchronous capabilities of this wrapper
|
||||
* Initializes threads and boost::asio::io_context
|
||||
* Run the function asynchronously
|
||||
*/
|
||||
class Asynco {
|
||||
vector<thread> _runners;
|
||||
unique_ptr<io_service::work> _work;
|
||||
template<class F, class... Args>
|
||||
auto atask(F&& f, Args&&... args) -> future<typename result_of<F(Args...)>::type> {
|
||||
using return_type = typename result_of<F(Args...)>::type;
|
||||
|
||||
void init_loops_in_threads(uint8_t threads);
|
||||
future<return_type> res = _asyncon.put_task(bind(forward<F>(f), forward<Args>(args)...));
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
io_context io_ctx;
|
||||
|
||||
/**
|
||||
* It starts the thread initialization and the Boost::Asio event loop in each of them
|
||||
*/
|
||||
|
||||
void run(uint8_t threads = thread::hardware_concurrency());
|
||||
|
||||
/**
|
||||
* Starts Boost::Asio event loop in the current thread
|
||||
*/
|
||||
|
||||
void run_on_this();
|
||||
|
||||
/**
|
||||
* Waits until all threads have finished working
|
||||
*/
|
||||
|
||||
void join();
|
||||
|
||||
/**
|
||||
* Run the function asynchronously in runtime
|
||||
*/
|
||||
template<class F, class... Args>
|
||||
auto async(F&& f, Args&&... args) -> future<invoke_result_t<F, Args...>> {
|
||||
using return_type = invoke_result_t<F, Args...>;
|
||||
future<return_type> res = io_ctx.post(boost::asio::use_future(bind(forward<F>(f), forward<Args>(args)...)));
|
||||
return res;
|
||||
}
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
/**
|
||||
* Run the coroutine in runtime
|
||||
*/
|
||||
template <typename T>
|
||||
future<T> async(boost::asio::awaitable<T> _coroutine) {
|
||||
promise<T> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
co_spawn(io_ctx, [_coroutine = move(_coroutine), promise = move(promise)]() mutable -> boost::asio::awaitable<void> {
|
||||
try {
|
||||
if constexpr (!is_void_v<T>) {
|
||||
T result = co_await move(_coroutine);
|
||||
promise.set_value(move(result));
|
||||
} else {
|
||||
co_await move(_coroutine);
|
||||
promise.set_value(); // Za void ne postavljamo rezultat
|
||||
}
|
||||
} catch (...) {
|
||||
promise.set_exception(current_exception()); // Postavljamo izuzetak
|
||||
}
|
||||
}, boost::asio::detached);
|
||||
|
||||
return future;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Wait until the asynchronous call completes
|
||||
*/
|
||||
template<typename T>
|
||||
T await(future<T>& r, uint16_t time_us = 10) {
|
||||
while (r.wait_for(std::chrono::microseconds(time_us)) != future_status::ready) {
|
||||
io_ctx.poll_one();
|
||||
}
|
||||
return r.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the asynchronous call completes
|
||||
*/
|
||||
template<typename T>
|
||||
T await(future<T>&& r, uint16_t time_us = 10) {
|
||||
while (r.wait_for(std::chrono::microseconds(time_us)) != future_status::ready) {
|
||||
io_ctx.poll_one();
|
||||
}
|
||||
return move(r).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the function asynchronously an wait until completes
|
||||
*/
|
||||
template<class F, class... Args>
|
||||
auto await(F&& f, Args&&... args) -> invoke_result_t<F, Args...> {
|
||||
return await(
|
||||
async(f, args...)
|
||||
);
|
||||
}
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
/**
|
||||
* Run the coruotine and wait
|
||||
*/
|
||||
template <typename T>
|
||||
T await(boost::asio::awaitable<T> _coroutine) {
|
||||
return await(
|
||||
async(
|
||||
move(_coroutine)
|
||||
));
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Wait until the multiple asynchronous call completes
|
||||
* Use only on no-void calls
|
||||
*/
|
||||
|
||||
template<typename... F>
|
||||
auto await(F&&... f) -> tuple<typename decay<decltype(await(f))>::type...> {
|
||||
return make_tuple(await(f)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the multiple asynchronous call completes
|
||||
* Use only on no-void calls
|
||||
*/
|
||||
|
||||
template<typename... F>
|
||||
auto await(F&... f) -> tuple<typename decay<decltype(await(f))>::type...> {
|
||||
return make_tuple(await(f)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the delayed timer
|
||||
*/
|
||||
|
||||
Timer delayed(function<void()> callback, uint64_t time);
|
||||
|
||||
/**
|
||||
* Initialize the periodic timer
|
||||
*/
|
||||
|
||||
Timer periodic(function<void()> callback, uint64_t time);
|
||||
|
||||
/**
|
||||
* Initialize trigger (typed event)
|
||||
*/
|
||||
|
||||
template<typename... T>
|
||||
Trigger<T...> trigger() {
|
||||
return Trigger<T...>(*this);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
/**
|
||||
* Block until the asynchronous call completes
|
||||
*/
|
||||
template<typename T>
|
||||
T wait(future<T>& r) {
|
||||
return r.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Block until the asynchronous call completes
|
||||
*/
|
||||
template<typename T>
|
||||
T wait(future<T>&& r) {
|
||||
return move(r).get();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,155 +0,0 @@
|
||||
#ifndef _ASYNCO_DEFAULT_
|
||||
#define _ASYNCO_DEFAULT_
|
||||
|
||||
#include "asynco.hpp"
|
||||
|
||||
namespace marcelb {
|
||||
namespace asynco {
|
||||
|
||||
/**
|
||||
* Default runtime
|
||||
*/
|
||||
|
||||
extern Asynco Asynco_Default_Runtime;
|
||||
|
||||
/**
|
||||
* Run the function asynchronously in default runtime
|
||||
*/
|
||||
template<class F, class... Args>
|
||||
auto async_(F&& f, Args&&... args) -> future<invoke_result_t<F, Args...>> {
|
||||
return Asynco_Default_Runtime.async(bind(forward<F>(f), forward<Args>(args)...));
|
||||
}
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
/**
|
||||
* Run the coroutine in default runtime
|
||||
*/
|
||||
template <typename T>
|
||||
std::future<T> async_(boost::asio::awaitable<T> _coroutine) {
|
||||
return Asynco_Default_Runtime.async(move(_coroutine));
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Wait until the asynchronous call completes
|
||||
*/
|
||||
template<typename T>
|
||||
T await_(future<T>& r, uint16_t time_us = 10) {
|
||||
return Asynco_Default_Runtime.await(r, time_us);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the asynchronous call completes
|
||||
*/
|
||||
template<typename T>
|
||||
T await_(future<T>&& r, uint16_t time_us = 10) {
|
||||
return Asynco_Default_Runtime.await(r, time_us);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the function asynchronously an wait until completes
|
||||
*/
|
||||
template<class F, class... Args>
|
||||
auto await_(F&& f, Args&&... args) -> invoke_result_t<F, Args...> {
|
||||
return Asynco_Default_Runtime.await(bind(forward<F>(f), forward<Args>(args)...));
|
||||
}
|
||||
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
/**
|
||||
* Run the coruotine and wait
|
||||
*/
|
||||
template <typename T>
|
||||
T await_(boost::asio::awaitable<T> _coroutine) {
|
||||
return Asynco_Default_Runtime.await(move(_coroutine));
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Wait until the multiple asynchronous call completes
|
||||
* Use only on no-void calls
|
||||
*/
|
||||
|
||||
template<typename... F>
|
||||
auto await_(F&&... f) -> std::tuple<typename std::decay<decltype(Asynco_Default_Runtime.await(f))>::type...> {
|
||||
return Asynco_Default_Runtime.await(move(f)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the multiple asynchronous call completes
|
||||
* Use only on no-void calls
|
||||
*/
|
||||
|
||||
template<typename... F>
|
||||
auto await_(F&... f) -> std::tuple<typename std::decay<decltype(Asynco_Default_Runtime.await(f))>::type...> {
|
||||
return Asynco_Default_Runtime.await(f...);;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the delayed timer
|
||||
*/
|
||||
|
||||
Timer delayed(function<void()> callback, uint64_t time);
|
||||
|
||||
/**
|
||||
* Initialize the periodic timer
|
||||
*/
|
||||
|
||||
Timer periodic(function<void()> callback, uint64_t time);
|
||||
|
||||
/**
|
||||
* Initialize trigger (typed event)
|
||||
*/
|
||||
|
||||
template<typename... T>
|
||||
Trigger<T...> trigger() {
|
||||
return Trigger<T...>(Asynco_Default_Runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reference of default runtime
|
||||
*/
|
||||
|
||||
Asynco& asynco_default_runtime();
|
||||
|
||||
/**
|
||||
* Run default runtime
|
||||
*/
|
||||
|
||||
void asynco_default_run();
|
||||
|
||||
/**
|
||||
* Run default runtime in this thread
|
||||
*/
|
||||
|
||||
void asynco_default_run_on_this();
|
||||
|
||||
/**
|
||||
* Waits until all threads have finished working
|
||||
*/
|
||||
|
||||
void asynco_default_join();
|
||||
|
||||
/**
|
||||
* Get reference of boost::asio::io_context
|
||||
*/
|
||||
|
||||
io_context& asynco_default_io_context();
|
||||
|
||||
/**
|
||||
* Run the function asynchronously in default runtime
|
||||
*/
|
||||
#define async_ marcelb::asynco::async_
|
||||
|
||||
/**
|
||||
* Wait until the asynchronous call completes
|
||||
*/
|
||||
#define await_ marcelb::asynco::await_
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
66
lib/event.hpp
Normal file
66
lib/event.hpp
Normal file
@ -0,0 +1,66 @@
|
||||
#ifndef _EVENT_
|
||||
#define _EVENT_
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include "runner.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace marcelb {
|
||||
namespace asynco {
|
||||
namespace events {
|
||||
|
||||
/**
|
||||
* Event class, for event-driven programming.
|
||||
* These events are typed according to the arguments of the callback function
|
||||
*/
|
||||
template<typename... T>
|
||||
class event {
|
||||
private:
|
||||
mutex m_eve;
|
||||
unordered_map<string, vector<function<void(T...)>>> events;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Defines event by key, and callback function
|
||||
*/
|
||||
void on(const string& key, function<void(T...)> callback) {
|
||||
lock_guard _off(m_eve);
|
||||
events[key].push_back(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* It emits an event and sends a callback function saved according to the key with the passed parameters
|
||||
*/
|
||||
template<typename... Args>
|
||||
void emit(const string& key, Args... args) {
|
||||
auto it_eve = events.find(key);
|
||||
if (it_eve != events.end()) {
|
||||
for (uint i =0; i<it_eve->second.size(); i++) {
|
||||
auto callback = bind(it_eve->second[i], forward<Args>(args)...);
|
||||
_asyncon.put_task(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an event listener from an event
|
||||
*/
|
||||
void off(const string& key) {
|
||||
lock_guard _off(m_eve);
|
||||
events.erase(key);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
116
lib/filesystem.hpp
Normal file
116
lib/filesystem.hpp
Normal file
@ -0,0 +1,116 @@
|
||||
#ifndef _ASYNCO_FS_
|
||||
#define _ASYNCO_FS_
|
||||
|
||||
#include "asynco.hpp"
|
||||
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace marcelb;
|
||||
using namespace asynco;
|
||||
|
||||
namespace marcelb {
|
||||
namespace asynco {
|
||||
namespace fs {
|
||||
|
||||
/**
|
||||
* Asynchronous file reading with callback after read complete
|
||||
*/
|
||||
template<typename Callback>
|
||||
void read(string path, Callback&& callback) {
|
||||
atask( [&path, callback] () {
|
||||
string content;
|
||||
try {
|
||||
string line;
|
||||
ifstream file (path);
|
||||
if (file.is_open()) {
|
||||
line.clear();
|
||||
while ( getline (file,line) ) {
|
||||
content += line + "\n";
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
else {
|
||||
throw runtime_error("Unable to open file");
|
||||
}
|
||||
|
||||
callback(content, nullptr);
|
||||
} catch(exception& error) {
|
||||
callback(content, &error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asynchronous file reading
|
||||
*/
|
||||
future<string> read(string path) {
|
||||
return atask( [&path] () {
|
||||
string content;
|
||||
string line;
|
||||
ifstream file (path);
|
||||
if (file.is_open()) {
|
||||
line.clear();
|
||||
while ( getline (file,line) ) {
|
||||
content += line + "\n";
|
||||
}
|
||||
file.close();
|
||||
return content;
|
||||
}
|
||||
|
||||
else {
|
||||
throw runtime_error("Unable to open file");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronous file writing with callback after write complete
|
||||
*/
|
||||
template<typename Callback>
|
||||
void write(string path, string content, Callback&& callback) {
|
||||
atask( [&path, &content, callback] () {
|
||||
try {
|
||||
ofstream file (path);
|
||||
if (file.is_open()) {
|
||||
file << content;
|
||||
file.close();
|
||||
}
|
||||
else {
|
||||
throw runtime_error("Unable to open file");
|
||||
}
|
||||
|
||||
callback(nullptr);
|
||||
} catch(exception& error) {
|
||||
callback(&error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asynchronous file writing with callback after write complete
|
||||
*/
|
||||
future<void> write(string path, string content) {
|
||||
return atask( [&path, &content] () {
|
||||
ofstream file (path);
|
||||
if (file.is_open()) {
|
||||
file << content;
|
||||
file.close();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
throw runtime_error("Unable to open file");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
239
lib/rotor.hpp
Normal file
239
lib/rotor.hpp
Normal file
@ -0,0 +1,239 @@
|
||||
#ifndef _ROTOR_
|
||||
#define _ROTOT_
|
||||
|
||||
#include "runner.hpp"
|
||||
#include "chrono"
|
||||
#include <memory>
|
||||
|
||||
#include "iostream"
|
||||
|
||||
using namespace std;
|
||||
using namespace marcelb;
|
||||
using namespace asynco;
|
||||
|
||||
namespace marcelb {
|
||||
namespace asynco {
|
||||
|
||||
/**
|
||||
* Get the time in ms from the epoch
|
||||
*/
|
||||
|
||||
int64_t rtime_ms() {
|
||||
return chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now()
|
||||
.time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
|
||||
int64_t rtime_us() {
|
||||
return chrono::duration_cast<chrono::microseconds>(chrono::system_clock::now()
|
||||
.time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Intern class for timer async loop
|
||||
*/
|
||||
class timer_core {
|
||||
public:
|
||||
mutex hangon;
|
||||
condition_variable cv;
|
||||
function<void()> callback;
|
||||
int64_t time;
|
||||
int64_t next;
|
||||
bool repeat;
|
||||
bool stop;
|
||||
|
||||
/**
|
||||
* Timer constructor, receives a callback function and time
|
||||
*/
|
||||
timer_core( function<void()> _callback, int64_t _time, bool _repeat):
|
||||
callback(_callback), time(_time*1000), repeat(_repeat), stop(false) {
|
||||
next = rtime_us() + time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop timer
|
||||
*/
|
||||
void clear() {
|
||||
// lock_guard<mutex> hang(hangon);
|
||||
stop = true;
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destruktor of timer, call stop
|
||||
*/
|
||||
~timer_core() {
|
||||
clear();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Event loop for time events
|
||||
*/
|
||||
|
||||
class rotor {
|
||||
vector<shared_ptr<timer_core>> tcores;
|
||||
mutex te_m;
|
||||
bool rotating = true;
|
||||
int64_t sampling;
|
||||
|
||||
condition_variable te_cv;
|
||||
|
||||
/**
|
||||
* Loop method, started by the constructor in a separate runner
|
||||
* It checks the events on the stack and sends the expired ones to the runner
|
||||
*/
|
||||
void loop() {
|
||||
while (rotating) {
|
||||
vector<shared_ptr<timer_core>>::iterator next_tc;
|
||||
shared_ptr<timer_core> next_ptr;
|
||||
|
||||
{
|
||||
unique_lock<mutex> te_l(te_m);
|
||||
te_cv.wait(te_l, [this]{ return !tcores.empty() || rotating; });
|
||||
if (!rotating) {
|
||||
break;
|
||||
}
|
||||
|
||||
next_tc = min_element( tcores.begin(), tcores.end(),
|
||||
[](shared_ptr<timer_core> a, shared_ptr<timer_core> b ) {
|
||||
return a->next < b->next;
|
||||
}
|
||||
);
|
||||
|
||||
next_ptr = *next_tc;
|
||||
}
|
||||
|
||||
unique_lock<mutex> next_l(next_ptr->hangon);
|
||||
next_ptr->cv.wait_for(next_l, chrono::microseconds(next_ptr->next - rtime_us()), [&next_ptr] () {
|
||||
return next_ptr->stop;
|
||||
});
|
||||
|
||||
if (next_ptr->stop) {
|
||||
remove(next_tc);
|
||||
} else {
|
||||
_asyncon.put_task(next_ptr->callback);
|
||||
if (next_ptr->repeat) {
|
||||
next_ptr->next += next_ptr->time;
|
||||
}
|
||||
else {
|
||||
remove(next_tc);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The method deletes a non-repeating or stopped event from the stack
|
||||
*/
|
||||
void remove(vector<shared_ptr<timer_core>>::iterator it) {
|
||||
lock_guard<mutex> lock(te_m);
|
||||
tcores.erase(it);
|
||||
// te_cv.notify_one();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for the rotor, starts the given loop by occupying one runner
|
||||
*/
|
||||
rotor() {
|
||||
_asyncon.put_task( [&] () {
|
||||
loop();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a time event to the stack
|
||||
*/
|
||||
void insert(shared_ptr<timer_core> tcore) {
|
||||
lock_guard<mutex> lock(te_m);
|
||||
tcores.push_back(tcore);
|
||||
te_cv.notify_one();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the number of active events
|
||||
*/
|
||||
int active() {
|
||||
return tcores.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops all active events and stops the rotor
|
||||
*/
|
||||
~rotor() {
|
||||
for (int i=0; i<tcores.size(); i++) {
|
||||
tcores[i]->clear();
|
||||
}
|
||||
rotating = false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* It is intended that there is only one global declaration
|
||||
*/
|
||||
static rotor _rotor;
|
||||
|
||||
/**
|
||||
* Core class for pure async timer functions
|
||||
*/
|
||||
|
||||
class _timer_intern {
|
||||
shared_ptr<timer_core> tcore;
|
||||
public:
|
||||
|
||||
_timer_intern(function<void()> _callback, int64_t _time, bool repeat) {
|
||||
tcore = make_shared<timer_core>(_callback, _time, repeat);
|
||||
_rotor.insert(tcore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop interval
|
||||
*/
|
||||
void clear() {
|
||||
tcore->clear();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class interval for periodic execution of the callback in time in ms
|
||||
*/
|
||||
class interval : public _timer_intern {
|
||||
public:
|
||||
|
||||
/**
|
||||
* The constructor receives a callback function and an interval time
|
||||
*/
|
||||
interval( function<void()> _callback, int64_t _time):
|
||||
_timer_intern(_callback, _time, true) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class interval for delayed callback execution in ms
|
||||
*/
|
||||
class timeout : public _timer_intern {
|
||||
public:
|
||||
|
||||
/**
|
||||
* The constructor receives a callback function and a delay time
|
||||
*/
|
||||
timeout( function<void()> _callback, int64_t delay):
|
||||
_timer_intern(_callback, delay, false) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
136
lib/runner.hpp
Normal file
136
lib/runner.hpp
Normal file
@ -0,0 +1,136 @@
|
||||
#ifndef _RUNNER_
|
||||
#define _RUNNER_
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <future>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace marcelb {
|
||||
namespace asynco {
|
||||
|
||||
#define HW_CONCURRENCY_MINIMAL 4
|
||||
|
||||
/**
|
||||
* The runner class implements multithread, task stack and event loop for asynchronous execution of tasks
|
||||
*/
|
||||
class runner {
|
||||
private:
|
||||
vector<thread> runners;
|
||||
queue<function<void()>> tasks;
|
||||
mutex q_io;
|
||||
condition_variable cv;
|
||||
bool stop;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* The constructor starts as many threads as the system has cores,
|
||||
* and runs an event loop inside each one.
|
||||
* Each event loop waits for tasks from the stack and executes them.
|
||||
*/
|
||||
runner(unsigned int _num_of_runners = 0) : stop(false) {
|
||||
unsigned int num_of_runners = _num_of_runners;
|
||||
|
||||
if (num_of_runners == 0) {
|
||||
#ifdef NUM_OF_RUNNERS
|
||||
num_of_runners = NUM_OF_RUNNERS;
|
||||
#else
|
||||
num_of_runners = thread::hardware_concurrency();
|
||||
if (num_of_runners < HW_CONCURRENCY_MINIMAL) {
|
||||
num_of_runners = HW_CONCURRENCY_MINIMAL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_of_runners; ++i) {
|
||||
runners.emplace_back( thread([&] {
|
||||
while (!stop) {
|
||||
function<void()> task;
|
||||
{
|
||||
unique_lock<mutex> lock(q_io);
|
||||
cv.wait(lock, [this] { return stop || !tasks.empty(); });
|
||||
// if (stop && tasks.empty())
|
||||
if (stop)
|
||||
return;
|
||||
task = move(tasks.front());
|
||||
tasks.pop();
|
||||
}
|
||||
task();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* With the method, we send the callback function and its arguments to the task stack
|
||||
*/
|
||||
template<class F, class... Args>
|
||||
auto put_task(F&& f, Args&&... args)
|
||||
-> future<typename result_of<F(Args...)>::type> {
|
||||
using return_type = typename result_of<F(Args...)>::type;
|
||||
|
||||
auto task = make_shared<packaged_task<return_type()>>(bind(forward<F>(f), forward<Args>(args)...));
|
||||
future<return_type> res = task->get_future();
|
||||
{
|
||||
unique_lock<mutex> lock(q_io);
|
||||
|
||||
if (stop) {
|
||||
throw runtime_error("Pool is stoped!");
|
||||
}
|
||||
|
||||
tasks.emplace([task]() { (*task)(); });
|
||||
}
|
||||
|
||||
cv.notify_one();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tasks the runner has to perform
|
||||
*/
|
||||
unsigned int count_tasks() {
|
||||
return tasks.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of threads used by the runner
|
||||
*/
|
||||
unsigned int count_threads() {
|
||||
return runners.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* The destructor stops all loops and stops all threads
|
||||
*/
|
||||
~runner() {
|
||||
{
|
||||
unique_lock<mutex> lock(q_io);
|
||||
stop = true;
|
||||
}
|
||||
cv.notify_all();
|
||||
for (thread& runner : runners) {
|
||||
runner.join();
|
||||
}
|
||||
runners.clear();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Internal global library variable
|
||||
*/
|
||||
static runner _asyncon;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,91 +0,0 @@
|
||||
#ifndef _ASYNCO_TIMERS_
|
||||
#define _ASYNCO_TIMERS_
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
using namespace boost::asio;
|
||||
|
||||
namespace marcelb {
|
||||
namespace asynco {
|
||||
|
||||
/**
|
||||
* Get the time in ms from the epoch
|
||||
*/
|
||||
|
||||
int64_t rtime_ms();
|
||||
|
||||
/**
|
||||
* Get the time in us from the epoch
|
||||
*/
|
||||
|
||||
int64_t rtime_us();
|
||||
|
||||
enum TimerType {
|
||||
Delayed,
|
||||
Periodic
|
||||
};
|
||||
|
||||
/**
|
||||
* Core timer class for construct time async functions
|
||||
*/
|
||||
class Timer {
|
||||
io_context& io_ctx;
|
||||
steady_timer st;
|
||||
bool _stop = false;
|
||||
TimerType type;
|
||||
function<void()> callback;
|
||||
uint64_t time;
|
||||
uint64_t _ticks = 0;
|
||||
|
||||
/**
|
||||
* A method to assign a callback wrapper and a reinitialization algorithm
|
||||
*/
|
||||
void init();
|
||||
public:
|
||||
|
||||
/**
|
||||
* The constructor creates the steady_timer and accompanying variables and runs a method to initialize the timer
|
||||
*/
|
||||
Timer (io_context& io_ctx, function<void()> _callback, uint64_t _time, TimerType _type = TimerType::Delayed);
|
||||
|
||||
/**
|
||||
* Stop timer
|
||||
* The stop flag is set and timer remove it from the queue
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Run callback now
|
||||
* Forces the callback function to run independently of the timer
|
||||
*/
|
||||
void now();
|
||||
|
||||
/**
|
||||
* Get the number of times the timer callback was runned
|
||||
*/
|
||||
uint64_t ticks();
|
||||
|
||||
/**
|
||||
* Get is the delayed callback runned
|
||||
*/
|
||||
bool expired();
|
||||
|
||||
/**
|
||||
* The logic status of the timer stop state
|
||||
*/
|
||||
bool stoped();
|
||||
|
||||
/**
|
||||
* The destructor stops the timer
|
||||
*/
|
||||
~Timer();
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,95 +0,0 @@
|
||||
#ifndef _ASYNCO_TRIGGER_
|
||||
#define _ASYNCO_TRIGGER_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include "asynco.hpp"
|
||||
namespace marcelb {
|
||||
namespace asynco {
|
||||
|
||||
class Asynco;
|
||||
|
||||
/**
|
||||
* Trigger class, for event-driven programming.
|
||||
* These events are typed according to the arguments of the callback function
|
||||
*/
|
||||
template<typename... T>
|
||||
class Trigger {
|
||||
private:
|
||||
Asynco& engine;
|
||||
mutex m_eve;
|
||||
unordered_map<string, vector<function<void(T...)>>> triggers;
|
||||
|
||||
public:
|
||||
Trigger(Asynco& _engine)
|
||||
: engine(_engine) {}
|
||||
|
||||
/**
|
||||
* Defines event by key, and callback function
|
||||
*/
|
||||
void on(const string& key, function<void(T...)> callback) {
|
||||
lock_guard _off(m_eve);
|
||||
triggers[key].push_back(callback);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* It emits an event and sends a callback function saved according to the key with the passed parameters
|
||||
*/
|
||||
template<typename... Args>
|
||||
void tick(const string& key, Args... args) {
|
||||
auto it_eve = triggers.find(key);
|
||||
if (it_eve != triggers.end()) {
|
||||
for (uint i =0; i<it_eve->second.size(); i++) {
|
||||
auto callback = bind(it_eve->second[i], forward<Args>(args)...);
|
||||
engine.async(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an Trigger listener from an event
|
||||
*/
|
||||
void off(const string& key) {
|
||||
lock_guard _off(m_eve);
|
||||
triggers.erase(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all Trigger listener
|
||||
*/
|
||||
void off() {
|
||||
lock_guard _off(m_eve);
|
||||
triggers.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get num of listeners by an Trigger key
|
||||
*/
|
||||
unsigned int listeners(const string& key) {
|
||||
return triggers[key].size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get num of all listeners
|
||||
*/
|
||||
unsigned int listeners() {
|
||||
unsigned int listeners = 0;
|
||||
for (auto& ev : triggers) {
|
||||
listeners += ev.second.size();
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,41 +0,0 @@
|
||||
#include "../lib/asynco.hpp"
|
||||
|
||||
namespace marcelb::asynco {
|
||||
|
||||
|
||||
void Asynco::init_loops_in_threads(uint8_t threads) {
|
||||
for (int i=0; i<threads; i++) {
|
||||
_runners.push_back(thread ( [this] () {
|
||||
io_ctx.run();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void Asynco::run(uint8_t threads) {
|
||||
_work = make_unique<io_service::work>(io_ctx);
|
||||
init_loops_in_threads(threads);
|
||||
}
|
||||
|
||||
void Asynco::run_on_this() {
|
||||
if (!_work) {
|
||||
_work = make_unique<io_service::work>(io_ctx);
|
||||
}
|
||||
io_ctx.run();
|
||||
}
|
||||
|
||||
void Asynco::join() {
|
||||
for (auto& runner : _runners) {
|
||||
runner.join();
|
||||
}
|
||||
}
|
||||
|
||||
Timer Asynco::delayed(function<void()> callback, uint64_t time) {
|
||||
return Timer(io_ctx, callback, time, TimerType::Delayed);
|
||||
}
|
||||
|
||||
Timer Asynco::periodic(function<void()> callback, uint64_t time) {
|
||||
return Timer(io_ctx, callback, time, TimerType::Periodic);
|
||||
}
|
||||
|
||||
|
||||
};
|
@ -1,36 +0,0 @@
|
||||
|
||||
#include "../lib/asynco_default.hpp"
|
||||
|
||||
namespace marcelb::asynco {
|
||||
|
||||
Asynco Asynco_Default_Runtime;
|
||||
|
||||
Timer delayed(function<void()> callback, uint64_t time) {
|
||||
return Timer(Asynco_Default_Runtime.io_ctx, callback, time, TimerType::Delayed);
|
||||
}
|
||||
|
||||
Timer periodic(function<void()> callback, uint64_t time) {
|
||||
return Timer(Asynco_Default_Runtime.io_ctx, callback, time, TimerType::Periodic);
|
||||
}
|
||||
|
||||
Asynco& asynco_default_runtime() {
|
||||
return Asynco_Default_Runtime;
|
||||
}
|
||||
|
||||
void asynco_default_run() {
|
||||
Asynco_Default_Runtime.run();
|
||||
}
|
||||
|
||||
void asynco_default_run_on_this() {
|
||||
Asynco_Default_Runtime.run_on_this();
|
||||
}
|
||||
|
||||
void asynco_default_join() {
|
||||
Asynco_Default_Runtime.join();
|
||||
}
|
||||
|
||||
io_context& asynco_default_io_context() {
|
||||
return Asynco_Default_Runtime.io_ctx;
|
||||
}
|
||||
|
||||
};
|
@ -1,65 +0,0 @@
|
||||
#include "../lib/timers.hpp"
|
||||
|
||||
namespace marcelb::asynco {
|
||||
|
||||
int64_t rtime_ms() {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()
|
||||
.time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
|
||||
int64_t rtime_us() {
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now()
|
||||
.time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
|
||||
void Timer::init() {
|
||||
st.async_wait( [this] (const boost::system::error_code&) {
|
||||
if (!_stop) {
|
||||
callback();
|
||||
if (type == TimerType::Periodic) {
|
||||
st = steady_timer(io_ctx, boost::asio::chrono::milliseconds(time));
|
||||
init();
|
||||
}
|
||||
_ticks++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Timer::Timer (io_context& _io_ctx, function<void()> _callback, uint64_t _time, TimerType _type):
|
||||
io_ctx(_io_ctx),
|
||||
st(io_ctx, boost::asio::chrono::milliseconds(_time)),
|
||||
_stop(false),
|
||||
type(_type),
|
||||
callback(_callback),
|
||||
time(_time) {
|
||||
init();
|
||||
}
|
||||
|
||||
void Timer::stop() {
|
||||
_stop = true;
|
||||
st.cancel();
|
||||
}
|
||||
|
||||
void Timer::now() {
|
||||
st.cancel();
|
||||
}
|
||||
|
||||
uint64_t Timer::ticks() {
|
||||
return _ticks;
|
||||
}
|
||||
|
||||
bool Timer::expired() {
|
||||
return bool(_ticks);
|
||||
}
|
||||
|
||||
bool Timer::stoped() {
|
||||
return _stop;
|
||||
}
|
||||
|
||||
Timer::~Timer() {
|
||||
stop();
|
||||
}
|
||||
|
||||
};
|
@ -1,29 +0,0 @@
|
||||
add_executable(asynco_default main_default.cpp)
|
||||
target_link_libraries(asynco_default asynco Boost::system)
|
||||
|
||||
add_executable(asynco_init main_init.cpp)
|
||||
target_link_libraries(asynco_init asynco Boost::system)
|
||||
|
||||
add_executable(asynco_async_default main_async_default.cpp)
|
||||
target_link_libraries(asynco_async_default asynco Boost::system)
|
||||
|
||||
add_executable(asynco_async main_async.cpp)
|
||||
target_link_libraries(asynco_async asynco Boost::system)
|
||||
|
||||
add_executable(asynco_timers_default main_timers_default.cpp)
|
||||
target_link_libraries(asynco_timers_default asynco Boost::system)
|
||||
|
||||
add_executable(asynco_timers main_timers.cpp)
|
||||
target_link_libraries(asynco_timers asynco Boost::system)
|
||||
|
||||
add_executable(asynco_trigger_default main_trigger_default.cpp)
|
||||
target_link_libraries(asynco_trigger_default asynco Boost::system)
|
||||
|
||||
add_executable(asynco_trigger main_trigger.cpp)
|
||||
target_link_libraries(asynco_trigger asynco Boost::system)
|
||||
|
||||
add_executable(asynco_coroutine_default main_coroutine_default.cpp)
|
||||
target_link_libraries(asynco_coroutine_default asynco Boost::system)
|
||||
|
||||
add_executable(asynco_coroutine main_coroutine.cpp)
|
||||
target_link_libraries(asynco_coroutine asynco Boost::system)
|
@ -1,84 +0,0 @@
|
||||
#include "../lib/asynco.hpp"
|
||||
using namespace marcelb::asynco;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
Asynco asynco;
|
||||
|
||||
void notLambdaFunction() {
|
||||
cout << "Call to not lambda function" << endl;
|
||||
}
|
||||
|
||||
|
||||
class clm {
|
||||
public:
|
||||
void classMethode() {
|
||||
cout << "Call class method" << endl;
|
||||
}
|
||||
};
|
||||
|
||||
void sleep_to (int _time) {
|
||||
promise<void> _promise;
|
||||
Timer t = asynco.delayed( [&]() {
|
||||
_promise.set_value();
|
||||
}, _time);
|
||||
|
||||
return asynco.await(_promise.get_future());
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
asynco.run(2);
|
||||
|
||||
/**
|
||||
* Run an lambda function asynchronously
|
||||
*/
|
||||
|
||||
asynco.async ( []() {
|
||||
cout << "async " << endl;
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Run not lambda function
|
||||
*/
|
||||
|
||||
asynco.async (notLambdaFunction);
|
||||
|
||||
/**
|
||||
* Run class method
|
||||
*/
|
||||
|
||||
clm classes;
|
||||
asynco.async ( [&classes] () {
|
||||
classes.classMethode();
|
||||
});
|
||||
|
||||
//------------------AWAIT----------------------
|
||||
|
||||
auto a = asynco.async ( []() {
|
||||
sleep_to(1000); //only for simulating long duration function
|
||||
return 5;
|
||||
});
|
||||
|
||||
cout << asynco.await(a) << endl;
|
||||
|
||||
/**
|
||||
* await async function call and use i cout
|
||||
*/
|
||||
|
||||
cout << asynco.await(asynco.async ( [] () {
|
||||
sleep_to(1000);
|
||||
cout << "await_ end" << endl;
|
||||
return 4;
|
||||
})) << endl;
|
||||
|
||||
|
||||
asynco.await ([]() { // run in runtime and await now
|
||||
cout << "Hello" << endl;
|
||||
});
|
||||
|
||||
asynco.join();
|
||||
return 0;
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
#include "../lib/asynco_default.hpp"
|
||||
using namespace marcelb::asynco;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
void notLambdaFunction() {
|
||||
cout << "Call to not lambda function" << endl;
|
||||
}
|
||||
|
||||
|
||||
class clm {
|
||||
public:
|
||||
void classMethode() {
|
||||
cout << "Call class method" << endl;
|
||||
}
|
||||
};
|
||||
|
||||
void sleep_to (int _time) {
|
||||
promise<void> _promise;
|
||||
Timer t = delayed( [&]() {
|
||||
_promise.set_value();
|
||||
}, _time);
|
||||
|
||||
return await_(_promise.get_future());
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
asynco_default_run();
|
||||
|
||||
/**
|
||||
* Run an lambda function asynchronously
|
||||
*/
|
||||
|
||||
async_ ( []() {
|
||||
cout << "async " << endl;
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Run not lambda function
|
||||
*/
|
||||
|
||||
async_ (notLambdaFunction);
|
||||
|
||||
/**
|
||||
* Run class method
|
||||
*/
|
||||
|
||||
clm classes;
|
||||
async_ ( [&classes] () {
|
||||
classes.classMethode();
|
||||
});
|
||||
|
||||
//------------------AWAIT----------------------
|
||||
|
||||
auto a = async_ ( []() {
|
||||
sleep_to(1000); //only for simulating long duration function
|
||||
return 5;
|
||||
});
|
||||
|
||||
cout << await_(a) << endl;
|
||||
|
||||
/**
|
||||
* await async function call and use i cout
|
||||
*/
|
||||
|
||||
cout << await_(async_ ( [] () {
|
||||
sleep_to(1000);
|
||||
cout << "await_ end" << endl;
|
||||
return 4;
|
||||
})) << endl;
|
||||
|
||||
|
||||
await_ ([]() { // run in runtime and await now
|
||||
cout << "Hello" << endl;
|
||||
});
|
||||
|
||||
asynco_default_join();
|
||||
return 0;
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
#include "../lib/asynco.hpp"
|
||||
using namespace marcelb::asynco;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
awaitable<int> c2(int a) {
|
||||
co_return a * 2;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
Asynco asynco; // or global
|
||||
asynco.run(2);
|
||||
|
||||
asynco.async(c2(4));
|
||||
|
||||
asynco.async([]() -> awaitable<void> {
|
||||
std::cout << "Hello" << std::endl;
|
||||
co_await c2(4);
|
||||
co_return;
|
||||
}());
|
||||
|
||||
|
||||
int r = asynco.await(
|
||||
asynco.async(
|
||||
c2(10)
|
||||
));
|
||||
|
||||
|
||||
auto a = asynco.await( c2(3));
|
||||
cout << a << endl;
|
||||
|
||||
asynco.await([]() -> awaitable<void> {
|
||||
cout << "Hello" << endl;
|
||||
co_return;
|
||||
}());
|
||||
|
||||
|
||||
asynco.join();
|
||||
return 0;
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
#include "../lib/asynco_default.hpp"
|
||||
using namespace marcelb::asynco;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
awaitable<int> c2(int a) {
|
||||
co_return a * 2;
|
||||
}
|
||||
|
||||
int main() {
|
||||
asynco_default_run();
|
||||
|
||||
|
||||
async_(c2(4));
|
||||
|
||||
async_([]() -> awaitable<void> {
|
||||
std::cout << "Hello" << std::endl;
|
||||
co_await c2(4);
|
||||
co_return;
|
||||
}());
|
||||
|
||||
|
||||
int r = await_(
|
||||
async_(
|
||||
c2(10)
|
||||
));
|
||||
|
||||
|
||||
auto a = await_ ( c2(3));
|
||||
cout << a << endl;
|
||||
|
||||
await_ ([]() -> awaitable<void> {
|
||||
cout << "Hello" << endl;
|
||||
co_return;
|
||||
}());
|
||||
|
||||
|
||||
asynco_default_join();
|
||||
return 0;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
#include "../lib/asynco_default.hpp"
|
||||
using namespace marcelb::asynco;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
asynco_default_run();
|
||||
|
||||
// code
|
||||
|
||||
asynco_default_join();
|
||||
return 0;
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#include "../lib/asynco.hpp"
|
||||
using namespace marcelb::asynco;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
|
||||
Asynco asynco;
|
||||
asynco.run(2);
|
||||
|
||||
// code
|
||||
|
||||
asynco.join();
|
||||
return 0;
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
#include "../lib/asynco.hpp"
|
||||
using namespace marcelb::asynco;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
Asynco asynco;
|
||||
asynco.run(2);
|
||||
|
||||
Timer inter1 = asynco.periodic ([]() {
|
||||
cout << "Interval 1" << endl;
|
||||
}, 1000);
|
||||
|
||||
// stop periodic
|
||||
inter1.stop();
|
||||
|
||||
// how many times it has expired
|
||||
int ti = inter1.ticks();
|
||||
|
||||
// is it stopped
|
||||
bool stoped_i = inter1.stoped();
|
||||
|
||||
// start delayed
|
||||
Timer time1 = asynco.delayed ( [] () {
|
||||
cout << "Timeout 1 " << endl;
|
||||
}, 10000);
|
||||
|
||||
// stop delayed
|
||||
time1.stop();
|
||||
|
||||
// is it expired
|
||||
int tt = time1.expired();
|
||||
|
||||
// is it stopped
|
||||
bool stoped_t = time1.stoped();
|
||||
|
||||
|
||||
asynco.join();
|
||||
return 0;
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
#include "../lib/asynco_default.hpp"
|
||||
using namespace marcelb::asynco;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
|
||||
int main() {
|
||||
asynco_default_run();
|
||||
|
||||
Timer inter1 = periodic ([]() {
|
||||
cout << "Interval 1" << endl;
|
||||
}, 1000);
|
||||
|
||||
// stop periodic
|
||||
// inter1.stop();
|
||||
|
||||
// how many times it has expired
|
||||
int ti = inter1.ticks();
|
||||
|
||||
// is it stopped
|
||||
bool stoped_i = inter1.stoped();
|
||||
|
||||
// start delayed
|
||||
Timer time1 = delayed ( [] () {
|
||||
cout << "Timeout 1 " << endl;
|
||||
}, 10000);
|
||||
|
||||
// stop delayed
|
||||
// time1.stop();
|
||||
|
||||
// is it expired
|
||||
int tt = time1.expired();
|
||||
|
||||
// is it stopped
|
||||
bool stoped_t = time1.stoped();
|
||||
|
||||
asynco_default_join();
|
||||
return 0;
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
#include "../lib/asynco.hpp"
|
||||
using namespace marcelb::asynco;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
Asynco asynco;
|
||||
|
||||
class ClassWithTriggers {
|
||||
Trigger<int> emitter1;
|
||||
Trigger<string> emitter2;
|
||||
|
||||
public:
|
||||
ClassWithTriggers(): emitter1(asynco), emitter2(asynco) {}
|
||||
|
||||
template<typename... T>
|
||||
void on(const string& key, function<void(T...)> callback) {
|
||||
if constexpr (sizeof...(T) == 1 && is_same_v<tuple_element_t<0, tuple<T...>>, int>) {
|
||||
emitter1.on(key, callback);
|
||||
}
|
||||
else if constexpr (sizeof...(T) == 1 && is_same_v<tuple_element_t<0, tuple<T...>>, string>) {
|
||||
emitter2.on(key, callback);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void tick(const string& key, Args&&... args) {
|
||||
if constexpr (sizeof...(Args) == 1 && is_same_v<tuple_element_t<0, tuple<Args...>>, int>) {
|
||||
emitter1.tick(key, forward<Args>(args)...);
|
||||
}
|
||||
else if constexpr (sizeof...(Args) == 1 && is_same_v<tuple_element_t<0, tuple<Args...>>, string>) {
|
||||
emitter2.tick(key, forward<Args>(args)...);
|
||||
}
|
||||
else {
|
||||
static_assert(sizeof...(Args) == 0, "Unsupported number or types of arguments");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
asynco.run(2);
|
||||
|
||||
/**
|
||||
* initialization of typed events
|
||||
*/
|
||||
|
||||
Trigger<int, int> ev2int = asynco.trigger<int, int>();
|
||||
Trigger<int, string> evintString = asynco.trigger<int, string>();
|
||||
Trigger<> evoid = asynco.trigger<>();
|
||||
|
||||
|
||||
ev2int.on("sum", [](int a, int b) {
|
||||
cout << "Sum " << a+b << endl;
|
||||
});
|
||||
|
||||
evintString.on("substract", [](int a, string b) {
|
||||
cout << "Substract " << a-stoi(b) << endl;
|
||||
});
|
||||
|
||||
evoid.on("void", []() {
|
||||
cout << "Void emited" << endl;
|
||||
});
|
||||
|
||||
// multiple listeners
|
||||
|
||||
string emited2 = "2";
|
||||
|
||||
evoid.on("void", [&]() {
|
||||
cout << "Void emited " << emited2 << endl;
|
||||
});
|
||||
|
||||
sleep(1);
|
||||
|
||||
/**
|
||||
* Emit
|
||||
*/
|
||||
|
||||
ev2int.tick("sum", 5, 8);
|
||||
|
||||
sleep(1);
|
||||
evintString.tick("substract", 3, to_string(2));
|
||||
|
||||
sleep(1);
|
||||
evoid.tick("void");
|
||||
|
||||
// Turn off the event listener
|
||||
|
||||
evoid.off("void");
|
||||
evoid.tick("void"); // nothing is happening
|
||||
|
||||
|
||||
class myOwnClass : public Trigger<int> {
|
||||
public:
|
||||
myOwnClass() : Trigger(asynco) {};
|
||||
};
|
||||
|
||||
myOwnClass myclass;
|
||||
|
||||
Timer t = asynco.delayed( [&] {
|
||||
myclass.tick("constructed", 1);
|
||||
}, 200);
|
||||
|
||||
myclass.on("constructed", [] (int i) {
|
||||
cout << "Constructed " << i << endl;
|
||||
});
|
||||
|
||||
|
||||
ClassWithTriggers mt;
|
||||
|
||||
mt.on<int>("int", function<void(int)>([&](int i) {
|
||||
cout << "Emit int " << i << endl;
|
||||
}));
|
||||
|
||||
mt.on<string>("string", function<void(string)>([&](string s) {
|
||||
cout << "Emit string " << s << endl;
|
||||
}));
|
||||
|
||||
mt.tick("int", 5);
|
||||
mt.tick("string", string("Hello world"));
|
||||
|
||||
|
||||
asynco.join();
|
||||
return 0;
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
#include "../lib/asynco_default.hpp"
|
||||
using namespace marcelb::asynco;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
class ClassWithTriggers {
|
||||
Trigger<int> emitter1;
|
||||
Trigger<string> emitter2;
|
||||
|
||||
public:
|
||||
ClassWithTriggers(): emitter1(asynco_default_runtime()), emitter2(asynco_default_runtime()) {}
|
||||
|
||||
template<typename... T>
|
||||
void on(const string& key, function<void(T...)> callback) {
|
||||
if constexpr (sizeof...(T) == 1 && is_same_v<tuple_element_t<0, tuple<T...>>, int>) {
|
||||
emitter1.on(key, callback);
|
||||
}
|
||||
else if constexpr (sizeof...(T) == 1 && is_same_v<tuple_element_t<0, tuple<T...>>, string>) {
|
||||
emitter2.on(key, callback);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void tick(const string& key, Args&&... args) {
|
||||
if constexpr (sizeof...(Args) == 1 && is_same_v<tuple_element_t<0, tuple<Args...>>, int>) {
|
||||
emitter1.tick(key, forward<Args>(args)...);
|
||||
}
|
||||
else if constexpr (sizeof...(Args) == 1 && is_same_v<tuple_element_t<0, tuple<Args...>>, string>) {
|
||||
emitter2.tick(key, forward<Args>(args)...);
|
||||
}
|
||||
else {
|
||||
static_assert(sizeof...(Args) == 0, "Unsupported number or types of arguments");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
asynco_default_run();
|
||||
|
||||
/**
|
||||
* initialization of typed events
|
||||
*/
|
||||
|
||||
Trigger<int, int> ev2int = trigger<int, int>();
|
||||
Trigger<int, string> evintString = trigger<int, string>();
|
||||
Trigger<> evoid = trigger<>();
|
||||
|
||||
ev2int.on("sum", [](int a, int b) {
|
||||
cout << "Sum " << a+b << endl;
|
||||
});
|
||||
|
||||
evintString.on("substract", [](int a, string b) {
|
||||
cout << "Substract " << a-stoi(b) << endl;
|
||||
});
|
||||
|
||||
evoid.on("void", []() {
|
||||
cout << "Void emited" << endl;
|
||||
});
|
||||
|
||||
// multiple listeners
|
||||
|
||||
string emited2 = "2";
|
||||
|
||||
evoid.on("void", [&]() {
|
||||
cout << "Void emited " << emited2 << endl;
|
||||
});
|
||||
|
||||
sleep(1);
|
||||
|
||||
/**
|
||||
* Emit
|
||||
*/
|
||||
|
||||
ev2int.tick("sum", 5, 8);
|
||||
|
||||
sleep(1);
|
||||
evintString.tick("substract", 3, to_string(2));
|
||||
|
||||
sleep(1);
|
||||
evoid.tick("void");
|
||||
|
||||
// Turn off the event listener
|
||||
|
||||
evoid.off("void");
|
||||
evoid.tick("void"); // nothing is happening
|
||||
|
||||
class myOwnClass : public Trigger<int> {
|
||||
public:
|
||||
myOwnClass() : Trigger(asynco_default_runtime()) {};
|
||||
};
|
||||
|
||||
myOwnClass myclass;
|
||||
|
||||
Timer t = delayed( [&] {
|
||||
myclass.tick("constructed", 1);
|
||||
}, 200);
|
||||
|
||||
myclass.on("constructed", [] (int i) {
|
||||
cout << "Constructed " << i << endl;
|
||||
});
|
||||
|
||||
ClassWithTriggers mt;
|
||||
|
||||
mt.on<int>("int", function<void(int)>([&](int i) {
|
||||
cout << "Emit int " << i << endl;
|
||||
}));
|
||||
|
||||
mt.on<string>("string", function<void(string)>([&](string s) {
|
||||
cout << "Emit string " << s << endl;
|
||||
}));
|
||||
|
||||
mt.tick("int", 5);
|
||||
mt.tick("string", string("Hello world"));
|
||||
|
||||
asynco_default_join();
|
||||
return 0;
|
||||
}
|
309
test/test.cpp
Normal file
309
test/test.cpp
Normal file
@ -0,0 +1,309 @@
|
||||
// #define NUM_OF_RUNNERS 2
|
||||
|
||||
#include "../lib/asynco.hpp"
|
||||
#include "../lib/event.hpp"
|
||||
#include "../lib/rotor.hpp"
|
||||
#include "../lib/filesystem.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace marcelb::asynco;
|
||||
using namespace events;
|
||||
using namespace asynco;
|
||||
using namespace this_thread;
|
||||
|
||||
void sleep_to (int _time) {
|
||||
promise<void> _promise;
|
||||
timeout t( [&]() {
|
||||
_promise.set_value();
|
||||
}, _time);
|
||||
|
||||
return _promise.get_future().get();
|
||||
}
|
||||
|
||||
void promise_reject (int _time) {
|
||||
promise<void> _promise;
|
||||
timeout t( [&]() {
|
||||
try {
|
||||
// simulate except
|
||||
throw runtime_error("Error simulation");
|
||||
_promise.set_value();
|
||||
} catch (...) {
|
||||
_promise.set_exception(current_exception());
|
||||
}
|
||||
}, _time);
|
||||
|
||||
return _promise.get_future().get();
|
||||
}
|
||||
|
||||
void notLambdaFunction() {
|
||||
cout << "Call to not lambda function" << endl;
|
||||
}
|
||||
|
||||
class clm {
|
||||
public:
|
||||
void classMethode() {
|
||||
cout << "Call class method" << endl;
|
||||
}
|
||||
};
|
||||
|
||||
// ------------------ EXTEND OWN CLASS WITH EVENTS -------------------
|
||||
|
||||
class myOwnClass : public event<int> {
|
||||
public:
|
||||
myOwnClass() : event() {};
|
||||
};
|
||||
|
||||
|
||||
int main () {
|
||||
|
||||
auto start = rtime_ms();
|
||||
|
||||
// --------------- TIME ASYNCHRONOUS FUNCTIONS --------------
|
||||
|
||||
/**
|
||||
* Init interval and timeout; clear interval and timeout
|
||||
*/
|
||||
|
||||
vector<interval> intervals;
|
||||
|
||||
for(int i=0; i<10; i++) {
|
||||
intervals.push_back(interval( [i, &start]() {
|
||||
cout << "interval " << i << " end: " << rtime_ms() - start << endl;
|
||||
}, (i%5 +1)*1000));
|
||||
}
|
||||
|
||||
// interval( [&] () {
|
||||
// cout << "interval 1: " << rtime_ms() - start << endl;
|
||||
// }, 50);
|
||||
|
||||
// interval( [&] () {
|
||||
// cout << "interval 1: " << rtime_ms() - start << endl;
|
||||
// }, 100);
|
||||
|
||||
// interval( [&] () {
|
||||
// cout << "interval 2: " << rtime_ms() - start << endl;
|
||||
// }, 200);
|
||||
|
||||
// interval( [&] () {
|
||||
// cout << "interval 3: " << rtime_ms() - start << endl;
|
||||
// }, 300);
|
||||
|
||||
|
||||
// interval( [&] () {
|
||||
// cout << "interval 4: " << rtime_ms() - start << endl;
|
||||
// }, 400);
|
||||
|
||||
// interval inter1 ([&]() {
|
||||
// cout << "interval prvi " << rtime_ms() - start << endl;
|
||||
// }, 1000);
|
||||
|
||||
// interval inter2 ([&]() {
|
||||
// cout << "interval drugi " << rtime_ms() - start << endl;
|
||||
// }, 2000);
|
||||
|
||||
// interval inter3 ([&]() {
|
||||
// cout << "interval treći " << rtime_ms() - start << endl;
|
||||
// }, 3000);
|
||||
|
||||
// interval inter4 ([&]() {
|
||||
// cout << "interval cetvrti " << rtime_ms() - start << endl;
|
||||
// }, 1000);
|
||||
|
||||
// interval inter5 ([&]() {
|
||||
// cout << "interval peti " << rtime_ms() - start << endl;
|
||||
// }, 2000);
|
||||
|
||||
// interval inter6 ([&]() {
|
||||
// cout << "interval sesti " << rtime_ms() - start << endl;
|
||||
// }, 3000);
|
||||
|
||||
// timeout time1 ( [&] () {
|
||||
// cout << "Close interval 1 i 2 " << rtime_ms() - start << endl;
|
||||
// // inter1.clear();
|
||||
// // cout << "inter1.stop " << inter1.stop << endl;
|
||||
// // inter2.clear();
|
||||
// // cout << "inter2.stop " << inter2.stop << endl;
|
||||
// }, 5000);
|
||||
|
||||
|
||||
// timeout time2 ([&] () {
|
||||
// cout << "Close interval 3 " << rtime_ms() - start << endl;
|
||||
// // inter3.clear();
|
||||
// time1.clear();
|
||||
// }, 2000);
|
||||
|
||||
// // // ------------------------ MAKE FUNCTIONS ASYNCHRONOUS -------------------------
|
||||
|
||||
// /**
|
||||
// * Run an function asyncronic
|
||||
// */
|
||||
|
||||
// atask( []() {
|
||||
// sleep_for(2s); // only for simulate log duration function
|
||||
// cout << "atask 1" << endl;
|
||||
// return 5;
|
||||
// });
|
||||
|
||||
// /**
|
||||
// * Call not lambda function
|
||||
// */
|
||||
|
||||
// atask (notLambdaFunction);
|
||||
|
||||
|
||||
// wait (
|
||||
// atask (
|
||||
// notLambdaFunction
|
||||
// )
|
||||
// );
|
||||
|
||||
// /**
|
||||
// * Call class method
|
||||
// */
|
||||
|
||||
// clm classes;
|
||||
// atask( [&classes] () {
|
||||
// classes.classMethode();
|
||||
// });
|
||||
|
||||
// sleep(5);
|
||||
|
||||
// /**
|
||||
// * Wait after runned as async
|
||||
// */
|
||||
|
||||
// auto a = atask( []() {
|
||||
// sleep_for(2s); // only for simulate log duration function
|
||||
// cout << "atask 2" << endl;
|
||||
// return 5;
|
||||
// });
|
||||
|
||||
// cout << wait(a) << endl;
|
||||
// cout << "print after atask 2" << endl;
|
||||
|
||||
// /**
|
||||
// * Wait async function call and use i cout
|
||||
// */
|
||||
|
||||
// cout << wait(atask( [] () {
|
||||
// sleep_for(chrono::seconds(1)); // only for simulate log duration function
|
||||
// cout << "wait end" << endl;
|
||||
// return 4;
|
||||
// })) << endl;
|
||||
|
||||
// /**
|
||||
// * Sleep with timeout sleep implement
|
||||
// */
|
||||
|
||||
// sleep_to(3000);
|
||||
// cout << "sleep_to " << rtime_ms() - start << endl;
|
||||
|
||||
// /**
|
||||
// * Catch promise reject
|
||||
// */
|
||||
|
||||
// try {
|
||||
// promise_reject(3000);
|
||||
// } catch (runtime_error err) {
|
||||
// cout<< err.what() << endl;
|
||||
// }
|
||||
|
||||
// cout << "promise_reject " << rtime_ms() - start << endl;
|
||||
|
||||
|
||||
// /**
|
||||
// * Nested asynchronous invocation
|
||||
// */
|
||||
|
||||
|
||||
// atask( [] {
|
||||
// cout << "idemo ..." << endl;
|
||||
// atask( [] {
|
||||
// cout << "ugdnježdena async funkcija " << endl;
|
||||
// });
|
||||
// });
|
||||
|
||||
// // --------------- EVENTS -------------------
|
||||
|
||||
// /**
|
||||
// * initialization of typed events
|
||||
// */
|
||||
|
||||
// event<int, int> ev2int;
|
||||
// event<int, string> evintString;
|
||||
// event<> evoid;
|
||||
|
||||
// ev2int.on("sum", [](int a, int b) {
|
||||
// cout << "Sum " << a+b << endl;
|
||||
// });
|
||||
|
||||
// ev2int.on("sum", [](int a, int b) {
|
||||
// cout << "Sum done" << endl;
|
||||
// });
|
||||
|
||||
// evintString.on("substract", [](int a, string b) {
|
||||
// cout << "Substract " << a-stoi(b) << endl;
|
||||
// });
|
||||
|
||||
// evoid.on("void", []() {
|
||||
// cout << "Void emited" << endl;
|
||||
// });
|
||||
|
||||
// string emited2 = "2";
|
||||
|
||||
// evoid.on("void", [&]() {
|
||||
// cout << "Void emited " << emited2 << endl;
|
||||
// });
|
||||
|
||||
// evoid.emit("void");
|
||||
// sleep(1);
|
||||
|
||||
// /**
|
||||
// * Emit
|
||||
// */
|
||||
|
||||
// ev2int.emit("sum", 5, 8);
|
||||
|
||||
|
||||
// sleep(1);
|
||||
// evintString.emit("substract", 3, to_string(2));
|
||||
|
||||
// sleep(1);
|
||||
// evoid.off("void");
|
||||
// evoid.emit("void");
|
||||
|
||||
// /**
|
||||
// * Own class
|
||||
// */
|
||||
|
||||
// myOwnClass myclass;
|
||||
|
||||
// timeout t( [&] {
|
||||
// myclass.emit("constructed", 1);
|
||||
// }, 200);
|
||||
|
||||
// myclass.on("constructed", [] (int i) {
|
||||
// cout << "Constructed " << i << endl;
|
||||
// });
|
||||
|
||||
|
||||
|
||||
// auto status = fs::read("test1.txt");
|
||||
|
||||
|
||||
// try {
|
||||
// auto data = wait(status);
|
||||
// cout << data;
|
||||
// } catch (exception& err) {
|
||||
// cout << err.what() << endl;
|
||||
// }
|
||||
|
||||
cout << "Sleep" << endl;
|
||||
sleep(100000); // only for testing
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user