216 lines
6.3 KiB
C
216 lines
6.3 KiB
C
|
#pragma once
|
||
|
|
||
|
#include <functional>
|
||
|
#include <tuple>
|
||
|
#include <memory>
|
||
|
#include <mutex>
|
||
|
#include <uv.h>
|
||
|
#include <nan.h>
|
||
|
|
||
|
namespace Nan {
|
||
|
namespace async_helper {
|
||
|
template <typename...>
|
||
|
struct callback_wrap;
|
||
|
|
||
|
template <typename...>
|
||
|
struct lambda_type;
|
||
|
|
||
|
template<typename lambda, class ret, class klass, class... Args>
|
||
|
struct lambda_type<lambda, ret(klass::*)(Args...) const > {
|
||
|
|
||
|
using void_function = std::function<void(Args...)>;
|
||
|
using result = callback_wrap<Args...>;
|
||
|
|
||
|
inline static void_function wrap(const std::shared_ptr<lambda>& lam) {
|
||
|
return [lam](Args&&... args) { (*lam)(std::forward<Args>(args)...); };
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template<typename lambda, class ret, class klass, class... Args>
|
||
|
struct lambda_type<lambda, ret(klass::*)(Args...) > {
|
||
|
using void_function = std::function<void(Args...)>;
|
||
|
using result = callback_wrap<Args...>;
|
||
|
|
||
|
inline static void_function wrap(const std::shared_ptr<lambda>& lam) {
|
||
|
return [lam](Args&&... args) { (*lam)(std::forward<Args>(args)...); };
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// helper class
|
||
|
template<typename R, template<typename...> class Params, typename... Args, std::size_t... I>
|
||
|
inline R call_helper(std::function<R(Args...)> const& func, Params<Args...> const& params, std::index_sequence<I...>) {
|
||
|
return func(std::forward<Args>((Args&&) std::get<I>(params))...);
|
||
|
}
|
||
|
|
||
|
template <typename... Args>
|
||
|
struct callback_args {
|
||
|
uv_async_t handle{};
|
||
|
bool flag_execute = false;
|
||
|
bool destroy_only = false;
|
||
|
bool option_destroy_run = false; /* execute the callback, even if the haven had been destoryed */
|
||
|
std::mutex destroy_lock;
|
||
|
|
||
|
std::function<void(Args...)> callback;
|
||
|
std::tuple<Args...> arguments;
|
||
|
};
|
||
|
|
||
|
template <bool...>
|
||
|
struct _or_ {
|
||
|
constexpr static bool value = false;
|
||
|
};
|
||
|
|
||
|
template <bool T, bool... Args>
|
||
|
struct _or_<T, Args...> {
|
||
|
constexpr static bool value = T || _or_<Args...>::value;
|
||
|
};
|
||
|
|
||
|
|
||
|
template <typename... Args>
|
||
|
struct callback_scoped {
|
||
|
callback_args<Args...>* handle;
|
||
|
std::function<void(Args&&...)> callback;
|
||
|
std::function<void()> destroy;
|
||
|
|
||
|
callback_scoped(
|
||
|
callback_args<Args...>* handle,
|
||
|
const std::function<void(Args &&...)> &callback,
|
||
|
std::function<void()> destroy
|
||
|
) : callback(callback), destroy(std::move(destroy)), handle(handle) {}
|
||
|
|
||
|
~callback_scoped() { destroy(); }
|
||
|
};
|
||
|
|
||
|
template <typename... Args>
|
||
|
struct callback_wrap {
|
||
|
std::shared_ptr<callback_scoped<Args...>> handle;
|
||
|
|
||
|
void call_cpy(Args... args, bool no_throw = false) {
|
||
|
if(!this->handle) {
|
||
|
if(no_throw)
|
||
|
return;
|
||
|
throw std::bad_function_call();
|
||
|
}
|
||
|
handle->callback(std::forward<Args>(args)...);
|
||
|
}
|
||
|
|
||
|
void call(Args&&... args, bool no_throw = false) {
|
||
|
if(!this->handle) {
|
||
|
if(no_throw)
|
||
|
return;
|
||
|
throw std::bad_function_call();
|
||
|
}
|
||
|
handle->callback(std::forward<Args>(args)...);
|
||
|
}
|
||
|
|
||
|
void operator()(Args&&... args) {
|
||
|
if(!this->handle)
|
||
|
throw std::bad_function_call();
|
||
|
handle->callback(std::forward<Args>(args)...);
|
||
|
}
|
||
|
void operator()(Args&&... args) const {
|
||
|
if(!this->handle)
|
||
|
throw std::bad_function_call();
|
||
|
handle->callback(std::forward<Args>(args)...);
|
||
|
}
|
||
|
|
||
|
callback_wrap<Args...>& operator=(const callback_wrap<Args...>& other) {
|
||
|
this->handle = other.handle;
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
operator bool() const {
|
||
|
return this->handle != nullptr;
|
||
|
}
|
||
|
|
||
|
callback_wrap<Args...>& operator=(nullptr_t) {
|
||
|
this->handle = nullptr;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @returns true if the callback will be called even
|
||
|
* if the handle has been destroyed
|
||
|
*/
|
||
|
bool option_destroyed_execute() const {
|
||
|
if(!this->handle)
|
||
|
std::__throw_logic_error("missing handle");
|
||
|
return this->handle->handle->option_destroy_run;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param flag
|
||
|
* If true then the callback will be called event
|
||
|
* if the handle has already been destroyed
|
||
|
* @return this
|
||
|
*/
|
||
|
callback_wrap<Args...>& option_destroyed_execute(bool flag) {
|
||
|
if(!this->handle)
|
||
|
throw std::logic_error("missing handle");
|
||
|
this->handle->handle->option_destroy_run = flag;
|
||
|
return *this;
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
template <typename... Args>
|
||
|
using callback_t = async_helper::callback_wrap<Args...>;
|
||
|
|
||
|
template <typename... Args, bool referenced_args = async_helper::_or_<std::is_reference<Args>::value...>::value>
|
||
|
inline async_helper::callback_wrap<Args...> async_callback(std::function<void(Args...)> callback) {
|
||
|
static_assert(!referenced_args, "Argument references aren't allowed");
|
||
|
|
||
|
using callback_t = std::function<void(Args...)>;
|
||
|
using callback_args = async_helper::callback_args<Args...>;
|
||
|
|
||
|
auto args = new callback_args{};
|
||
|
args->callback = std::move(callback);
|
||
|
args->destroy_only = false;
|
||
|
memset(&args->handle, 0, sizeof(args->handle));
|
||
|
args->handle.data = args;
|
||
|
|
||
|
uv_async_init(Nan::GetCurrentEventLoop(), &args->handle, [](uv_async_t* async) {
|
||
|
auto _args = (callback_args*) async->data;
|
||
|
std::unique_lock destroy_lock(_args->destroy_lock); //This may changed after calling the function (deleted while invoking is an example)
|
||
|
if(!_args->destroy_only || (_args->option_destroy_run && _args->flag_execute)) {
|
||
|
_args->flag_execute = false;
|
||
|
auto arguments = std::move(_args->arguments); //Move the tuple now before it get overridden :)
|
||
|
destroy_lock.unlock();
|
||
|
async_helper::call_helper(_args->callback, arguments, std::index_sequence_for<Args...>{});
|
||
|
destroy_lock.lock();
|
||
|
}
|
||
|
|
||
|
if(_args->destroy_only) {
|
||
|
uv_close((uv_handle_t*) (void*) async, [](uv_handle_t* handle) {
|
||
|
delete (callback_args*) handle->data;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
});
|
||
|
|
||
|
return {
|
||
|
std::make_shared<async_helper::callback_scoped<Args...>>(
|
||
|
args,
|
||
|
[args](Args&&... invoker_args) {
|
||
|
std::lock_guard lock(args->destroy_lock);
|
||
|
args->flag_execute = true;
|
||
|
args->arguments = std::tuple<Args...>{std::forward<Args>(invoker_args)...};
|
||
|
uv_async_send(&args->handle);
|
||
|
},
|
||
|
[args]{
|
||
|
std::lock_guard lock(args->destroy_lock);
|
||
|
args->destroy_only = true;
|
||
|
uv_async_send(&args->handle);
|
||
|
}
|
||
|
)
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/* lambda edition */
|
||
|
template <typename lambda,
|
||
|
typename lambda_info = typename async_helper::lambda_type<lambda, decltype(&lambda::operator())>,
|
||
|
typename result = typename lambda_info::result>
|
||
|
inline result async_callback(lambda&& lam) {
|
||
|
auto handle = std::make_shared<lambda>(std::forward<lambda>(lam));
|
||
|
return async_callback(lambda_info::wrap(handle));
|
||
|
}
|
||
|
}
|