diff --git a/native/dist/ext_nan/include/NanEventCallback.h b/native/dist/ext_nan/include/NanEventCallback.h index 655ce22..24ee07b 100644 --- a/native/dist/ext_nan/include/NanEventCallback.h +++ b/native/dist/ext_nan/include/NanEventCallback.h @@ -208,4 +208,105 @@ namespace Nan { auto handle = std::make_shared(std::forward(lam)); return async_callback(lambda_info::wrap(handle)); } + + struct JavaScriptQueue { + public: + explicit JavaScriptQueue() { + auto event_loop = Nan::GetCurrentEventLoop(); + assert(event_loop); + + this->callback_data = new CallbackData{}; + uv_async_init(event_loop, &this->callback_data->handle, JavaScriptQueue::async_send_callback); + } + + ~JavaScriptQueue() { + { + std::lock_guard lock{this->callback_data->callback_mutex}; + this->callback_data->destroy = true; + } + uv_async_send(&this->callback_data->handle); + } + + JavaScriptQueue(const JavaScriptQueue&) = delete; + JavaScriptQueue(JavaScriptQueue&&) = delete; + + template + inline void enqueue(lambda&& callback) const { + auto callable = new Callable(std::forward(callback)); + + { + std::lock_guard lock{callback_data->callback_mutex}; + *this->callback_data->callback_tail = callable; + this->callback_data->callback_tail = &callable->next; + } + + uv_async_send(&this->callback_data->handle); + } + private: + struct AbstractCallable; + struct CallbackData { + uv_async_t handle; + bool destroy{false}; + + std::mutex callback_mutex{}; + AbstractCallable* callback_head{nullptr}; + AbstractCallable** callback_tail{&this->callback_head}; + + explicit CallbackData() { + memset(&this->handle, 0, sizeof(this->handle)); + this->handle.data = this; + } + }; + + struct AbstractCallable { + AbstractCallable() = default; + virtual ~AbstractCallable() = default; + virtual void call() = 0; + + AbstractCallable* next{nullptr}; + }; + + template + struct Callable : public AbstractCallable { + explicit Callable(lambda&& callback) : callback{std::forward(callback)} {} + ~Callable() override = default; + + void call() override { + this->callback(); + } + + lambda callback; + }; + + static void async_send_callback(uv_async_t* handle) { + auto data = (CallbackData*) handle->data; + assert(data->handle.data == data); + + std::unique_lock lock{data->callback_mutex}; + auto destroy = data->destroy; + auto callback_head = std::exchange(data->callback_head, nullptr); + data->callback_tail = &data->callback_head; + lock.unlock(); + + Nan::HandleScope scope{}; + while(callback_head) { + callback_head->call(); + + auto next = callback_head->next; + delete callback_head; + callback_head = next; + } + + if(destroy) { + /* uv_async_t inherits from uv_handle_t */ + uv_close((uv_handle_t*) handle, JavaScriptQueue::async_close_callback); + } + } + + static void async_close_callback(uv_handle_t* handle) { + delete (CallbackData*) handle->data; + } + + CallbackData* callback_data; + }; } \ No newline at end of file