Initial commit
[yaffs-website] / node_modules / node-sass / src / callback_bridge.h
1 #ifndef CALLBACK_BRIDGE_H
2 #define CALLBACK_BRIDGE_H
3
4 #include <vector>
5 #include <nan.h>
6 #include <algorithm>
7 #include <uv.h>
8
9 #define COMMA ,
10
11 template <typename T, typename L = void*>
12 class CallbackBridge {
13   public:
14     CallbackBridge(v8::Local<v8::Function>, bool);
15     virtual ~CallbackBridge();
16
17     // Executes the callback
18     T operator()(std::vector<void*>);
19
20   protected:
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;
29
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*);
33
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;
38
39
40     virtual std::vector<v8::Local<v8::Value>> pre_process_args(std::vector<L>) const =0;
41
42     Nan::Callback* callback;
43     bool is_sync;
44
45     uv_mutex_t cv_mutex;
46     uv_cond_t condition_variable;
47     uv_async_t *async;
48     std::vector<L> argv;
49     bool has_returned;
50     T return_value;
51 };
52
53 template <typename T, typename L>
54 Nan::Persistent<v8::Function> CallbackBridge<T, L>::wrapper_constructor;
55
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) {
58   /* 
59    * This is invoked from the main JavaScript thread.
60    * V8 context is available.
61    */
62   Nan::HandleScope scope;
63   uv_mutex_init(&this->cv_mutex);
64   uv_cond_init(&this->condition_variable);
65   if (!is_sync) {
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);
69   }
70
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);
74 }
75
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);
82
83   if (!is_sync) {
84     uv_close((uv_handle_t*)this->async, &async_gone);
85   }
86 }
87
88 template <typename T, typename L>
89 T CallbackBridge<T, L>::operator()(std::vector<void*> argv) {
90   // argv.push_back(wrapper);
91   if (this->is_sync) {
92     /* 
93      * This is invoked from the main JavaScript thread.
94      * V8 context is available.
95      *
96      * Establish Local<> scope for all functions
97      * from types invoked by pre_process_args() and
98      * post_process_args().
99      */
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);
105     }
106
107     argv_v8.push_back(Nan::New(wrapper));
108
109     return this->post_process_return_value(
110       this->callback->Call(argv_v8.size(), &argv_v8[0])
111     );
112   } else {
113     /* 
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
118      *
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.
126      */
127     this->argv = argv;
128
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);
134     }
135     uv_mutex_unlock(&this->cv_mutex);
136     return this->return_value;
137   }
138 }
139
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);
143
144   /* 
145    * Function scheduled via uv_async mechanism, therefore
146    * it is invoked from the main JavaScript thread.
147    * V8 context is available.
148    *
149    * Establish Local<> scope for all functions
150    * from types invoked by pre_process_args() and
151    * post_process_args().
152    */
153   Nan::HandleScope scope;
154   Nan::TryCatch try_catch;
155
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);
159   }
160   argv_v8.push_back(Nan::New(bridge->wrapper));
161
162   bridge->callback->Call(argv_v8.size(), &argv_v8[0]);
163
164   if (try_catch.HasCaught()) {
165     Nan::FatalException(try_catch);
166   }
167 }
168
169 template <typename T, typename L>
170 NAN_METHOD(CallbackBridge<T COMMA L>::ReturnCallback) {
171
172   /* 
173    * Callback function invoked by the user code.
174    * It is invoked from the main JavaScript thread.
175    * V8 context is available.
176    *
177    * Implicit Local<> handle scope created by NAN_METHOD(.)
178    */
179   CallbackBridge<T, L>* bridge = static_cast<CallbackBridge<T, L>*>(Nan::GetInternalFieldPointer(info.This(), 0));
180   Nan::TryCatch try_catch;
181
182   bridge->return_value = bridge->post_process_return_value(info[0]);
183
184   {
185     uv_mutex_lock(&bridge->cv_mutex);
186     bridge->has_returned = true;
187     uv_mutex_unlock(&bridge->cv_mutex);
188   }
189
190   uv_cond_broadcast(&bridge->condition_variable);
191
192   if (try_catch.HasCaught()) {
193     Nan::FatalException(try_catch);
194   }
195 }
196
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);
204
205     Nan::SetPrototypeTemplate(tpl, "success",
206       Nan::New<v8::FunctionTemplate>(ReturnCallback)
207     );
208
209     wrapper_constructor.Reset(Nan::GetFunction(tpl).ToLocalChecked());
210   }
211
212   return Nan::New(wrapper_constructor);
213 }
214
215 template <typename T, typename L>
216 NAN_METHOD(CallbackBridge<T COMMA L>::New) {
217   info.GetReturnValue().Set(info.This());
218 }
219
220 template <typename T, typename L>
221 void CallbackBridge<T, L>::async_gone(uv_handle_t *handle) {
222   delete (uv_async_t *)handle;
223 }
224
225 #endif