Skip to content

Commit

Permalink
Added disposing/release mechanism to the system
Browse files Browse the repository at this point in the history
To make sure we free any underlying values due to the dreaded hermes GC issue where internally allocated memory is not calcualated when considering garbage collection we need to implement this ourselves.

A shared value is now disposed and all resources allocated should be released. This is done through wrappers.

When deleting argument wrappers we also release any resources.

Fixes #129
  • Loading branch information
chrfalch committed Oct 28, 2023
1 parent 800cf1b commit e66f5d5
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 5 deletions.
2 changes: 1 addition & 1 deletion cpp/WKTJsiWorklet.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ 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) {
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
14 changes: 14 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,18 @@ 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:
void dispose(bool disposed) override { release_wrapped_resources(); }

private:
/**
Creates a proxy for the host object so that we can make the runtime trust
Expand Down
17 changes: 17 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,22 @@ 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:
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
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

0 comments on commit e66f5d5

Please sign in to comment.