Skip to content

feat: Lock Array and Object wrappers for safe multithreading #164

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion cpp/wrappers/WKTJsiArrayWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,19 @@ class JsiArrayWrapper : public JsiHostObject,
return jsi::String::createFromUtf8(runtime, toString(runtime));
}

JSI_PROPERTY_GET(length) { return static_cast<double>(_array.size()); }
JSI_PROPERTY_GET(length) {
std::unique_lock lock(_readWriteMutex);
return static_cast<double>(_array.size());
}

JSI_HOST_FUNCTION(iterator) {
int index = 0;
auto iterator = jsi::Object(runtime);
auto next = [index,
this](jsi::Runtime &runtime, const jsi::Value &thisValue,
const jsi::Value *arguments, size_t count) mutable {
std::unique_lock lock(_readWriteMutex);

auto retVal = jsi::Object(runtime);
if (index < _array.size()) {
retVal.setProperty(runtime, "value", _array[index]->unwrap(runtime));
Expand All @@ -62,6 +67,8 @@ class JsiArrayWrapper : public JsiHostObject,
}

JSI_HOST_FUNCTION(push) {
std::unique_lock lock(_readWriteMutex);

// Push all arguments to the array end
for (size_t i = 0; i < count; i++) {
_array.push_back(JsiWrapper::wrap(runtime, arguments[i], this,
Expand All @@ -72,6 +79,8 @@ class JsiArrayWrapper : public JsiHostObject,
};

JSI_HOST_FUNCTION(unshift) {
std::unique_lock lock(_readWriteMutex);

// Insert all arguments to the array beginning
for (size_t i = 0; i < count; i++) {
_array.insert(_array.begin(),
Expand All @@ -83,6 +92,8 @@ class JsiArrayWrapper : public JsiHostObject,
};

JSI_HOST_FUNCTION(pop) {
std::unique_lock lock(_readWriteMutex);

// Pop last element from array
if (_array.empty()) {
return jsi::Value::undefined();
Expand All @@ -94,6 +105,8 @@ class JsiArrayWrapper : public JsiHostObject,
};

JSI_HOST_FUNCTION(shift) {
std::unique_lock lock(_readWriteMutex);

// Shift first element from array
if (_array.empty()) {
return jsi::Value::undefined();
Expand All @@ -105,6 +118,8 @@ class JsiArrayWrapper : public JsiHostObject,
};

JSI_HOST_FUNCTION(forEach) {
std::unique_lock lock(_readWriteMutex);

auto callbackFn = arguments[0].asObject(runtime).asFunction(runtime);
for (size_t i = 0; i < _array.size(); i++) {
auto arg = _array.at(i)->unwrap(runtime);
Expand All @@ -114,6 +129,8 @@ class JsiArrayWrapper : public JsiHostObject,
};

JSI_HOST_FUNCTION(map) {
std::unique_lock lock(_readWriteMutex);

auto callbackFn = arguments[0].asObject(runtime).asFunction(runtime);
auto result = jsi::Array(runtime, _array.size());
for (size_t i = 0; i < _array.size(); i++) {
Expand All @@ -125,6 +142,8 @@ class JsiArrayWrapper : public JsiHostObject,
};

JSI_HOST_FUNCTION(filter) {
std::unique_lock lock(_readWriteMutex);

auto callbackFn = arguments[0].asObject(runtime).asFunction(runtime);
std::vector<std::shared_ptr<JsiWrapper>> result;

Expand All @@ -143,6 +162,8 @@ class JsiArrayWrapper : public JsiHostObject,
};

JSI_HOST_FUNCTION(find) {
std::unique_lock lock(_readWriteMutex);

auto callbackFn = arguments[0].asObject(runtime).asFunction(runtime);
for (size_t i = 0; i < _array.size(); i++) {
auto arg = _array.at(i)->unwrap(runtime);
Expand All @@ -155,6 +176,8 @@ class JsiArrayWrapper : public JsiHostObject,
};

JSI_HOST_FUNCTION(every) {
std::unique_lock lock(_readWriteMutex);

auto callbackFn = arguments[0].asObject(runtime).asFunction(runtime);
for (size_t i = 0; i < _array.size(); i++) {
auto arg = JsiWrapper::unwrap(runtime, _array.at(i));
Expand All @@ -167,6 +190,8 @@ class JsiArrayWrapper : public JsiHostObject,
};

JSI_HOST_FUNCTION(findIndex) {
std::unique_lock lock(_readWriteMutex);

auto callbackFn = arguments[0].asObject(runtime).asFunction(runtime);
for (size_t i = 0; i < _array.size(); i++) {
auto arg = JsiWrapper::unwrap(runtime, _array.at(i));
Expand All @@ -179,6 +204,8 @@ class JsiArrayWrapper : public JsiHostObject,
};

JSI_HOST_FUNCTION(indexOf) {
std::unique_lock lock(_readWriteMutex);

auto wrappedArg = JsiWrapper::wrap(runtime, arguments[0], nullptr,
getUseProxiesForUnwrapping());
for (size_t i = 0; i < _array.size(); i++) {
Expand Down Expand Up @@ -214,6 +241,8 @@ class JsiArrayWrapper : public JsiHostObject,
}

JSI_HOST_FUNCTION(flat) {
std::unique_lock lock(_readWriteMutex);

auto depth = count > 0 ? arguments[0].asNumber() : -1;
std::vector<std::shared_ptr<JsiWrapper>> result =
flat_internal(depth, _array);
Expand All @@ -226,6 +255,8 @@ class JsiArrayWrapper : public JsiHostObject,
};

JSI_HOST_FUNCTION(includes) {
std::unique_lock lock(_readWriteMutex);

auto wrappedArg = JsiWrapper::wrap(runtime, arguments[0], nullptr,
getUseProxiesForUnwrapping());
for (size_t i = 0; i < _array.size(); i++) {
Expand All @@ -240,6 +271,8 @@ class JsiArrayWrapper : public JsiHostObject,
};

JSI_HOST_FUNCTION(concat) {
std::unique_lock lock(_readWriteMutex);

auto nextArray = arguments[0].asObject(runtime).asArray(runtime);
auto results = jsi::Array(
runtime, static_cast<size_t>(_array.size() + nextArray.size(runtime)));
Expand All @@ -256,6 +289,8 @@ class JsiArrayWrapper : public JsiHostObject,
}

JSI_HOST_FUNCTION(join) {
std::unique_lock lock(_readWriteMutex);

auto separator =
count > 0 ? arguments[0].asString(runtime).utf8(runtime) : ",";
auto result = std::string("");
Expand All @@ -270,6 +305,8 @@ class JsiArrayWrapper : public JsiHostObject,
}

JSI_HOST_FUNCTION(reduce) {
std::unique_lock lock(_readWriteMutex);

auto callbackFn = arguments[0].asObject(runtime).asFunction(runtime);
std::shared_ptr<JsiWrapper> acc =
JsiWrapper::wrap(runtime, jsi::Value::undefined(), nullptr,
Expand Down Expand Up @@ -344,6 +381,8 @@ class JsiArrayWrapper : public JsiHostObject,
* @param value Value to set
*/
void setValue(jsi::Runtime &runtime, const jsi::Value &value) override {
std::unique_lock lock(_readWriteMutex);

assert(value.isObject());
auto object = value.asObject(runtime);
assert(object.isArray(runtime));
Expand All @@ -369,6 +408,8 @@ class JsiArrayWrapper : public JsiHostObject,
auto nameStr = name.utf8(runtime);
if (!nameStr.empty() &&
std::all_of(nameStr.begin(), nameStr.end(), ::isdigit)) {
std::unique_lock lock(_readWriteMutex);

// Return property by index
auto index = std::stoi(nameStr.c_str());
// Ensure we have the required length
Expand Down Expand Up @@ -398,6 +439,8 @@ class JsiArrayWrapper : public JsiHostObject,
auto nameStr = name.utf8(runtime);
if (!nameStr.empty() &&
std::all_of(nameStr.begin(), nameStr.end(), ::isdigit)) {
std::unique_lock lock(_readWriteMutex);

// Return property by index
auto index = std::stoi(nameStr.c_str());
auto prop = _array[index];
Expand All @@ -413,6 +456,8 @@ class JsiArrayWrapper : public JsiHostObject,
* @return Array as string
*/
std::string toString(jsi::Runtime &runtime) override {
std::unique_lock lock(_readWriteMutex);

std::string retVal = "";
// Return array contents
for (size_t i = 0; i < _array.size(); i++) {
Expand All @@ -424,7 +469,10 @@ class JsiArrayWrapper : public JsiHostObject,

std::vector<jsi::PropNameID>
getPropertyNames(jsi::Runtime &runtime) override {
std::unique_lock lock(_readWriteMutex);

std::vector<jsi::PropNameID> propNames;
propNames.reserve(_array.size());
for (size_t i = 0; i < _array.size(); i++) {
propNames.push_back(jsi::PropNameID::forUtf8(runtime, std::to_string(i)));
}
Expand Down
4 changes: 4 additions & 0 deletions cpp/wrappers/WKTJsiObjectWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ class JsiObjectWrapper : public JsiHostObject,
*/
void set(jsi::Runtime &runtime, const jsi::PropNameID &name,
const jsi::Value &value) override {
std::unique_lock lock(_readWriteMutex);

auto nameStr = name.utf8(runtime);
if (_properties.count(nameStr) == 0) {
_properties.emplace(
Expand All @@ -135,6 +137,8 @@ class JsiObjectWrapper : public JsiHostObject,
* @return Property value or undefined.
*/
jsi::Value get(jsi::Runtime &runtime, const jsi::PropNameID &name) override {
std::unique_lock lock(_readWriteMutex);

auto nameStr = name.utf8(runtime);
if (_properties.count(nameStr) != 0) {
auto prop = _properties.at(nameStr);
Expand Down
6 changes: 4 additions & 2 deletions cpp/wrappers/WKTJsiWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ namespace RNWorklet {
namespace jsi = facebook::jsi;

jsi::Value JsiWrapper::getValue(jsi::Runtime &runtime) {
std::unique_lock<std::mutex> lock(_readWriteMutex);
std::unique_lock lock(_readWriteMutex);

switch (_type) {
case JsiWrapperType::Undefined:
return jsi::Value::undefined();
Expand Down Expand Up @@ -79,7 +80,8 @@ void JsiWrapper::setValue(jsi::Runtime &runtime, const jsi::Value &value) {
}

void JsiWrapper::updateValue(jsi::Runtime &runtime, const jsi::Value &value) {
std::unique_lock<std::mutex> lock(_readWriteMutex);
std::unique_lock lock(_readWriteMutex);

setValue(runtime, value);
// Notify changes
notify();
Expand Down
8 changes: 6 additions & 2 deletions cpp/wrappers/WKTJsiWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,12 @@ class JsiWrapper {
return retVal;
}

protected:
/**
* Used for locking calls from multiple runtimes.
*/
std::recursive_mutex _readWriteMutex;

private:
/**
* Notify listeners that the value has changed
Expand All @@ -226,8 +232,6 @@ class JsiWrapper {
}
}

std::mutex _readWriteMutex;

JsiWrapper *_parent;

JsiWrapperType _type;
Expand Down
Loading