#pragma once #include #include #include #include #include #include namespace Nan { namespace async_helper { template struct callback_wrap; template struct lambda_type; template struct lambda_type { using void_function = std::function; using result = callback_wrap; inline static void_function wrap(const std::shared_ptr& lam) { return [lam](Args&&... args) { (*lam)(std::forward(args)...); }; } }; template struct lambda_type { using void_function = std::function; using result = callback_wrap; inline static void_function wrap(const std::shared_ptr& lam) { return [lam](Args&&... args) { (*lam)(std::forward(args)...); }; } }; // helper class template class Params, typename... Args, std::size_t... I> inline R call_helper(std::function const& func, Params const& params, std::index_sequence) { return func(std::forward((Args&&) std::get(params))...); } template 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 callback; std::tuple arguments; }; template struct _or_ { constexpr static bool value = false; }; template struct _or_ { constexpr static bool value = T || _or_::value; }; template struct callback_scoped { callback_args* handle; std::function callback; std::function destroy; callback_scoped( callback_args* handle, const std::function &callback, std::function destroy ) : callback(callback), destroy(std::move(destroy)), handle(handle) {} ~callback_scoped() { destroy(); } }; template struct callback_wrap { std::shared_ptr> handle; 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)...); } void operator()(Args&&... args) { if(!this->handle) throw std::bad_function_call(); handle->callback(std::forward(args)...); } void operator()(Args&&... args) const { if(!this->handle) throw std::bad_function_call(); handle->callback(std::forward(args)...); } callback_wrap& operator=(const callback_wrap& other) { this->handle = other.handle; return *this; } operator bool() const { return this->handle != nullptr; } callback_wrap& 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& option_destroyed_execute(bool flag) { if(!this->handle) throw std::logic_error("missing handle"); this->handle->handle->option_destroy_run = flag; return *this; } }; }; template using callback_t = async_helper::callback_wrap; template ::value...>::value> inline async_helper::callback_wrap async_callback(std::function callback) { static_assert(!referenced_args, "Argument references aren't allowed"); using callback_t = std::function; using callback_args = async_helper::callback_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{}); 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>( args, [args](Args&&... invoker_args) { std::lock_guard lock(args->destroy_lock); args->flag_execute = true; args->arguments = std::tuple{std::forward(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 result = typename lambda_info::result> inline result async_callback(lambda&& lam) { auto handle = std::make_shared(std::forward(lam)); return async_callback(lambda_info::wrap(handle)); } }