Skip to content

Commit c511a5d

Browse files
authored
Fix Threading issues (SV access from different Thread) (#1883)
## 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
1 parent bb88e9f commit c511a5d

File tree

4 files changed

+53
-33
lines changed

4 files changed

+53
-33
lines changed

Common/cpp/SharedItems/MutableValue.cpp

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ void MutableValue::setValue(jsi::Runtime &rt, const jsi::Value &newValue) {
1616
listener.second();
1717
}
1818
};
19-
if (RuntimeDecorator::isWorkletRuntime(rt)) {
19+
if (RuntimeDecorator::isUIRuntime(rt)) {
2020
notifyListeners();
2121
} else {
2222
runtimeManager->scheduler->scheduleOnUI([notifyListeners] {
@@ -32,8 +32,27 @@ jsi::Value MutableValue::getValue(jsi::Runtime &rt) {
3232

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

36-
if (RuntimeDecorator::isReactRuntime(rt)) {
39+
if (RuntimeDecorator::isUIRuntime(rt)) {
40+
// UI thread
41+
if (propName == "value") {
42+
auto setterProxy = jsi::Object::createFromHostObject(rt, std::make_shared<MutableValueSetterProxy>(shared_from_this()));
43+
runtimeManager->valueSetter->getValue(rt)
44+
.asObject(rt)
45+
.asFunction(rt)
46+
.callWithThis(rt, setterProxy, newValue);
47+
} else if (propName == "_animation") {
48+
// TODO: assert to allow animation to be set from UI only
49+
if (animation.expired()) {
50+
animation = getWeakRef(rt);
51+
}
52+
*animation.lock() = jsi::Value(rt, newValue);
53+
}
54+
} else {
55+
// React-JS Thread or another threaded Runtime.
3756
if (propName == "value") {
3857
auto shareable = ShareableValue::adapt(rt, newValue, runtimeManager);
3958
runtimeManager->scheduler->scheduleOnUI([this, shareable] {
@@ -46,23 +65,8 @@ void MutableValue::set(jsi::Runtime &rt, const jsi::PropNameID &name, const jsi:
4665
.callWithThis(rt, setterProxy, newValue);
4766
});
4867
}
49-
return;
5068
}
5169

52-
// UI thread
53-
if (propName == "value") {
54-
auto setterProxy = jsi::Object::createFromHostObject(rt, std::make_shared<MutableValueSetterProxy>(shared_from_this()));
55-
runtimeManager->valueSetter->getValue(rt)
56-
.asObject(rt)
57-
.asFunction(rt)
58-
.callWithThis(rt, setterProxy, newValue);
59-
} else if (propName == "_animation") {
60-
// TODO: assert to allow animation to be set from UI only
61-
if (animation.expired()) {
62-
animation = getWeakRef(rt);
63-
}
64-
*animation.lock() = jsi::Value(rt, newValue);
65-
}
6670
}
6771

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

75-
if (RuntimeDecorator::isWorkletRuntime(rt)) {
79+
if (RuntimeDecorator::isUIRuntime(rt)) {
7680
// _value and _animation should be accessed from UI only
7781
if (propName == "_value") {
7882
return getValue(rt);

Common/cpp/SharedItems/ShareableValue.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ jsi::Value ShareableValue::toJSValue(jsi::Runtime &rt) {
327327
auto runtimeManager = this->runtimeManager;
328328
auto& frozenObject = ValueWrapper::asFrozenObject(this->valueContainer);
329329
if (RuntimeDecorator::isWorkletRuntime(rt)) {
330-
// when running on UI thread we prep a function
330+
// when running on worklet thread we prep a function
331331

332332
auto jsThis = std::make_shared<jsi::Object>(frozenObject->shallowClone(*runtimeManager->runtime));
333333
std::shared_ptr<jsi::Function> funPtr(runtimeManager->workletsCache->getFunction(rt, frozenObject));

Common/cpp/Tools/RuntimeDecorator.cpp

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ void RuntimeDecorator::decorateRuntime(jsi::Runtime &rt, std::string label) {
1010
rt.global().setProperty(rt, "_WORKLET", jsi::Value(true));
1111
// This property will be used for debugging
1212
rt.global().setProperty(rt, "_LABEL", jsi::String::createFromAscii(rt, label));
13-
13+
1414
jsi::Object dummyGlobal(rt);
1515
auto dummyFunction = [](
1616
jsi::Runtime &rt,
@@ -21,12 +21,12 @@ void RuntimeDecorator::decorateRuntime(jsi::Runtime &rt, std::string label) {
2121
return jsi::Value::undefined();
2222
};
2323
jsi::Function __reanimatedWorkletInit = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "__reanimatedWorkletInit"), 1, dummyFunction);
24-
24+
2525
dummyGlobal.setProperty(rt, "__reanimatedWorkletInit", __reanimatedWorkletInit);
2626
rt.global().setProperty(rt, "global", dummyGlobal);
27-
27+
2828
rt.global().setProperty(rt, "jsThis", jsi::Value::undefined());
29-
29+
3030
auto callback = [](
3131
jsi::Runtime &rt,
3232
const jsi::Value &thisValue,
@@ -47,7 +47,7 @@ void RuntimeDecorator::decorateRuntime(jsi::Runtime &rt, std::string label) {
4747
};
4848
jsi::Value log = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "_log"), 1, callback);
4949
rt.global().setProperty(rt, "_log", log);
50-
50+
5151
auto setGlobalConsole = [](
5252
jsi::Runtime &rt,
5353
const jsi::Value &thisValue,
@@ -67,7 +67,8 @@ void RuntimeDecorator::decorateUIRuntime(jsi::Runtime &rt,
6767
MeasuringFunction measure,
6868
TimeProviderFunction getCurrentTime) {
6969
RuntimeDecorator::decorateRuntime(rt, "UI");
70-
70+
rt.global().setProperty(rt, "_UI", jsi::Value(true));
71+
7172
auto clb = [updater](
7273
jsi::Runtime &rt,
7374
const jsi::Value &thisValue,
@@ -82,8 +83,8 @@ void RuntimeDecorator::decorateUIRuntime(jsi::Runtime &rt,
8283
};
8384
jsi::Value updateProps = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "_updateProps"), 2, clb);
8485
rt.global().setProperty(rt, "_updateProps", updateProps);
85-
86-
86+
87+
8788
auto clb2 = [requestFrame](
8889
jsi::Runtime &rt,
8990
const jsi::Value &thisValue,
@@ -98,7 +99,7 @@ void RuntimeDecorator::decorateUIRuntime(jsi::Runtime &rt,
9899
};
99100
jsi::Value requestAnimationFrame = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "requestAnimationFrame"), 1, clb2);
100101
rt.global().setProperty(rt, "requestAnimationFrame", requestAnimationFrame);
101-
102+
102103
auto clb3 = [scrollTo](
103104
jsi::Runtime &rt,
104105
const jsi::Value &thisValue,
@@ -114,7 +115,7 @@ void RuntimeDecorator::decorateUIRuntime(jsi::Runtime &rt,
114115
};
115116
jsi::Value scrollToFunction = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "_scrollTo"), 4, clb3);
116117
rt.global().setProperty(rt, "_scrollTo", scrollToFunction);
117-
118+
118119
auto clb4 = [measure](
119120
jsi::Runtime &rt,
120121
const jsi::Value &thisValue,
@@ -131,7 +132,7 @@ void RuntimeDecorator::decorateUIRuntime(jsi::Runtime &rt,
131132
};
132133
jsi::Value measureFunction = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "_measure"), 1, clb4);
133134
rt.global().setProperty(rt, "_measure", measureFunction);
134-
135+
135136
auto clb6 = [getCurrentTime](
136137
jsi::Runtime &rt,
137138
const jsi::Value &thisValue,
@@ -142,16 +143,21 @@ void RuntimeDecorator::decorateUIRuntime(jsi::Runtime &rt,
142143
};
143144
jsi::Value timeFun = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "_getCurrentTime"), 0, clb6);
144145
rt.global().setProperty(rt, "_getCurrentTime", timeFun);
145-
146+
146147
rt.global().setProperty(rt, "_frameTimestamp", jsi::Value::undefined());
147148
rt.global().setProperty(rt, "_eventTimestamp", jsi::Value::undefined());
148149
}
149150

150-
bool RuntimeDecorator::isWorkletRuntime(jsi::Runtime& rt) {
151-
auto isUi = rt.global().getProperty(rt, "_WORKLET");
151+
bool RuntimeDecorator::isUIRuntime(jsi::Runtime& rt) {
152+
auto isUi = rt.global().getProperty(rt, "_UI");
152153
return isUi.isBool() && isUi.getBool();
153154
}
154155

156+
bool RuntimeDecorator::isWorkletRuntime(jsi::Runtime& rt) {
157+
auto isWorklet = rt.global().getProperty(rt, "_WORKLET");
158+
return isWorklet.isBool() && isWorklet.getBool();
159+
}
160+
155161
bool RuntimeDecorator::isReactRuntime(jsi::Runtime& rt) {
156162
return !isWorkletRuntime(rt);
157163
}

Common/cpp/headers/Tools/RuntimeDecorator.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,17 @@ class RuntimeDecorator {
2020
MeasuringFunction measure,
2121
TimeProviderFunction getCurrentTime);
2222

23+
/**
24+
Returns true if the given Runtime is the Reanimated UI-Thread Runtime.
25+
*/
26+
static bool isUIRuntime(jsi::Runtime &rt);
27+
/**
28+
Returns true if the given Runtime is a Runtime that supports Workletization. (REA, Vision, ...)
29+
*/
2330
static bool isWorkletRuntime(jsi::Runtime &rt);
31+
/**
32+
Returns true if the given Runtime is the default React-JS Runtime.
33+
*/
2434
static bool isReactRuntime(jsi::Runtime &rt);
2535
};
2636

0 commit comments

Comments
 (0)