parent
d3c7b3a91b
commit
8fdf56d1bb
@ -0,0 +1 @@ |
||||
test/test |
@ -0,0 +1,216 @@ |
||||
|
||||
# Asynco |
||||
|
||||
A C++ library for event-driven asynchronous multi-threaded programming. |
||||
|
||||
## Features |
||||
|
||||
- Object oriented |
||||
- Small and easy to integrate |
||||
- Header only |
||||
- Asynchronous launch functions |
||||
- Multithread parallel execution of tasks |
||||
- Timer functions: interval, timeout |
||||
- Events (on, emit) |
||||
- Event loop |
||||
|
||||
## Installation |
||||
|
||||
Just download the latest release and unzip it into your project. |
||||
|
||||
```c++ |
||||
#include "asynco/lib/asynco.hpp" // asynco(), wait() |
||||
#include "asynco/lib/event.hpp" // event |
||||
#include "asynco/lib/rotor.hpp" // interval, timeout |
||||
#include "asynco/lib/runner.hpp" // on_async |
||||
using namespace marcelb; |
||||
|
||||
#ifndef ON_RUNNER |
||||
#define ON_RUNNER |
||||
runner on_async; |
||||
#endif |
||||
``` |
||||
|
||||
## Usage |
||||
|
||||
Time asynchronous functions |
||||
|
||||
```c++ |
||||
// start interval |
||||
interval inter1 ([&]() { |
||||
cout << "Interval 1" << endl; |
||||
}, 1000); |
||||
|
||||
// stop interval |
||||
inter1.clear(); |
||||
|
||||
// start timeout |
||||
timeout time1 ( [&] () { |
||||
cout << "Timeout 1 " << endl; |
||||
}, 10000); |
||||
|
||||
// stop timeout |
||||
time1.clear(); |
||||
``` |
||||
Make functions asynchronous |
||||
|
||||
```c++ |
||||
/** |
||||
* Put task directly and get returned value - it is not recommended to use it |
||||
*/ |
||||
|
||||
auto res1 = on_async.put_task( [] () { |
||||
cout << "Jebiga " <<endl; |
||||
throw string ("jebiga!!"); |
||||
}); |
||||
|
||||
try { |
||||
res1.get(); |
||||
} catch (const string except) { |
||||
cout << except << endl; |
||||
} |
||||
|
||||
/** |
||||
* Run an function asyncronic |
||||
*/ |
||||
|
||||
asynco( []() { |
||||
sleep_for(2s); // only for simulate log duration function |
||||
cout << "asynco" << endl; |
||||
return 5; |
||||
}); |
||||
|
||||
|
||||
/** |
||||
* Wait after runned as async |
||||
*/ |
||||
|
||||
auto a = asynco( []() { |
||||
sleep_for(2s); // only for simulate log duration function |
||||
cout << "asynco" << endl; |
||||
return 5; |
||||
}); |
||||
|
||||
cout << wait(move(a)) << endl; |
||||
|
||||
/** |
||||
* Wait async function call and use i cout |
||||
*/ |
||||
|
||||
cout << wait(asynco( [] () { |
||||
sleep_for(chrono::seconds(1)); // only for simulate log 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; |
||||
} |
||||
``` |
||||
Events |
||||
|
||||
```c++ |
||||
/** |
||||
* 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; |
||||
}); |
||||
|
||||
evintString.on("substract", [](int a, string b) { |
||||
cout << "Substract " << a-stoi(b) << endl; |
||||
}); |
||||
|
||||
evoid.on("void", []() { |
||||
cout << "Void emited" << endl; |
||||
}); |
||||
|
||||
sleep(1); |
||||
|
||||
/** |
||||
* Emit |
||||
*/ |
||||
|
||||
ev2int.emit("sum", 5, 8); |
||||
|
||||
sleep(1); |
||||
evintString.emit("substract", 3, to_string(2)); |
||||
|
||||
sleep(1); |
||||
evoid.emit("void"); |
||||
``` |
||||
Extend own class whit events |
||||
|
||||
```c++ |
||||
class myOwnClass : public event<int> { |
||||
public: |
||||
myOwnClass() : event() {}; |
||||
}; |
||||
|
||||
myOwnClass myclass; |
||||
|
||||
timeout t( [&] { |
||||
myclass.emit("constructed", 1); |
||||
}, 200); |
||||
|
||||
myclass.on("constructed", [] (int i) { |
||||
cout << "Constructed " << i << endl; |
||||
}); |
||||
|
||||
``` |
||||
## License |
||||
|
||||
[APACHE 2.0](http://www.apache.org/licenses/LICENSE-2.0/) |
||||
|
||||
|
||||
## Support & Feedback |
||||
|
||||
For support and any feedback, contact the address: marcelb96@yahoo.com. |
||||
|
||||
## Contributing |
||||
|
||||
Contributions are always welcome! |
||||
|
||||
Feel free to fork and start working with or without a later pull request. Or contact for suggest and request an option. |
||||
|
@ -1,164 +0,0 @@ |
||||
#ifndef _ASYNCO_ |
||||
#define _ASYNCO_ |
||||
|
||||
#include <vector> |
||||
#include <mutex> |
||||
#include <future> |
||||
#include <thread> |
||||
|
||||
using namespace std; |
||||
|
||||
namespace marcelb { |
||||
|
||||
class interval; |
||||
class timeout; |
||||
|
||||
class loop_core { |
||||
vector<interval> intervals; |
||||
vector<timeout> timeouts; |
||||
time_t sampling; |
||||
mutex i_m, t_m; |
||||
future<void> bot; |
||||
|
||||
loop_core() { |
||||
bot = async(launch::async, [this] () { |
||||
loop(); |
||||
}); |
||||
// on_async.put_task( [this] () {
|
||||
// loop();
|
||||
// });
|
||||
} |
||||
|
||||
void run(interval& _interval) { |
||||
lock_guard<mutex> lock(i_m); |
||||
intervals.push_back(_interval); |
||||
update_sampling(); |
||||
} |
||||
|
||||
void run(timeout& _timeout) { |
||||
lock_guard<mutex> lock(t_m); |
||||
timeouts.push_back(_timeout); |
||||
update_sampling(); |
||||
} |
||||
|
||||
void loop() { |
||||
while (true) { |
||||
for (auto& _interval : intervals) { |
||||
int64_t now = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count(); |
||||
if (now - _interval.execute >= _interval._duration) { |
||||
_interval.callback(); |
||||
_interval.execute = now; |
||||
} |
||||
} |
||||
|
||||
for (int i=0; i<timeouts.size(); i++) { |
||||
int64_t now = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count(); |
||||
if (now - timeouts[i]._construct >= timeouts[i]._duration) { |
||||
auto& _timeout = timeouts[i]; |
||||
{ |
||||
lock_guard<mutex> lock(t_m); |
||||
timeouts.erase(timeouts.begin() + i); |
||||
} |
||||
_timeout.callback(); |
||||
} |
||||
} |
||||
this_thread::sleep_for(chrono::milliseconds(sampling)); |
||||
} |
||||
} |
||||
|
||||
void update_sampling() { |
||||
sampling = 0; |
||||
for (auto& _interval : intervals) { |
||||
sampling += _interval._duration; |
||||
} |
||||
for (auto& _timeout : timeouts) { |
||||
sampling += _timeout._duration; |
||||
} |
||||
sampling /= (intervals.size() + timeouts.size())*2; |
||||
} |
||||
|
||||
|
||||
}; |
||||
|
||||
loop_core co_loop; |
||||
|
||||
|
||||
|
||||
|
||||
class interval { |
||||
public: |
||||
bool run = true; |
||||
function<void()> callback; |
||||
const time_t _duration; |
||||
time_t execute = 0; |
||||
|
||||
// public:
|
||||
interval(function<void()> func, const time_t duration): callback(func), _duration(duration) { |
||||
#ifndef ON_ASYNC |
||||
throw string("Not on_async defined!"); |
||||
#endif |
||||
|
||||
auto task = [&] () { |
||||
while (run) { |
||||
// this_thread::sleep_for(chrono::milliseconds(_duration));
|
||||
// if (run) {
|
||||
callback(); |
||||
// }
|
||||
} |
||||
}; |
||||
|
||||
// on_async.put_task(task);
|
||||
co_loop.run(this*); |
||||
} |
||||
|
||||
void clear() { |
||||
run = false; |
||||
} |
||||
|
||||
~interval() { |
||||
clear(); |
||||
} |
||||
}; |
||||
|
||||
class timeout { |
||||
public: |
||||
bool run = true; |
||||
function<void()> callback; |
||||
const time_t _duration; |
||||
int64_t _construct = |
||||
chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()) |
||||
.count(); |
||||
|
||||
// public:
|
||||
timeout(function<void()> f, const time_t duration): callback(f), _duration(duration) { |
||||
#ifndef ON_ASYNC |
||||
throw string("Not on_async defined!"); |
||||
#endif |
||||
|
||||
auto task = [&] () { |
||||
// int64_t _start =
|
||||
// chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch())
|
||||
// .count();
|
||||
// this_thread::sleep_for(chrono::milliseconds(_duration - (_start - _construct)));
|
||||
if (run) { |
||||
callback(); |
||||
} |
||||
}; |
||||
|
||||
// on_async.put_task(task);
|
||||
co_loop.run(this*); |
||||
} |
||||
|
||||
void clear() { |
||||
run = false; |
||||
} |
||||
|
||||
~timeout() { |
||||
clear(); |
||||
} |
||||
}; |
||||
|
||||
|
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,212 @@ |
||||
#ifndef _ROTOR_ |
||||
#define _ROTOT_ |
||||
|
||||
#include "runner.hpp" |
||||
#include "chrono" |
||||
|
||||
#include "iostream" |
||||
|
||||
using namespace std; |
||||
using namespace marcelb; |
||||
|
||||
#ifndef ON_RUNNER |
||||
#define ON_RUNNER |
||||
runner on_async; |
||||
#endif |
||||
|
||||
namespace marcelb { |
||||
|
||||
/**
|
||||
* 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(); |
||||
} |
||||
|
||||
/**
|
||||
* Structure for time events |
||||
*/ |
||||
|
||||
struct time_event { |
||||
function<void()> callback; |
||||
int64_t init; |
||||
int64_t time; |
||||
bool repeat; |
||||
bool stop; |
||||
}; |
||||
|
||||
/**
|
||||
* Event loop for time events |
||||
*/ |
||||
|
||||
class rotor { |
||||
vector<struct time_event *> tevents; |
||||
mutex te_m; |
||||
bool rotating = true; |
||||
int64_t sampling; |
||||
|
||||
/**
|
||||
* 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) { |
||||
for (int i=0; i<tevents.size(); i++) { |
||||
|
||||
if (tevents[i]->stop) { |
||||
remove(i); |
||||
i--; |
||||
} |
||||
|
||||
else if (expired(tevents[i])) { |
||||
on_async.put_task(tevents[i]->callback); |
||||
if (tevents[i]->repeat) { |
||||
tevents[i]->init = rtime_ms(); |
||||
} |
||||
else { |
||||
remove(i); |
||||
i--; |
||||
} |
||||
} |
||||
} |
||||
this_thread::sleep_for(chrono::milliseconds(sampling)); |
||||
}
|
||||
} |
||||
|
||||
/**
|
||||
* The method checks whether the time event has expired |
||||
*/ |
||||
bool expired(struct time_event *tevent) { |
||||
return rtime_ms() - tevent->init >= tevent->time; |
||||
} |
||||
|
||||
/**
|
||||
* The method deletes a non-repeating or stopped event from the stack |
||||
*/ |
||||
void remove(const int& position) { |
||||
lock_guard<mutex> lock(te_m); |
||||
tevents.erase(tevents.begin()+position); |
||||
} |
||||
|
||||
/**
|
||||
* Updates the idle time of the loop, according to twice the frequency of available events |
||||
*/ |
||||
void update_sampling() { |
||||
sampling = tevents[0]->time; |
||||
for (int i=0; i<tevents.size(); i++) { |
||||
if (sampling > tevents[i]->time) { |
||||
sampling = tevents[i]->time; |
||||
} |
||||
} |
||||
sampling /= tevents.size()*2; |
||||
} |
||||
|
||||
public: |
||||
|
||||
/**
|
||||
* Constructor for the rotor, starts the given loop by occupying one runner |
||||
*/ |
||||
rotor() { |
||||
on_async.put_task( [&] () { |
||||
loop(); |
||||
}); |
||||
}; |
||||
|
||||
/**
|
||||
* Adds a time event to the stack |
||||
*/ |
||||
void insert(struct time_event *tevent) { |
||||
lock_guard<mutex> lock(te_m); |
||||
tevents.push_back(tevent); |
||||
update_sampling(); |
||||
}; |
||||
|
||||
/**
|
||||
* Returns the number of active events |
||||
*/ |
||||
int active() { |
||||
return tevents.size(); |
||||
} |
||||
|
||||
/**
|
||||
* Stops all active events and stops the rotor |
||||
*/ |
||||
~rotor() { |
||||
for (int i=0; i<tevents.size(); i++) { |
||||
tevents[i]->stop = true; |
||||
} |
||||
rotating = false; |
||||
} |
||||
|
||||
}; |
||||
|
||||
/**
|
||||
* It is intended that there is only one global declaration |
||||
*/ |
||||
rotor _rotor; |
||||
|
||||
/**
|
||||
* A class for all timer functions |
||||
*/ |
||||
class timer_core { |
||||
public: |
||||
struct time_event t_event; |
||||
|
||||
/**
|
||||
* Timer constructor, receives a callback function and time |
||||
*/ |
||||
timer_core( function<void()> _callback, int64_t _time): |
||||
t_event({ _callback, rtime_ms(), _time, false, false }) { |
||||
|
||||
} |
||||
/**
|
||||
* Stop timer |
||||
*/ |
||||
void clear() { |
||||
t_event.stop = true; |
||||
} |
||||
|
||||
/**
|
||||
* Destruktor of timer, call stop |
||||
*/ |
||||
~timer_core() { |
||||
clear(); |
||||
} |
||||
}; |
||||
|
||||
/**
|
||||
* Class interval for periodic execution of the callback in time in ms |
||||
*/ |
||||
class interval : public timer_core { |
||||
public: |
||||
|
||||
/**
|
||||
* The constructor receives a callback function and an interval time |
||||
*/ |
||||
interval( function<void()> _callback, int64_t _time): timer_core(_callback, _time) { |
||||
t_event.repeat = true; |
||||
_rotor.insert(&t_event); |
||||
} |
||||
}; |
||||
|
||||
/**
|
||||
* Class interval for delayed callback execution in ms |
||||
*/ |
||||
class timeout : public timer_core { |
||||
public: |
||||
|
||||
/**
|
||||
* The constructor receives a callback function and a delay time |
||||
*/ |
||||
timeout( function<void()> _callback, int64_t delay): timer_core(_callback, delay) { |
||||
t_event.repeat = false; |
||||
_rotor.insert(&t_event); |
||||
} |
||||
}; |
||||
|
||||
} |
||||
|
||||
#endif |
Loading…
Reference in new issue