Skip to content

Commit 66a788f

Browse files
Kathy Grayfacebook-github-bot
authored andcommitted
Add locking around calls from JSCExecuter
Reviewed By: javache Differential Revision: D5488452 fbshipit-source-id: bda18e7948574117b8ce95894782e0e6e9c321de
1 parent 8d757e5 commit 66a788f

File tree

3 files changed

+83
-14
lines changed

3 files changed

+83
-14
lines changed

ReactCommon/cxxreact/JSCExecutor.cpp

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> scrip
287287
// TODO t15069155: reduce the number of overrides here
288288
#ifdef WITH_FBJSCEXTENSIONS
289289
if (auto fileStr = dynamic_cast<const JSBigFileString *>(script.get())) {
290+
JSContextLock lock(m_context);
290291
JSLoadSourceStatus jsStatus;
291292
auto bcSourceCode = JSCreateSourceCodeFromFile(fileStr->fd(), jsSourceURL, nullptr, &jsStatus);
292293

@@ -295,7 +296,6 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> scrip
295296
if (!bcSourceCode) {
296297
throw std::runtime_error("Unexpected error opening compiled bundle");
297298
}
298-
299299
evaluateSourceCode(m_context, bcSourceCode, jsSourceURL);
300300

301301
flush();
@@ -332,6 +332,7 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> scrip
332332
#endif
333333
{
334334
String jsScript;
335+
JSContextLock lock(m_context);
335336
{
336337
SystraceSection s_("JSCExecutor::loadApplicationScript-createExpectingAscii");
337338
ReactMarker::logMarker(ReactMarker::JS_BUNDLE_STRING_CONVERT_START);
@@ -433,10 +434,10 @@ void JSCExecutor::flush() {
433434

434435
void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
435436
SystraceSection s("JSCExecutor::callFunction");
436-
437437
// This weird pattern is because Value is not default constructible.
438438
// The lambda is inlined, so there's no overhead.
439439
auto result = [&] {
440+
JSContextLock lock(m_context);
440441
try {
441442
if (!m_callFunctionReturnResultAndFlushedQueueJS) {
442443
bindBridge();
@@ -451,13 +452,13 @@ void JSCExecutor::callFunction(const std::string& moduleId, const std::string& m
451452
std::runtime_error("Error calling " + moduleId + "." + methodId));
452453
}
453454
}();
454-
455455
callNativeModules(std::move(result));
456456
}
457457

458458
void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic& arguments) {
459459
SystraceSection s("JSCExecutor::invokeCallback");
460460
auto result = [&] {
461+
JSContextLock lock(m_context);
461462
try {
462463
if (!m_invokeCallbackAndReturnFlushedQueueJS) {
463464
bindBridge();
@@ -471,29 +472,29 @@ void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic&
471472
std::runtime_error(folly::to<std::string>("Error invoking callback ", callbackId)));
472473
}
473474
}();
474-
475475
callNativeModules(std::move(result));
476476
}
477477

478478
Value JSCExecutor::callFunctionSyncWithValue(
479479
const std::string& module, const std::string& method, Value args) {
480480
SystraceSection s("JSCExecutor::callFunction");
481-
482-
if (!m_callFunctionReturnResultAndFlushedQueueJS) {
483-
bindBridge();
484-
}
485-
Object result = m_callFunctionReturnResultAndFlushedQueueJS->callAsFunction({
486-
Value(m_context, String::createExpectingAscii(m_context, module)),
487-
Value(m_context, String::createExpectingAscii(m_context, method)),
488-
std::move(args),
489-
}).asObject();
481+
Object result = [&] {
482+
JSContextLock lock(m_context);
483+
if (!m_callFunctionReturnResultAndFlushedQueueJS) {
484+
bindBridge();
485+
}
486+
return m_callFunctionReturnResultAndFlushedQueueJS->callAsFunction({
487+
Value(m_context, String::createExpectingAscii(m_context, module)),
488+
Value(m_context, String::createExpectingAscii(m_context, method)),
489+
std::move(args),
490+
}).asObject();
491+
}();
490492

491493
Value length = result.getProperty("length");
492494

493495
if (!length.isNumber() || length.asInteger() != 2) {
494496
std::runtime_error("Return value of a callFunction must be an array of size 2");
495497
}
496-
497498
callNativeModules(result.getPropertyAtIndex(1));
498499
return result.getPropertyAtIndex(0);
499500
}

ReactCommon/jschelpers/JSCHelpers.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,24 @@
88

99
#include <glog/logging.h>
1010

11+
#if WITH_FBJSCEXTENSIONS
12+
#include <pthread.h>
13+
#endif
14+
1115
#include "JavaScriptCore.h"
1216
#include "Value.h"
1317

18+
#if WITH_FBJSCEXTENSIONS
19+
#undef ASSERT
20+
#undef WTF_EXPORT_PRIVATE
21+
22+
#include <JavaScriptCore/config.h>
23+
#include <wtf/WTFThreadData.h>
24+
25+
#undef TRUE
26+
#undef FALSE
27+
#endif
28+
1429
namespace facebook {
1530
namespace react {
1631

@@ -203,6 +218,42 @@ JSValueRef evaluateSourceCode(JSContextRef context, JSSourceCodeRef source, JSSt
203218
}
204219
#endif
205220

221+
JSContextLock::JSContextLock(JSGlobalContextRef ctx) noexcept
222+
#if WITH_FBJSCEXTENSIONS
223+
: ctx_(ctx),
224+
globalLock_(PTHREAD_MUTEX_INITIALIZER)
225+
{
226+
WTFThreadData& threadData = wtfThreadData();
227+
228+
// Code below is responsible for acquiring locks. It should execute
229+
// atomically, thus none of the functions invoked from now on are allowed to
230+
// throw an exception
231+
try {
232+
if (!threadData.isDebuggerThread()) {
233+
CHECK(0 == pthread_mutex_lock(&globalLock_));
234+
}
235+
JSLock(ctx_);
236+
} catch (...) {
237+
abort();
238+
}
239+
}
240+
#else
241+
{}
242+
#endif
243+
244+
245+
JSContextLock::~JSContextLock() noexcept {
246+
#if WITH_FBJSCEXTENSIONS
247+
WTFThreadData& threadData = wtfThreadData();
248+
249+
JSUnlock(ctx_);
250+
if (!threadData.isDebuggerThread()) {
251+
CHECK(0 == pthread_mutex_unlock(&globalLock_));
252+
}
253+
#endif
254+
}
255+
256+
206257
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation) {
207258
try {
208259
throw;

ReactCommon/jschelpers/JSCHelpers.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,23 @@ JSValueRef evaluateSourceCode(
9696
JSStringRef sourceURL);
9797
#endif
9898

99+
/**
100+
* A lock for protecting accesses to the JSGlobalContext
101+
* This will be a no-op for most compilations, where #if WITH_FBJSCEXTENSIONS is false,
102+
* but avoids deadlocks in execution environments with advanced locking requirements,
103+
* particularly with uses of the pthread mutex lock
104+
**/
105+
class JSContextLock {
106+
public:
107+
JSContextLock(JSGlobalContextRef ctx) noexcept;
108+
~JSContextLock() noexcept;
109+
private:
110+
#if WITH_FBJSCEXTENSIONS
111+
JSGlobalContextRef ctx_;
112+
pthread_mutex_t globalLock_;
113+
#endif
114+
};
115+
99116
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation);
100117
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, JSObjectRef jsFunctionCause);
101118

0 commit comments

Comments
 (0)