Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit f0fca38

Browse files
committed
Implement timeout for callbacks
1 parent 660cc66 commit f0fca38

File tree

2 files changed

+42
-23
lines changed

2 files changed

+42
-23
lines changed

src/binding.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,15 @@ Sass_Import_List sass_importer(const char* cur_path, Sass_Importer_Entry cb, str
1818
argv.push_back((void*)prev_path);
1919

2020
TRACEINST(&bridge) << "Importer will be executed";
21-
return bridge(argv);
21+
22+
Sass_Import_List retval = bridge(argv);
23+
if (bridge.timedout) {
24+
TRACEINST(&bridge) << "Importer timeout";
25+
retval = sass_make_import_list(1);
26+
retval[0] = sass_make_import_entry(0, 0, 0);
27+
sass_import_set_error(retval[0], "Importer timed out or blocked (>2000ms)", -1, -1);
28+
}
29+
return retval;
2230
}
2331

2432
union Sass_Value* sass_custom_function(const union Sass_Value* s_args, Sass_Function_Entry cb, struct Sass_Options* opts)
@@ -33,7 +41,12 @@ union Sass_Value* sass_custom_function(const union Sass_Value* s_args, Sass_Func
3341

3442
try {
3543
TRACEINST(&bridge) << "Function will be executed";
36-
return bridge(argv);
44+
Sass_Value *retval = bridge(argv);
45+
if (bridge.timedout) {
46+
TRACEINST(&bridge) << "Function timeout";
47+
return sass_make_error("Function timed out or blocked (>2000ms)");
48+
} else
49+
return retval;
3750
}
3851
catch (const std::exception& e) {
3952
return sass_make_error(e.what());

src/callback_bridge.h

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class CallbackBridge {
2020

2121
// Executes the callback
2222
T operator()(std::vector<void*>);
23+
int timedout;
2324

2425
protected:
2526
// We will expose a bridge object to the JS callback that wraps this instance so we don't loose context.
@@ -60,7 +61,7 @@ template <typename T, typename L>
6061
Nan::Persistent<v8::Function> CallbackBridge<T, L>::wrapper_constructor;
6162

6263
template <typename T, typename L>
63-
CallbackBridge<T, L>::CallbackBridge(v8::Local<v8::Function> callback, bool is_sync) : callback(new Nan::Callback(callback)), is_sync(is_sync) {
64+
CallbackBridge<T, L>::CallbackBridge(v8::Local<v8::Function> callback, bool is_sync) : timedout(0), callback(new Nan::Callback(callback)), is_sync(is_sync) {
6465
/*
6566
* This is invoked from the main JavaScript thread.
6667
* V8 context is available.
@@ -70,7 +71,7 @@ CallbackBridge<T, L>::CallbackBridge(v8::Local<v8::Function> callback, bool is_s
7071
}
7172

7273
template <typename T, typename L>
73-
CallbackBridge<T, L>::CallbackBridge(const CallbackBridge<T,L>& other) : callback(new Nan::Callback(other.callback->GetFunction())), is_sync(other.is_sync) {
74+
CallbackBridge<T, L>::CallbackBridge(const CallbackBridge<T,L>& other) : timedout(0), callback(new Nan::Callback(other.callback->GetFunction())), is_sync(other.is_sync) {
7475
/*
7576
* This is invoked from the main JavaScript thread.
7677
* V8 context is available.
@@ -99,6 +100,7 @@ CallbackBridge<T, L>::operator= (const CallbackBridge<T,L>& other)
99100

100101
this->callback = new Nan::Callback(other.callback->GetFunction());
101102
this->is_sync = other.is_sync;
103+
this->timedout = 0;
102104
init_uv();
103105
init_wrapper();
104106
}
@@ -174,12 +176,12 @@ T CallbackBridge<T, L>::operator()(std::vector<void*> argv) {
174176
* async I/O executed from JavaScript callbacks.
175177
*/
176178
this->argv = argv;
177-
179+
this->timedout = 0;
178180
uv_mutex_lock(&this->cv_mutex);
179181
this->has_returned = false;
180182
uv_async_send(this->async);
181-
while (!this->has_returned) {
182-
uv_cond_wait(&this->condition_variable, &this->cv_mutex);
183+
while (!this->has_returned && this->timedout == 0) {
184+
this->timedout = uv_cond_timedwait(&this->condition_variable, &this->cv_mutex, 2 * (uint64_t)1e9);
183185
}
184186
uv_mutex_unlock(&this->cv_mutex);
185187
return this->return_value;
@@ -199,16 +201,18 @@ void CallbackBridge<T, L>::dispatched_async_uv_callback(uv_async_t *req) {
199201
* from types invoked by pre_process_args() and
200202
* post_process_args().
201203
*/
202-
Nan::HandleScope scope;
203-
Nan::TryCatch try_catch;
204+
if (!bridge->timedout) {
205+
Nan::HandleScope scope;
206+
Nan::TryCatch try_catch;
204207

205-
std::vector<v8::Local<v8::Value>> argv_v8 = bridge->pre_process_args(bridge->argv);
206-
argv_v8.push_back(Nan::New(bridge->wrapper));
208+
std::vector<v8::Local<v8::Value>> argv_v8 = bridge->pre_process_args(bridge->argv);
209+
argv_v8.push_back(Nan::New(bridge->wrapper));
207210

208-
bridge->callback->Call(argv_v8.size(), &argv_v8[0]);
211+
bridge->callback->Call(argv_v8.size(), &argv_v8[0]);
209212

210-
if (try_catch.HasCaught()) {
211-
Nan::FatalException(try_catch);
213+
if (try_catch.HasCaught()) {
214+
Nan::FatalException(try_catch);
215+
}
212216
}
213217
}
214218

@@ -225,18 +229,20 @@ NAN_METHOD(CallbackBridge<T COMMA L>::ReturnCallback) {
225229
CallbackBridge<T, L>* bridge = static_cast<CallbackBridge<T, L>*>(Nan::GetInternalFieldPointer(info.This(), 0));
226230
Nan::TryCatch try_catch;
227231

228-
bridge->return_value = bridge->post_process_return_value(info[0]);
232+
if (!bridge->timedout) {
233+
bridge->return_value = bridge->post_process_return_value(info[0]);
229234

230-
{
231-
uv_mutex_lock(&bridge->cv_mutex);
232-
bridge->has_returned = true;
233-
uv_mutex_unlock(&bridge->cv_mutex);
234-
}
235+
{
236+
uv_mutex_lock(&bridge->cv_mutex);
237+
bridge->has_returned = true;
238+
uv_mutex_unlock(&bridge->cv_mutex);
239+
}
235240

236-
uv_cond_broadcast(&bridge->condition_variable);
241+
uv_cond_broadcast(&bridge->condition_variable);
237242

238-
if (try_catch.HasCaught()) {
239-
Nan::FatalException(try_catch);
243+
if (try_catch.HasCaught()) {
244+
Nan::FatalException(try_catch);
245+
}
240246
}
241247
}
242248

0 commit comments

Comments
 (0)