callback_bridge.h 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. #ifndef CALLBACK_BRIDGE_H
  2. #define CALLBACK_BRIDGE_H
  3. #include <vector>
  4. #include <nan.h>
  5. #include <algorithm>
  6. #include <uv.h>
  7. #define COMMA ,
  8. template <typename T, typename L = void*>
  9. class CallbackBridge {
  10. public:
  11. CallbackBridge(v8::Local<v8::Function>, bool);
  12. virtual ~CallbackBridge();
  13. // Executes the callback
  14. T operator()(std::vector<void*>);
  15. protected:
  16. // We will expose a bridge object to the JS callback that wraps this instance so we don't loose context.
  17. // This is the V8 constructor for such objects.
  18. static Nan::MaybeLocal<v8::Function> get_wrapper_constructor();
  19. static void async_gone(uv_handle_t *handle);
  20. static NAN_METHOD(New);
  21. static NAN_METHOD(ReturnCallback);
  22. static Nan::Persistent<v8::Function> wrapper_constructor;
  23. Nan::Persistent<v8::Object> wrapper;
  24. // The callback that will get called in the main thread after the worker thread used for the sass
  25. // compilation step makes a call to uv_async_send()
  26. static void dispatched_async_uv_callback(uv_async_t*);
  27. // The V8 values sent to our ReturnCallback must be read on the main thread not the sass worker thread.
  28. // This gives a chance to specialized subclasses to transform those values into whatever makes sense to
  29. // sass before we resume the worker thread.
  30. virtual T post_process_return_value(v8::Local<v8::Value>) const =0;
  31. virtual std::vector<v8::Local<v8::Value>> pre_process_args(std::vector<L>) const =0;
  32. Nan::Callback* callback;
  33. Nan::AsyncResource* async_resource;
  34. bool is_sync;
  35. uv_mutex_t cv_mutex;
  36. uv_cond_t condition_variable;
  37. uv_async_t *async;
  38. std::vector<L> argv;
  39. bool has_returned;
  40. T return_value;
  41. };
  42. template <typename T, typename L>
  43. Nan::Persistent<v8::Function> CallbackBridge<T, L>::wrapper_constructor;
  44. template <typename T, typename L>
  45. CallbackBridge<T, L>::CallbackBridge(v8::Local<v8::Function> callback, bool is_sync) : callback(new Nan::Callback(callback)), is_sync(is_sync) {
  46. /*
  47. * This is invoked from the main JavaScript thread.
  48. * V8 context is available.
  49. */
  50. Nan::HandleScope scope;
  51. uv_mutex_init(&this->cv_mutex);
  52. uv_cond_init(&this->condition_variable);
  53. if (!is_sync) {
  54. this->async = new uv_async_t;
  55. this->async->data = (void*) this;
  56. uv_async_init(uv_default_loop(), this->async, (uv_async_cb) dispatched_async_uv_callback);
  57. this->async_resource = new Nan::AsyncResource("node-sass:CallbackBridge");
  58. }
  59. v8::Local<v8::Function> func = CallbackBridge<T, L>::get_wrapper_constructor().ToLocalChecked();
  60. wrapper.Reset(Nan::NewInstance(func).ToLocalChecked());
  61. Nan::SetInternalFieldPointer(Nan::New(wrapper), 0, this);
  62. }
  63. template <typename T, typename L>
  64. CallbackBridge<T, L>::~CallbackBridge() {
  65. delete this->callback;
  66. this->wrapper.Reset();
  67. uv_cond_destroy(&this->condition_variable);
  68. uv_mutex_destroy(&this->cv_mutex);
  69. if (!is_sync) {
  70. uv_close((uv_handle_t*)this->async, &async_gone);
  71. delete this->async_resource;
  72. }
  73. }
  74. template <typename T, typename L>
  75. T CallbackBridge<T, L>::operator()(std::vector<void*> argv) {
  76. // argv.push_back(wrapper);
  77. if (this->is_sync) {
  78. /*
  79. * This is invoked from the main JavaScript thread.
  80. * V8 context is available.
  81. *
  82. * Establish Local<> scope for all functions
  83. * from types invoked by pre_process_args() and
  84. * post_process_args().
  85. */
  86. Nan::HandleScope scope;
  87. Nan::TryCatch try_catch;
  88. std::vector<v8::Local<v8::Value>> argv_v8 = pre_process_args(argv);
  89. if (try_catch.HasCaught()) {
  90. Nan::FatalException(try_catch);
  91. }
  92. argv_v8.push_back(Nan::New(wrapper));
  93. return this->post_process_return_value(
  94. Nan::Call(*this->callback, argv_v8.size(), &argv_v8[0]).ToLocalChecked()
  95. );
  96. } else {
  97. /*
  98. * This is invoked from the worker thread.
  99. * No V8 context and functions available.
  100. * Just wait for response from asynchronously
  101. * scheduled JavaScript code
  102. *
  103. * XXX Issue #1048: We block here even if the
  104. * event loop stops and the callback
  105. * would never be executed.
  106. * XXX Issue #857: By waiting here we occupy
  107. * one of the threads taken from the
  108. * uv threadpool. Might deadlock if
  109. * async I/O executed from JavaScript callbacks.
  110. */
  111. this->argv = argv;
  112. uv_mutex_lock(&this->cv_mutex);
  113. this->has_returned = false;
  114. uv_async_send(this->async);
  115. while (!this->has_returned) {
  116. uv_cond_wait(&this->condition_variable, &this->cv_mutex);
  117. }
  118. uv_mutex_unlock(&this->cv_mutex);
  119. return this->return_value;
  120. }
  121. }
  122. template <typename T, typename L>
  123. void CallbackBridge<T, L>::dispatched_async_uv_callback(uv_async_t *req) {
  124. CallbackBridge* bridge = static_cast<CallbackBridge*>(req->data);
  125. /*
  126. * Function scheduled via uv_async mechanism, therefore
  127. * it is invoked from the main JavaScript thread.
  128. * V8 context is available.
  129. *
  130. * Establish Local<> scope for all functions
  131. * from types invoked by pre_process_args() and
  132. * post_process_args().
  133. */
  134. Nan::HandleScope scope;
  135. Nan::TryCatch try_catch;
  136. std::vector<v8::Local<v8::Value>> argv_v8 = bridge->pre_process_args(bridge->argv);
  137. if (try_catch.HasCaught()) {
  138. Nan::FatalException(try_catch);
  139. }
  140. argv_v8.push_back(Nan::New(bridge->wrapper));
  141. bridge->callback->Call(argv_v8.size(), &argv_v8[0], bridge->async_resource);
  142. if (try_catch.HasCaught()) {
  143. Nan::FatalException(try_catch);
  144. }
  145. }
  146. template <typename T, typename L>
  147. NAN_METHOD(CallbackBridge<T COMMA L>::ReturnCallback) {
  148. /*
  149. * Callback function invoked by the user code.
  150. * It is invoked from the main JavaScript thread.
  151. * V8 context is available.
  152. *
  153. * Implicit Local<> handle scope created by NAN_METHOD(.)
  154. */
  155. CallbackBridge<T, L>* bridge = static_cast<CallbackBridge<T, L>*>(Nan::GetInternalFieldPointer(info.This(), 0));
  156. Nan::TryCatch try_catch;
  157. bridge->return_value = bridge->post_process_return_value(info[0]);
  158. {
  159. uv_mutex_lock(&bridge->cv_mutex);
  160. bridge->has_returned = true;
  161. uv_mutex_unlock(&bridge->cv_mutex);
  162. }
  163. uv_cond_broadcast(&bridge->condition_variable);
  164. if (try_catch.HasCaught()) {
  165. Nan::FatalException(try_catch);
  166. }
  167. }
  168. template <typename T, typename L>
  169. Nan::MaybeLocal<v8::Function> CallbackBridge<T, L>::get_wrapper_constructor() {
  170. /* Uses handle scope created in the CallbackBridge<T, L> constructor */
  171. if (wrapper_constructor.IsEmpty()) {
  172. v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
  173. tpl->SetClassName(Nan::New("CallbackBridge").ToLocalChecked());
  174. tpl->InstanceTemplate()->SetInternalFieldCount(1);
  175. Nan::SetPrototypeTemplate(tpl, "success",
  176. Nan::New<v8::FunctionTemplate>(ReturnCallback)
  177. );
  178. wrapper_constructor.Reset(Nan::GetFunction(tpl).ToLocalChecked());
  179. }
  180. return Nan::New(wrapper_constructor);
  181. }
  182. template <typename T, typename L>
  183. NAN_METHOD(CallbackBridge<T COMMA L>::New) {
  184. info.GetReturnValue().Set(info.This());
  185. }
  186. template <typename T, typename L>
  187. void CallbackBridge<T, L>::async_gone(uv_handle_t *handle) {
  188. delete (uv_async_t *)handle;
  189. }
  190. #endif