Skip to content

Commit

Permalink
Fix Threading issues (SV access from different Thread) (#1883)
Browse files Browse the repository at this point in the history
## Description

There are a few functions that check if you're currently on the REA UI Runtime by using `RuntimeDecorator::isWorkletRuntime`. This might actually lead to threading issues if you're calling those functions from another worklet runtime that is not from Reanimated (e.g. VisionCamera Frame Processors, Multithreading, ...) - so this PR fixes this. A few of those functions should actually check if they are specifically on the UI thread, Worklet thread is not enough.

## Changes

- Add `RuntimeDecorator::isUIRuntime` - a function that specifically checks if the given `jsi::Runtime` is the REA UI Runtime as opposed to any Worklet Runtime
- Replace a few `isWorkletRuntime` calls with `isUIRuntime`

<!--

## Screenshots / GIFs
  • Loading branch information
mrousavy authored May 4, 2021
1 parent bb88e9f commit c511a5d
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 33 deletions.
40 changes: 22 additions & 18 deletions Common/cpp/SharedItems/MutableValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ void MutableValue::setValue(jsi::Runtime &rt, const jsi::Value &newValue) {
listener.second();
}
};
if (RuntimeDecorator::isWorkletRuntime(rt)) {
if (RuntimeDecorator::isUIRuntime(rt)) {
notifyListeners();
} else {
runtimeManager->scheduler->scheduleOnUI([notifyListeners] {
Expand All @@ -32,8 +32,27 @@ jsi::Value MutableValue::getValue(jsi::Runtime &rt) {

void MutableValue::set(jsi::Runtime &rt, const jsi::PropNameID &name, const jsi::Value &newValue) {
auto propName = name.utf8(rt);
if (!runtimeManager->valueSetter) {
throw jsi::JSError(rt, "Value-Setter is not yet configured! Make sure the core-functions are installed.");
}

if (RuntimeDecorator::isReactRuntime(rt)) {
if (RuntimeDecorator::isUIRuntime(rt)) {
// UI thread
if (propName == "value") {
auto setterProxy = jsi::Object::createFromHostObject(rt, std::make_shared<MutableValueSetterProxy>(shared_from_this()));
runtimeManager->valueSetter->getValue(rt)
.asObject(rt)
.asFunction(rt)
.callWithThis(rt, setterProxy, newValue);
} else if (propName == "_animation") {
// TODO: assert to allow animation to be set from UI only
if (animation.expired()) {
animation = getWeakRef(rt);
}
*animation.lock() = jsi::Value(rt, newValue);
}
} else {
// React-JS Thread or another threaded Runtime.
if (propName == "value") {
auto shareable = ShareableValue::adapt(rt, newValue, runtimeManager);
runtimeManager->scheduler->scheduleOnUI([this, shareable] {
Expand All @@ -46,23 +65,8 @@ void MutableValue::set(jsi::Runtime &rt, const jsi::PropNameID &name, const jsi:
.callWithThis(rt, setterProxy, newValue);
});
}
return;
}

// UI thread
if (propName == "value") {
auto setterProxy = jsi::Object::createFromHostObject(rt, std::make_shared<MutableValueSetterProxy>(shared_from_this()));
runtimeManager->valueSetter->getValue(rt)
.asObject(rt)
.asFunction(rt)
.callWithThis(rt, setterProxy, newValue);
} else if (propName == "_animation") {
// TODO: assert to allow animation to be set from UI only
if (animation.expired()) {
animation = getWeakRef(rt);
}
*animation.lock() = jsi::Value(rt, newValue);
}
}

jsi::Value MutableValue::get(jsi::Runtime &rt, const jsi::PropNameID &name) {
Expand All @@ -72,7 +76,7 @@ jsi::Value MutableValue::get(jsi::Runtime &rt, const jsi::PropNameID &name) {
return getValue(rt);
}

if (RuntimeDecorator::isWorkletRuntime(rt)) {
if (RuntimeDecorator::isUIRuntime(rt)) {
// _value and _animation should be accessed from UI only
if (propName == "_value") {
return getValue(rt);
Expand Down
2 changes: 1 addition & 1 deletion Common/cpp/SharedItems/ShareableValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ jsi::Value ShareableValue::toJSValue(jsi::Runtime &rt) {
auto runtimeManager = this->runtimeManager;
auto& frozenObject = ValueWrapper::asFrozenObject(this->valueContainer);
if (RuntimeDecorator::isWorkletRuntime(rt)) {
// when running on UI thread we prep a function
// when running on worklet thread we prep a function

auto jsThis = std::make_shared<jsi::Object>(frozenObject->shallowClone(*runtimeManager->runtime));
std::shared_ptr<jsi::Function> funPtr(runtimeManager->workletsCache->getFunction(rt, frozenObject));
Expand Down
34 changes: 20 additions & 14 deletions Common/cpp/Tools/RuntimeDecorator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ void RuntimeDecorator::decorateRuntime(jsi::Runtime &rt, std::string label) {
rt.global().setProperty(rt, "_WORKLET", jsi::Value(true));
// This property will be used for debugging
rt.global().setProperty(rt, "_LABEL", jsi::String::createFromAscii(rt, label));

jsi::Object dummyGlobal(rt);
auto dummyFunction = [](
jsi::Runtime &rt,
Expand All @@ -21,12 +21,12 @@ void RuntimeDecorator::decorateRuntime(jsi::Runtime &rt, std::string label) {
return jsi::Value::undefined();
};
jsi::Function __reanimatedWorkletInit = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "__reanimatedWorkletInit"), 1, dummyFunction);

dummyGlobal.setProperty(rt, "__reanimatedWorkletInit", __reanimatedWorkletInit);
rt.global().setProperty(rt, "global", dummyGlobal);

rt.global().setProperty(rt, "jsThis", jsi::Value::undefined());

auto callback = [](
jsi::Runtime &rt,
const jsi::Value &thisValue,
Expand All @@ -47,7 +47,7 @@ void RuntimeDecorator::decorateRuntime(jsi::Runtime &rt, std::string label) {
};
jsi::Value log = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "_log"), 1, callback);
rt.global().setProperty(rt, "_log", log);

auto setGlobalConsole = [](
jsi::Runtime &rt,
const jsi::Value &thisValue,
Expand All @@ -67,7 +67,8 @@ void RuntimeDecorator::decorateUIRuntime(jsi::Runtime &rt,
MeasuringFunction measure,
TimeProviderFunction getCurrentTime) {
RuntimeDecorator::decorateRuntime(rt, "UI");

rt.global().setProperty(rt, "_UI", jsi::Value(true));

auto clb = [updater](
jsi::Runtime &rt,
const jsi::Value &thisValue,
Expand All @@ -82,8 +83,8 @@ void RuntimeDecorator::decorateUIRuntime(jsi::Runtime &rt,
};
jsi::Value updateProps = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "_updateProps"), 2, clb);
rt.global().setProperty(rt, "_updateProps", updateProps);


auto clb2 = [requestFrame](
jsi::Runtime &rt,
const jsi::Value &thisValue,
Expand All @@ -98,7 +99,7 @@ void RuntimeDecorator::decorateUIRuntime(jsi::Runtime &rt,
};
jsi::Value requestAnimationFrame = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "requestAnimationFrame"), 1, clb2);
rt.global().setProperty(rt, "requestAnimationFrame", requestAnimationFrame);

auto clb3 = [scrollTo](
jsi::Runtime &rt,
const jsi::Value &thisValue,
Expand All @@ -114,7 +115,7 @@ void RuntimeDecorator::decorateUIRuntime(jsi::Runtime &rt,
};
jsi::Value scrollToFunction = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "_scrollTo"), 4, clb3);
rt.global().setProperty(rt, "_scrollTo", scrollToFunction);

auto clb4 = [measure](
jsi::Runtime &rt,
const jsi::Value &thisValue,
Expand All @@ -131,7 +132,7 @@ void RuntimeDecorator::decorateUIRuntime(jsi::Runtime &rt,
};
jsi::Value measureFunction = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "_measure"), 1, clb4);
rt.global().setProperty(rt, "_measure", measureFunction);

auto clb6 = [getCurrentTime](
jsi::Runtime &rt,
const jsi::Value &thisValue,
Expand All @@ -142,16 +143,21 @@ void RuntimeDecorator::decorateUIRuntime(jsi::Runtime &rt,
};
jsi::Value timeFun = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "_getCurrentTime"), 0, clb6);
rt.global().setProperty(rt, "_getCurrentTime", timeFun);

rt.global().setProperty(rt, "_frameTimestamp", jsi::Value::undefined());
rt.global().setProperty(rt, "_eventTimestamp", jsi::Value::undefined());
}

bool RuntimeDecorator::isWorkletRuntime(jsi::Runtime& rt) {
auto isUi = rt.global().getProperty(rt, "_WORKLET");
bool RuntimeDecorator::isUIRuntime(jsi::Runtime& rt) {
auto isUi = rt.global().getProperty(rt, "_UI");
return isUi.isBool() && isUi.getBool();
}

bool RuntimeDecorator::isWorkletRuntime(jsi::Runtime& rt) {
auto isWorklet = rt.global().getProperty(rt, "_WORKLET");
return isWorklet.isBool() && isWorklet.getBool();
}

bool RuntimeDecorator::isReactRuntime(jsi::Runtime& rt) {
return !isWorkletRuntime(rt);
}
Expand Down
10 changes: 10 additions & 0 deletions Common/cpp/headers/Tools/RuntimeDecorator.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,17 @@ class RuntimeDecorator {
MeasuringFunction measure,
TimeProviderFunction getCurrentTime);

/**
Returns true if the given Runtime is the Reanimated UI-Thread Runtime.
*/
static bool isUIRuntime(jsi::Runtime &rt);
/**
Returns true if the given Runtime is a Runtime that supports Workletization. (REA, Vision, ...)
*/
static bool isWorkletRuntime(jsi::Runtime &rt);
/**
Returns true if the given Runtime is the default React-JS Runtime.
*/
static bool isReactRuntime(jsi::Runtime &rt);
};

Expand Down

0 comments on commit c511a5d

Please sign in to comment.