1 #ifndef CALLBACK_BRIDGE_H
2 #define CALLBACK_BRIDGE_H
11 template <typename T, typename L = void*>
12 class CallbackBridge {
14 CallbackBridge(v8::Local<v8::Function>, bool);
15 virtual ~CallbackBridge();
17 // Executes the callback
18 T operator()(std::vector<void*>);
21 // We will expose a bridge object to the JS callback that wraps this instance so we don't loose context.
22 // This is the V8 constructor for such objects.
23 static Nan::MaybeLocal<v8::Function> get_wrapper_constructor();
24 static void async_gone(uv_handle_t *handle);
25 static NAN_METHOD(New);
26 static NAN_METHOD(ReturnCallback);
27 static Nan::Persistent<v8::Function> wrapper_constructor;
28 Nan::Persistent<v8::Object> wrapper;
30 // The callback that will get called in the main thread after the worker thread used for the sass
31 // compilation step makes a call to uv_async_send()
32 static void dispatched_async_uv_callback(uv_async_t*);
34 // The V8 values sent to our ReturnCallback must be read on the main thread not the sass worker thread.
35 // This gives a chance to specialized subclasses to transform those values into whatever makes sense to
36 // sass before we resume the worker thread.
37 virtual T post_process_return_value(v8::Local<v8::Value>) const =0;
40 virtual std::vector<v8::Local<v8::Value>> pre_process_args(std::vector<L>) const =0;
42 Nan::Callback* callback;
46 uv_cond_t condition_variable;
53 template <typename T, typename L>
54 Nan::Persistent<v8::Function> CallbackBridge<T, L>::wrapper_constructor;
56 template <typename T, typename L>
57 CallbackBridge<T, L>::CallbackBridge(v8::Local<v8::Function> callback, bool is_sync) : callback(new Nan::Callback(callback)), is_sync(is_sync) {
59 * This is invoked from the main JavaScript thread.
60 * V8 context is available.
62 Nan::HandleScope scope;
63 uv_mutex_init(&this->cv_mutex);
64 uv_cond_init(&this->condition_variable);
66 this->async = new uv_async_t;
67 this->async->data = (void*) this;
68 uv_async_init(uv_default_loop(), this->async, (uv_async_cb) dispatched_async_uv_callback);
71 v8::Local<v8::Function> func = CallbackBridge<T, L>::get_wrapper_constructor().ToLocalChecked();
72 wrapper.Reset(Nan::NewInstance(func).ToLocalChecked());
73 Nan::SetInternalFieldPointer(Nan::New(wrapper), 0, this);
76 template <typename T, typename L>
77 CallbackBridge<T, L>::~CallbackBridge() {
78 delete this->callback;
79 this->wrapper.Reset();
80 uv_cond_destroy(&this->condition_variable);
81 uv_mutex_destroy(&this->cv_mutex);
84 uv_close((uv_handle_t*)this->async, &async_gone);
88 template <typename T, typename L>
89 T CallbackBridge<T, L>::operator()(std::vector<void*> argv) {
90 // argv.push_back(wrapper);
93 * This is invoked from the main JavaScript thread.
94 * V8 context is available.
96 * Establish Local<> scope for all functions
97 * from types invoked by pre_process_args() and
98 * post_process_args().
100 Nan::HandleScope scope;
101 Nan::TryCatch try_catch;
102 std::vector<v8::Local<v8::Value>> argv_v8 = pre_process_args(argv);
103 if (try_catch.HasCaught()) {
104 Nan::FatalException(try_catch);
107 argv_v8.push_back(Nan::New(wrapper));
109 return this->post_process_return_value(
110 this->callback->Call(argv_v8.size(), &argv_v8[0])
114 * This is invoked from the worker thread.
115 * No V8 context and functions available.
116 * Just wait for response from asynchronously
117 * scheduled JavaScript code
119 * XXX Issue #1048: We block here even if the
120 * event loop stops and the callback
121 * would never be executed.
122 * XXX Issue #857: By waiting here we occupy
123 * one of the threads taken from the
124 * uv threadpool. Might deadlock if
125 * async I/O executed from JavaScript callbacks.
129 uv_mutex_lock(&this->cv_mutex);
130 this->has_returned = false;
131 uv_async_send(this->async);
132 while (!this->has_returned) {
133 uv_cond_wait(&this->condition_variable, &this->cv_mutex);
135 uv_mutex_unlock(&this->cv_mutex);
136 return this->return_value;
140 template <typename T, typename L>
141 void CallbackBridge<T, L>::dispatched_async_uv_callback(uv_async_t *req) {
142 CallbackBridge* bridge = static_cast<CallbackBridge*>(req->data);
145 * Function scheduled via uv_async mechanism, therefore
146 * it is invoked from the main JavaScript thread.
147 * V8 context is available.
149 * Establish Local<> scope for all functions
150 * from types invoked by pre_process_args() and
151 * post_process_args().
153 Nan::HandleScope scope;
154 Nan::TryCatch try_catch;
156 std::vector<v8::Local<v8::Value>> argv_v8 = bridge->pre_process_args(bridge->argv);
157 if (try_catch.HasCaught()) {
158 Nan::FatalException(try_catch);
160 argv_v8.push_back(Nan::New(bridge->wrapper));
162 bridge->callback->Call(argv_v8.size(), &argv_v8[0]);
164 if (try_catch.HasCaught()) {
165 Nan::FatalException(try_catch);
169 template <typename T, typename L>
170 NAN_METHOD(CallbackBridge<T COMMA L>::ReturnCallback) {
173 * Callback function invoked by the user code.
174 * It is invoked from the main JavaScript thread.
175 * V8 context is available.
177 * Implicit Local<> handle scope created by NAN_METHOD(.)
179 CallbackBridge<T, L>* bridge = static_cast<CallbackBridge<T, L>*>(Nan::GetInternalFieldPointer(info.This(), 0));
180 Nan::TryCatch try_catch;
182 bridge->return_value = bridge->post_process_return_value(info[0]);
185 uv_mutex_lock(&bridge->cv_mutex);
186 bridge->has_returned = true;
187 uv_mutex_unlock(&bridge->cv_mutex);
190 uv_cond_broadcast(&bridge->condition_variable);
192 if (try_catch.HasCaught()) {
193 Nan::FatalException(try_catch);
197 template <typename T, typename L>
198 Nan::MaybeLocal<v8::Function> CallbackBridge<T, L>::get_wrapper_constructor() {
199 /* Uses handle scope created in the CallbackBridge<T, L> constructor */
200 if (wrapper_constructor.IsEmpty()) {
201 v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
202 tpl->SetClassName(Nan::New("CallbackBridge").ToLocalChecked());
203 tpl->InstanceTemplate()->SetInternalFieldCount(1);
205 Nan::SetPrototypeTemplate(tpl, "success",
206 Nan::New<v8::FunctionTemplate>(ReturnCallback)
209 wrapper_constructor.Reset(Nan::GetFunction(tpl).ToLocalChecked());
212 return Nan::New(wrapper_constructor);
215 template <typename T, typename L>
216 NAN_METHOD(CallbackBridge<T COMMA L>::New) {
217 info.GetReturnValue().Set(info.This());
220 template <typename T, typename L>
221 void CallbackBridge<T, L>::async_gone(uv_handle_t *handle) {
222 delete (uv_async_t *)handle;