Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
6 changes: 3 additions & 3 deletions cpp/WKTJsRuntimeFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
// Hermes
#include <hermes/hermes.h>
#elif __has_include(<React-jsc/JSCRuntime.h>)
// JSC
#include <React-jsc/JSCRuntime.h>
// JSC
#include <React-jsc/JSCRuntime.h>
#else
#include <jsc/JSCRuntime.h>
#include <jsc/JSCRuntime.h>
#endif

namespace RNWorklet {
Expand Down
28 changes: 23 additions & 5 deletions cpp/WKTJsiWorklet.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ class JsiWorklet : public JsiHostObject,
JsiWorklet(jsi::Runtime &runtime, std::shared_ptr<jsi::Function> func) {
createWorklet(runtime, func);
}

~JsiWorklet() {
JsiHostObject::dispose();
}

JSI_HOST_FUNCTION(isWorklet) { return isWorklet(); }

Expand Down Expand Up @@ -233,6 +237,15 @@ class JsiWorklet : public JsiHostObject,
}
}

protected:
void dispose(bool disposed) override {
if (!disposed) {
if (_closureWrapper != nullptr) {
_closureWrapper->release_wrapped_resources();
}
}
}

private:
/**
Installs the worklet function into the worklet runtime
Expand Down Expand Up @@ -301,14 +314,19 @@ class JsiWorklet : public JsiHostObject,
.asString(runtime)
.utf8(runtime);
}

// Double-check if the code property is valid.
bool isCodeEmpty = std::all_of(_code.begin(), _code.end(), std::isspace);
if (isCodeEmpty) {
std::string error = "Failed to create Worklet, the provided code is empty. Tips:\n"
"* Is the babel plugin correctly installed?\n"
"* If you are using react-native-reanimated, make sure the react-native-reanimated plugin does not override the react-native-worklets-core/plugin.\n"
"* Make sure the JS Worklet contains a \"" + std::string(PropNameWorkletInitDataCode) + "\" property with the function's code.";
std::string error =
"Failed to create Worklet, the provided code is empty. Tips:\n"
"* Is the babel plugin correctly installed?\n"
"* If you are using react-native-reanimated, make sure the "
"react-native-reanimated plugin does not override the "
"react-native-worklets-core/plugin.\n"
"* Make sure the JS Worklet contains a \"" +
std::string(PropNameWorkletInitDataCode) +
"\" property with the function's code.";
throw jsi::JSError(runtime, error);
}

Expand Down
7 changes: 4 additions & 3 deletions cpp/WKTJsiWorkletApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,10 @@ class JsiWorkletApi : public JsiHostObject {

JSI_PROPERTY_GET(currentContext) {
auto current = JsiWorkletContext::getCurrent(runtime);
if (!current) return jsi::Value::undefined();
return jsi::Object::createFromHostObject(
runtime, current->shared_from_this());
if (!current)
return jsi::Value::undefined();
return jsi::Object::createFromHostObject(runtime,
current->shared_from_this());
}

JSI_EXPORT_PROPERTY_GETTERS(JSI_EXPORT_PROP_GET(JsiWorkletApi,
Expand Down
7 changes: 7 additions & 0 deletions cpp/base/WKTJsiHostObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ JsiHostObject::~JsiHostObject() {
#endif
}

void JsiHostObject::dispose() {
if (!_disposed) {
dispose(_disposed);
_disposed = true;
}
}

void JsiHostObject::set(jsi::Runtime &rt, const jsi::PropNameID &name,
const jsi::Value &value) {

Expand Down
11 changes: 11 additions & 0 deletions cpp/base/WKTJsiHostObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ class JsiHostObject : public jsi::HostObject {
JsiHostObject();
~JsiHostObject();

/**
Disposes and releases all used resources
*/
void dispose();

protected:
/**
Override to return map of name/functions
Expand Down Expand Up @@ -194,7 +199,13 @@ class JsiHostObject : public jsi::HostObject {
*/
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &runtime) override;

/**
Override to dispose
*/
virtual void dispose(bool disposed) {}

private:
std::map<void *, std::map<std::string, jsi::Function>> _hostFunctionCache;
std::atomic<bool> _disposed = {false};
};
} // namespace RNWorklet
13 changes: 13 additions & 0 deletions cpp/sharedvalues/WKTJsiSharedValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ class JsiSharedValue : public JsiHostObject {
*/
~JsiSharedValue() { _valueWrapper = nullptr; }

JSI_HOST_FUNCTION(dispose) {
JsiHostObject::dispose();
return jsi::Value::undefined();
}

JSI_HOST_FUNCTION(toString) {
return jsi::String::createFromUtf8(runtime,
_valueWrapper->toString(runtime));
Expand Down Expand Up @@ -100,6 +105,7 @@ class JsiSharedValue : public JsiHostObject {
}

JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiSharedValue, toString),
JSI_EXPORT_FUNC(JsiSharedValue, dispose),
JSI_EXPORT_FUNC(JsiSharedValue, addListener))

JSI_EXPORT_PROPERTY_GETTERS(JSI_EXPORT_PROP_GET(JsiSharedValue, value))
Expand All @@ -122,6 +128,13 @@ class JsiSharedValue : public JsiHostObject {
_valueWrapper->removeListener(listenerId);
}

protected:
void dispose(bool disposed) override {
if (!disposed) {
_valueWrapper->release_wrapped_resources();
}
}

private:
std::shared_ptr<JsiWrapper> _valueWrapper;
std::shared_ptr<JsiWorkletContext> _context;
Expand Down
6 changes: 6 additions & 0 deletions cpp/wrappers/WKTArgumentsWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ class ArgumentsWrapper {
}
}

~ArgumentsWrapper() {
for (size_t i = 0; i < _arguments.size(); i++) {
_arguments[i]->release_wrapped_resources();
}
}

size_t getCount() const { return _count; }

std::vector<jsi::Value> getArguments(jsi::Runtime &runtime) const {
Expand Down
19 changes: 19 additions & 0 deletions cpp/wrappers/WKTJsiArrayWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class JsiArrayWrapper : public JsiHostObject,
JsiWrapper *parent)
: JsiWrapper(runtime, value, parent, JsiWrapperType::Array) {}

~JsiArrayWrapper() { JsiHostObject::dispose(); }

JSI_HOST_FUNCTION(toStringImpl) {
return jsi::String::createFromUtf8(runtime, toString(runtime));
}
Expand Down Expand Up @@ -413,6 +415,23 @@ class JsiArrayWrapper : public JsiHostObject,

const std::vector<std::shared_ptr<JsiWrapper>> &getArray() { return _array; }

/**
Overridden dispose - release our array resources!
*/
void release_wrapped_resources() override {
for (size_t i = 0; i < _array.size(); i++) {
_array[i]->release_wrapped_resources();
}
}

protected:
// Release resources when the owning JS engine calls dispose on this object
void dispose(bool disposed) override {
if (!disposed) {
release_wrapped_resources();
}
}

private:
/**
Creates a proxy for the host object so that we can make the runtime trust
Expand Down
18 changes: 18 additions & 0 deletions cpp/wrappers/WKTJsiObjectWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class JsiObjectWrapper : public JsiHostObject,
JsiWrapper *parent)
: JsiWrapper(runtime, value, parent) {}

~JsiObjectWrapper() { JsiHostObject::dispose(); }

JSI_HOST_FUNCTION(toStringImpl) {
return jsi::String::createFromUtf8(runtime, toString(runtime));
}
Expand Down Expand Up @@ -156,7 +158,23 @@ class JsiObjectWrapper : public JsiHostObject,
}
}

/**
Overridden dispose - release our array resources!
*/
void release_wrapped_resources() override {
for (auto it = _properties.begin(); it != _properties.end(); it++) {
it->second->release_wrapped_resources();
}
}

protected:
// Release resources when the owning JS engine calls dispose on this object
void dispose(bool disposed) override {
if (!disposed) {
release_wrapped_resources();
}
}

jsi::Value getAsProxyOrValue(jsi::Runtime &runtime) override {
if (getType() == JsiWrapperType::Object) {
return getObjectAsProxy(runtime, shared_from_this());
Expand Down
21 changes: 20 additions & 1 deletion cpp/wrappers/WKTJsiPromiseWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,20 @@ class JsiPromiseWrapper

explicit JsiPromiseWrapper(jsi::Runtime &runtime);

~JsiPromiseWrapper() {}
~JsiPromiseWrapper() { JsiHostObject::dispose(); }

/**
Overridden dispose - release our array resources!
*/
void release_wrapped_resources() override {
if (_reason != nullptr) {
_reason->release_wrapped_resources();
}
if (_value != nullptr) {
_value->release_wrapped_resources();
}
}

/**
Returns true if the object is a thenable object - ie. an object with a then
function. Which is basically what a promise is.
Expand Down Expand Up @@ -138,6 +151,12 @@ class JsiPromiseWrapper
}

protected:
void dispose(bool disposed) override {
if (!disposed) {
release_wrapped_resources();
}
}

jsi::Value then(jsi::Runtime &runtime, const jsi::Value &thisValue,
const jsi::Value *thenFn, const jsi::Value *catchFn);

Expand Down
10 changes: 7 additions & 3 deletions cpp/wrappers/WKTJsiWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ class JsiWrapper {
*/
void removeListener(size_t listenerId) { _listeners.erase(listenerId); }

/**
Override to ensure releasing resources correctly
*/
virtual void release_wrapped_resources() {}

protected:
/**
* Returns a wrapper for the value
Expand Down Expand Up @@ -185,7 +190,6 @@ class JsiWrapper {
const jsi::Value &thisValue,
const jsi::Value *arguments, size_t count);

protected:
/**
* Sets the value from a JS value
* @param runtime runtime for the value
Expand Down Expand Up @@ -253,10 +257,10 @@ class JsiWrapper {
* @param parent Parent wrapper
*/
explicit JsiWrapper(JsiWrapper *parent) : _parent(parent) {
_readWriteMutex = new std::mutex();
_readWriteMutex = std::make_shared<std::mutex>();
}

std::mutex *_readWriteMutex;
std::shared_ptr<std::mutex> _readWriteMutex;
JsiWrapper *_parent;

JsiWrapperType _type;
Expand Down
7 changes: 6 additions & 1 deletion src/hooks/useSharedValue.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useRef } from "react";
import { useEffect, useRef } from "react";
import type { ISharedValue } from "../types";

/**
Expand All @@ -11,5 +11,10 @@ export function useSharedValue<T>(initialValue: T): ISharedValue<T> {
if (ref.current == null) {
ref.current = Worklets.createSharedValue(initialValue);
}
// Free on unmount
useEffect(() => {
return ref.current?.dispose();
}, []);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will immediately free the shared value, I think we only want to run this in the cleanup function of the useEffect hook, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely - this looks like the exact reason for having great people doing code reviews! Thanks!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed now.


return ref.current;
}
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export interface ISharedValue<T> {
get value(): T;
set value(v: T);
addListener(listener: () => void): () => void;
dispose: () => void;
}

export interface IWorklet {
Expand Down