Skip to content

Commit

Permalink
Check for thread consistency in JSCRuntime
Browse files Browse the repository at this point in the history
Summary:
In the version of JSC on iOS 11, creating a JSContext on one
thread and using it on another can trigger subtle and nearly
impossible to debug reentrancy-related crashes in the VM (see
https://bugs.webkit.org/show_bug.cgi?id=186827).  In !NDEBUG builds,
check for this case and throw an exception, so it can be detected
early.

Reviewed By: amnn

Differential Revision: D13313264

fbshipit-source-id: ee85435c20e23c8520495ce743d2f91f2eeada5c
  • Loading branch information
mhorowitz authored and facebook-github-bot committed Dec 4, 2018
1 parent 512676c commit bdb084e
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 9 deletions.
2 changes: 2 additions & 0 deletions ReactCommon/cxxreact/Instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ void Instance::initializeBridge(
m_syncCV.notify_all();
});

// If the NativeToJsBridge ctor throws an exception, this check will
// likely happen before before the redbox can be rendered.
CHECK(nativeToJsBridge_);
}

Expand Down
40 changes: 31 additions & 9 deletions ReactCommon/jsi/JSCRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ struct Lock {
void unlock(const jsc::JSCRuntime&) const {}
};

#if __has_builtin(__builtin_expect)
#define JSC_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true)
#define JSC_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false)
#else
#define JSC_LIKELY(EXPR) (EXPR)
#define JSC_UNLIKELY(EXPR) (EXPR)
#endif

class JSCRuntime : public jsi::Runtime {
public:
// Creates new context in new context group
Expand Down Expand Up @@ -191,23 +199,32 @@ class JSCRuntime : public jsi::Runtime {
void checkException(JSValueRef exc, const char* msg);
void checkException(JSValueRef res, JSValueRef exc, const char* msg);

void checkThreadId() {
#ifndef NDEBUG
if (JSC_UNLIKELY(tid_ != std::this_thread::get_id())) {
// In the version of JSC on iOS 11, creating a JSContext on one
// thread and using it on another can trigger subtle and nearly
// impossible to debug reentrancy-related crashes in the VM (see
// https://bugs.webkit.org/show_bug.cgi?id=186827). In !NDEBUG
// builds, check for this case and throw an exception, so it can
// be detected early. This could be called anywhere, but for
// now, it's called only in a few less frequently used places to
// avoid unnecessary checks.
throw std::logic_error("Detected JSC thread hazard");
}
#endif
}

JSGlobalContextRef ctx_;
std::atomic<bool> ctxInvalid_;
std::string desc_;
#ifndef NDEBUG
mutable std::atomic<intptr_t> objectCounter_;
mutable std::atomic<intptr_t> stringCounter_;
std::thread::id tid_;
#endif
};

#if __has_builtin(__builtin_expect)
#define JSC_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true)
#define JSC_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false)
#else
#define JSC_LIKELY(EXPR) (EXPR)
#define JSC_UNLIKELY(EXPR) (EXPR)
#endif

#define JSC_ASSERT(x) \
do { \
if (JSC_UNLIKELY(!!(x))) { \
Expand Down Expand Up @@ -292,7 +309,8 @@ JSCRuntime::JSCRuntime(JSGlobalContextRef ctx)
#ifndef NDEBUG
,
objectCounter_(0),
stringCounter_(0)
stringCounter_(0),
tid_(std::this_thread::get_id())
#endif
{
}
Expand All @@ -317,6 +335,8 @@ JSCRuntime::~JSCRuntime() {
void JSCRuntime::evaluateJavaScript(
std::unique_ptr<const jsi::Buffer> buffer,
const std::string& sourceURL) {
checkThreadId();

std::string tmp(
reinterpret_cast<const char*>(buffer->data()), buffer->size());
JSStringRef sourceRef = JSStringCreateWithUTF8CString(tmp.c_str());
Expand All @@ -335,6 +355,8 @@ void JSCRuntime::evaluateJavaScript(
}

jsi::Object JSCRuntime::global() {
checkThreadId();

return createObject(JSContextGetGlobalObject(ctx_));
}

Expand Down

0 comments on commit bdb084e

Please sign in to comment.