FS-10465: [mod_v8] Fix javascript failures when a session callback is called from another thread/leg.

This commit is contained in:
Andrey Volk 2017-07-03 23:01:59 +03:00
parent f9d23ccadd
commit 105afc3f29
4 changed files with 44 additions and 15 deletions

View File

@ -146,6 +146,7 @@ public:
FSSession *jss_b; FSSession *jss_b;
v8::Persistent<v8::Object> session_obj_a; v8::Persistent<v8::Object> session_obj_a;
v8::Persistent<v8::Object> session_obj_b; v8::Persistent<v8::Object> session_obj_b;
v8::Persistent<v8::Context> context;
FSInputCallbackState(void); FSInputCallbackState(void);
~FSInputCallbackState(void); ~FSInputCallbackState(void);

View File

@ -130,6 +130,16 @@
if (v8::V8::IsExecutionTerminating(info.GetIsolate())) return;\ if (v8::V8::IsExecutionTerminating(info.GetIsolate())) return;\
if (JSMain::GetScriptInstanceFromIsolate(info.GetIsolate()) && JSMain::GetScriptInstanceFromIsolate(info.GetIsolate())->GetForcedTermination()) return if (JSMain::GetScriptInstanceFromIsolate(info.GetIsolate()) && JSMain::GetScriptInstanceFromIsolate(info.GetIsolate())->GetForcedTermination()) return
/* Macro for easy unlocking an isolate on a long running c call */
#define JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(call) {\
/* Unlock isolate on a long running c call to let another thread execute the callback */\
info.GetIsolate()->Exit();\
Unlocker unlock(info.GetIsolate());\
call;\
}\
/* Lock it back */\
info.GetIsolate()->Enter();
/* strdup function for all platforms */ /* strdup function for all platforms */
#ifdef NDEBUG #ifdef NDEBUG
#if (_MSC_VER >= 1500) // VC9+ #if (_MSC_VER >= 1500) // VC9+

View File

@ -686,12 +686,13 @@ JS_GLOBAL_FUNCTION_IMPL_STATIC(Bridge)
cb_state.session_obj_a.Reset(info.GetIsolate(), obj_a); cb_state.session_obj_a.Reset(info.GetIsolate(), obj_a);
cb_state.session_obj_b.Reset(info.GetIsolate(), obj_b); cb_state.session_obj_b.Reset(info.GetIsolate(), obj_b);
cb_state.session_state = jss_a; cb_state.session_state = jss_a;
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
dtmf_func = FSSession::CollectInputCallback; dtmf_func = FSSession::CollectInputCallback;
bp = &cb_state; bp = &cb_state;
} }
} }
switch_ivr_multi_threaded_bridge(jss_a->GetSession(), jss_b->GetSession(), dtmf_func, bp, bp); JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_multi_threaded_bridge(jss_a->GetSession(), jss_b->GetSession(), dtmf_func, bp, bp));
info.GetReturnValue().Set(true); info.GetReturnValue().Set(true);
} }

View File

@ -72,6 +72,15 @@ static const char js_class_name[] = "Session";
}\ }\
} while (foo == 1) } while (foo == 1)
#define LOCK_ISOLATE_ON_CALLBACK_MACRO() \
FSSession *obj = cb_state->session_state;\
Isolate *isolate = obj->GetOwner()->GetIsolate();\
Locker lock(isolate);\
Isolate::Scope isolate_scope(isolate);\
HandleScope handle_scope(isolate);\
Local<Context> context = Local<Context>::New(isolate, cb_state->context);\
Context::Scope context_scope(context);
static int foo = 0; static int foo = 0;
string FSSession::GetJSClassName() string FSSession::GetJSClassName()
@ -173,6 +182,7 @@ FSInputCallbackState::~FSInputCallbackState(void)
ret.Reset(); ret.Reset();
session_obj_a.Reset(); session_obj_a.Reset();
session_obj_b.Reset(); session_obj_b.Reset();
context.Reset();
} }
#define MAX_STACK_DEPTH 2 #define MAX_STACK_DEPTH 2
@ -285,8 +295,8 @@ switch_status_t FSSession::CommonCallback(switch_core_session_t *session, void *
switch_status_t FSSession::StreamInputCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen) switch_status_t FSSession::StreamInputCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
{ {
FSInputCallbackState *cb_state = (FSInputCallbackState *)buf; FSInputCallbackState *cb_state = (FSInputCallbackState *)buf;
FSSession *obj = cb_state->session_state; LOCK_ISOLATE_ON_CALLBACK_MACRO();
HandleScope handle_scope(obj->GetOwner()->GetIsolate());
switch_status_t status; switch_status_t status;
switch_file_handle_t *fh = (switch_file_handle_t *)cb_state->extra; switch_file_handle_t *fh = (switch_file_handle_t *)cb_state->extra;
@ -400,8 +410,8 @@ switch_status_t FSSession::StreamInputCallback(switch_core_session_t *session, v
switch_status_t FSSession::RecordInputCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen) switch_status_t FSSession::RecordInputCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
{ {
FSInputCallbackState *cb_state = (FSInputCallbackState *)buf; FSInputCallbackState *cb_state = (FSInputCallbackState *)buf;
FSSession *obj = cb_state->session_state; LOCK_ISOLATE_ON_CALLBACK_MACRO();
HandleScope handle_scope(obj->GetOwner()->GetIsolate());
switch_status_t status; switch_status_t status;
switch_file_handle_t *fh = (switch_file_handle_t *)cb_state->extra; switch_file_handle_t *fh = (switch_file_handle_t *)cb_state->extra;
@ -441,8 +451,8 @@ switch_status_t FSSession::RecordInputCallback(switch_core_session_t *session, v
switch_status_t FSSession::CollectInputCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen) switch_status_t FSSession::CollectInputCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
{ {
FSInputCallbackState *cb_state = (FSInputCallbackState *)buf; FSInputCallbackState *cb_state = (FSInputCallbackState *)buf;
FSSession *obj = cb_state->session_state; LOCK_ISOLATE_ON_CALLBACK_MACRO();
HandleScope handle_scope(obj->GetOwner()->GetIsolate());
const char *ret; const char *ret;
switch_status_t status; switch_status_t status;
@ -528,6 +538,7 @@ JS_SESSION_FUNCTION_IMPL(RecordFile)
if (!func.IsEmpty()) { if (!func.IsEmpty()) {
cb_state.session_state = this; cb_state.session_state = this;
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
cb_state.function.Reset(info.GetIsolate(), func); cb_state.function.Reset(info.GetIsolate(), func);
if (info.Length() > 2 && !info[2].IsEmpty()) { if (info.Length() > 2 && !info[2].IsEmpty()) {
cb_state.arg.Reset(info.GetIsolate(), info[2]); cb_state.arg.Reset(info.GetIsolate(), info[2]);
@ -557,7 +568,7 @@ JS_SESSION_FUNCTION_IMPL(RecordFile)
args.buf = bp; args.buf = bp;
args.buflen = len; args.buflen = len;
switch_ivr_record_file(this->_session, &fh, file_name.c_str(), &args, limit); JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_record_file(this->_session, &fh, file_name.c_str(), &args, limit));
info.GetReturnValue().Set(cb_state.ret); info.GetReturnValue().Set(cb_state.ret);
CheckHangupHook(this, &ret); CheckHangupHook(this, &ret);
@ -593,6 +604,7 @@ JS_SESSION_FUNCTION_IMPL(CollectInput)
} }
cb_state.session_state = this; cb_state.session_state = this;
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
dtmf_func = CollectInputCallback; dtmf_func = CollectInputCallback;
bp = &cb_state; bp = &cb_state;
len = sizeof(cb_state); len = sizeof(cb_state);
@ -611,7 +623,7 @@ JS_SESSION_FUNCTION_IMPL(CollectInput)
args.buf = bp; args.buf = bp;
args.buflen = len; args.buflen = len;
switch_ivr_collect_digits_callback(this->_session, &args, digit_timeout, abs_timeout); JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_collect_digits_callback(this->_session, &args, digit_timeout, abs_timeout));
info.GetReturnValue().Set(cb_state.ret); info.GetReturnValue().Set(cb_state.ret);
CheckHangupHook(this, &ret); CheckHangupHook(this, &ret);
@ -674,6 +686,7 @@ JS_SESSION_FUNCTION_IMPL(SayPhrase)
} }
cb_state.session_state = this; cb_state.session_state = this;
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
dtmf_func = CollectInputCallback; dtmf_func = CollectInputCallback;
bp = &cb_state; bp = &cb_state;
len = sizeof(cb_state); len = sizeof(cb_state);
@ -685,7 +698,7 @@ JS_SESSION_FUNCTION_IMPL(SayPhrase)
args.buf = bp; args.buf = bp;
args.buflen = len; args.buflen = len;
switch_ivr_phrase_macro(this->_session, phrase_name.c_str(), phrase_data.c_str(), phrase_lang.c_str(), &args); JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_phrase_macro(this->_session, phrase_name.c_str(), phrase_data.c_str(), phrase_lang.c_str(), &args));
info.GetReturnValue().Set(cb_state.ret); info.GetReturnValue().Set(cb_state.ret);
CheckHangupHook(this, &ret); CheckHangupHook(this, &ret);
@ -825,6 +838,7 @@ JS_SESSION_FUNCTION_IMPL(StreamFile)
} }
cb_state.session_state = this; cb_state.session_state = this;
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
dtmf_func = StreamInputCallback; dtmf_func = StreamInputCallback;
bp = &cb_state; bp = &cb_state;
len = sizeof(cb_state); len = sizeof(cb_state);
@ -847,7 +861,7 @@ JS_SESSION_FUNCTION_IMPL(StreamFile)
args.input_callback = dtmf_func; args.input_callback = dtmf_func;
args.buf = bp; args.buf = bp;
args.buflen = len; args.buflen = len;
switch_ivr_play_file(this->_session, &fh, file_name.c_str(), &args); JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_play_file(this->_session, &fh, file_name.c_str(), &args));
info.GetReturnValue().Set(cb_state.ret); info.GetReturnValue().Set(cb_state.ret);
switch_snprintf(posbuf, sizeof(posbuf), "%u", fh.offset_pos); switch_snprintf(posbuf, sizeof(posbuf), "%u", fh.offset_pos);
@ -894,6 +908,7 @@ JS_SESSION_FUNCTION_IMPL(Sleep)
} }
cb_state.session_state = this; cb_state.session_state = this;
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
dtmf_func = CollectInputCallback; dtmf_func = CollectInputCallback;
bp = &cb_state; bp = &cb_state;
len = sizeof(cb_state); len = sizeof(cb_state);
@ -908,7 +923,7 @@ JS_SESSION_FUNCTION_IMPL(Sleep)
args.input_callback = dtmf_func; args.input_callback = dtmf_func;
args.buf = bp; args.buf = bp;
args.buflen = len; args.buflen = len;
switch_ivr_sleep(this->_session, ms, (switch_bool_t)sync, &args); JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_sleep(this->_session, ms, (switch_bool_t)sync, &args));
info.GetReturnValue().Set(cb_state.ret); info.GetReturnValue().Set(cb_state.ret);
CheckHangupHook(this, &ret); CheckHangupHook(this, &ret);
@ -1069,6 +1084,7 @@ JS_SESSION_FUNCTION_IMPL(Speak)
} }
cb_state.session_state = this; cb_state.session_state = this;
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
dtmf_func = CollectInputCallback; dtmf_func = CollectInputCallback;
bp = &cb_state; bp = &cb_state;
len = sizeof(cb_state); len = sizeof(cb_state);
@ -1083,7 +1099,7 @@ JS_SESSION_FUNCTION_IMPL(Speak)
switch_core_speech_flush_tts(&this->_speech->sh); switch_core_speech_flush_tts(&this->_speech->sh);
if (switch_core_codec_ready(&this->_speech->codec)) { if (switch_core_codec_ready(&this->_speech->codec)) {
this->_speech->speaking = 1; this->_speech->speaking = 1;
switch_ivr_speak_text_handle(this->_session, &this->_speech->sh, &this->_speech->codec, NULL, (char *)text, &args); JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_speak_text_handle(this->_session, &this->_speech->sh, &this->_speech->codec, NULL, (char *)text, &args));
this->_speech->speaking = 0; this->_speech->speaking = 0;
} }
@ -1811,13 +1827,14 @@ JS_SESSION_FUNCTION_IMPL(Bridge)
cb_state.session_obj_a.Reset(info.GetIsolate(), info.Holder()); cb_state.session_obj_a.Reset(info.GetIsolate(), info.Holder());
cb_state.session_obj_b.Reset(info.GetIsolate(), obj_b); cb_state.session_obj_b.Reset(info.GetIsolate(), obj_b);
cb_state.session_state = this; cb_state.session_state = this;
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
dtmf_func = CollectInputCallback; dtmf_func = CollectInputCallback;
bp = &cb_state; bp = &cb_state;
} }
} }
switch_ivr_multi_threaded_bridge(_session, jss_b->_session, dtmf_func, bp, bp); JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_multi_threaded_bridge(_session, jss_b->_session, dtmf_func, bp, bp));
info.GetReturnValue().Set(true); info.GetReturnValue().Set(true);
} }