Skip to content
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

Optimize the way we extract code string and in which we pass it across runtimes #3930

Merged
merged 6 commits into from
Jan 17, 2023
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
20 changes: 15 additions & 5 deletions Common/cpp/NativeModules/NativeReanimatedModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ NativeReanimatedModule::NativeReanimatedModule(
};

auto makeShareableClone = [this](jsi::Runtime &rt, const jsi::Value &value) {
return this->makeShareableClone(rt, value);
return this->makeShareableClone(rt, value, jsi::Value::undefined());
};

auto updateDataSynchronously =
Expand Down Expand Up @@ -246,7 +246,8 @@ jsi::Value NativeReanimatedModule::getDataSynchronously(

jsi::Value NativeReanimatedModule::makeShareableClone(
jsi::Runtime &rt,
const jsi::Value &value) {
const jsi::Value &value,
const jsi::Value &shouldRetainRemote) {
std::shared_ptr<Shareable> shareable;
if (value.isObject()) {
auto object = value.asObject(rt);
Expand All @@ -258,15 +259,24 @@ jsi::Value NativeReanimatedModule::makeShareableClone(
shareable = std::make_shared<ShareableRemoteFunction>(
runtimeHelper, rt, object.asFunction(rt));
} else if (object.isArray(rt)) {
shareable = std::make_shared<ShareableArray>(
runtimeHelper, rt, object.asArray(rt));
if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) {
shareable = std::make_shared<RetainingShareable<ShareableArray>>(
runtimeHelper, rt, object.asArray(rt));
} else {
shareable = std::make_shared<ShareableArray>(rt, object.asArray(rt));
}
#ifdef RCT_NEW_ARCH_ENABLED
} else if (object.isHostObject<ShadowNodeWrapper>(rt)) {
shareable = std::make_shared<ShareableShadowNodeWrapper>(
runtimeHelper, rt, object);
#endif
} else {
shareable = std::make_shared<ShareableObject>(runtimeHelper, rt, object);
if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) {
shareable = std::make_shared<RetainingShareable<ShareableObject>>(
runtimeHelper, rt, object);
} else {
shareable = std::make_shared<ShareableObject>(rt, object);
}
}
} else if (value.isString()) {
shareable = std::make_shared<ShareableString>(rt, value.asString(rt));
Expand Down
6 changes: 4 additions & 2 deletions Common/cpp/NativeModules/NativeReanimatedModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec,
const jsi::Value &callGuard,
const jsi::Value &valueUnpacker) override;

jsi::Value makeShareableClone(jsi::Runtime &rt, const jsi::Value &value)
override;
jsi::Value makeShareableClone(
jsi::Runtime &rt,
const jsi::Value &value,
const jsi::Value &shouldRetainRemote) override;

jsi::Value makeSynchronizedDataHolder(
jsi::Runtime &rt,
Expand Down
4 changes: 2 additions & 2 deletions Common/cpp/NativeModules/NativeReanimatedModuleSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ static jsi::Value SPEC_PREFIX(makeShareableClone)(
const jsi::Value *args,
size_t count) {
return static_cast<NativeReanimatedModuleSpec *>(&turboModule)
->makeShareableClone(rt, std::move(args[0]));
->makeShareableClone(rt, std::move(args[0]), std::move(args[1]));
}

// Sync methods
Expand Down Expand Up @@ -165,7 +165,7 @@ NativeReanimatedModuleSpec::NativeReanimatedModuleSpec(
MethodMetadata{2, SPEC_PREFIX(installCoreFunctions)};

methodMap_["makeShareableClone"] =
MethodMetadata{1, SPEC_PREFIX(makeShareableClone)};
MethodMetadata{2, SPEC_PREFIX(makeShareableClone)};

methodMap_["makeSynchronizedDataHolder"] =
MethodMetadata{1, SPEC_PREFIX(makeSynchronizedDataHolder)};
Expand Down
3 changes: 2 additions & 1 deletion Common/cpp/NativeModules/NativeReanimatedModuleSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class JSI_EXPORT NativeReanimatedModuleSpec : public TurboModule {
// SharedValue
virtual jsi::Value makeShareableClone(
jsi::Runtime &rt,
const jsi::Value &value) = 0;
const jsi::Value &value,
const jsi::Value &shouldRetainRemote) = 0;

// Synchronized data objects
virtual jsi::Value makeSynchronizedDataHolder(
Expand Down
70 changes: 8 additions & 62 deletions Common/cpp/SharedItems/Shareables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ CoreFunction::CoreFunction(
jsi::Runtime &rt = *runtimeHelper->rnRuntime();
auto workletObject = workletValue.asObject(rt);
rnFunction_ = std::make_unique<jsi::Function>(workletObject.asFunction(rt));
functionBody_ =
workletObject.getProperty(rt, "asString").asString(rt).utf8(rt);
functionBody_ = workletObject.getPropertyAsObject(rt, "__initData")
.getProperty(rt, "code")
.asString(rt)
.utf8(rt);
location_ = "worklet_" +
std::to_string(static_cast<unsigned long long>(
workletObject.getProperty(rt, "__workletHash").getNumber()));
Expand Down Expand Up @@ -59,73 +61,17 @@ std::shared_ptr<Shareable> extractShareableOrThrow(

Shareable::~Shareable() {}

#if HAS_JS_WEAK_OBJECTS

jsi::Value RetainingShareable::getJSValue(jsi::Runtime &rt) {
jsi::Value value;
if (runtimeHelper_->isRNRuntime(rt)) {
// TODO: it is suboptimal to generate new object every time getJS is called
// on host runtime – the objects we are generating already exists and we
// should possibly just grab a hold of such object and use it here instead
// of creating a new JS representation. As far as I understand the only
// case where it can be realistically called this way is when a shared
// value is created and then accessed on the same runtime
return toJSValue(rt);
} else if (remoteValue_ == nullptr) {
value = toJSValue(rt);
remoteValue_ = std::make_unique<jsi::WeakObject>(rt, value.asObject(rt));
} else {
value = remoteValue_->lock(rt);
if (value.isUndefined()) {
value = toJSValue(rt);
remoteValue_ = std::make_unique<jsi::WeakObject>(rt, value.asObject(rt));
}
}
return value;
}

RetainingShareable::~RetainingShareable() {
if (runtimeHelper_->uiRuntimeDestroyed) {
// The below use of unique_ptr.release prevents the smart pointer from
// calling the destructor of the kept object. This effectively results in
// leaking some memory. We do this on purpose, as sometimes we would keep
// references to JSI objects past the lifetime of its runtime (e.g., shared
// values references from the RN VM holds reference to JSI objects on the
// UI runtime). When the UI runtime is terminated, the orphaned JSI objects
// would crash the app when their destructors are called, because they
// call into a memory that's managed by the terminated runtime. We accept
// the tradeoff of leaking memory here, as it has a limited impact. This
// scenario can only occur when the React instance is torn down which
// happens in development mode during app reloads, or in production when the
// app is being shut down gracefully by the system. An alternative solution
// would require us to keep track of all JSI values that are in use which
// would require additional data structure and compute spent on bookkeeping
// that only for the sake of destroying the values in time before the
// runtime is terminated. Note that the underlying memory that jsi::Value
// refers to is managed by the VM and gets freed along with the runtime.
remoteValue_.release();
}
}

#endif // HAS_JS_WEAK_OBJECTS

ShareableArray::ShareableArray(
const std::shared_ptr<JSRuntimeHelper> &runtimeHelper,
jsi::Runtime &rt,
const jsi::Array &array)
: RetainingShareable(runtimeHelper, rt, ArrayType) {
ShareableArray::ShareableArray(jsi::Runtime &rt, const jsi::Array &array)
: Shareable(ArrayType) {
auto size = array.size(rt);
data_.reserve(size);
for (size_t i = 0; i < size; i++) {
data_.push_back(extractShareableOrThrow(rt, array.getValueAtIndex(rt, i)));
}
}

ShareableObject::ShareableObject(
const std::shared_ptr<JSRuntimeHelper> &runtimeHelper,
jsi::Runtime &rt,
const jsi::Object &object)
: RetainingShareable(runtimeHelper, rt, ObjectType) {
ShareableObject::ShareableObject(jsi::Runtime &rt, const jsi::Object &object)
: Shareable(ObjectType) {
auto propertyNames = object.getPropertyNames(rt);
auto size = propertyNames.size(rt);
data_.reserve(size);
Expand Down
99 changes: 60 additions & 39 deletions Common/cpp/SharedItems/Shareables.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,34 +141,57 @@ class Shareable {
ValueType valueType_;
};

class RetainingShareable : public Shareable {
protected:
std::shared_ptr<JSRuntimeHelper> runtimeHelper_;

#if HAS_JS_WEAK_OBJECTS

template <typename BaseClass>
class RetainingShareable : virtual public BaseClass {
private:
std::unique_ptr<jsi::WeakObject> remoteValue_;

public:
jsi::Value getJSValue(jsi::Runtime &rt) final;

RetainingShareable(
const std::shared_ptr<JSRuntimeHelper> &runtimeHelper,
jsi::Runtime &rt,
ValueType valueType)
: Shareable(valueType), runtimeHelper_(runtimeHelper) {}

~RetainingShareable();
#else
std::shared_ptr<JSRuntimeHelper> runtimeHelper_;
std::unique_ptr<jsi::Value> remoteValue_;

public:
template <typename... Args>
RetainingShareable(
const std::shared_ptr<JSRuntimeHelper> &runtimeHelper,
jsi::Runtime &rt,
ValueType valueType)
: Shareable(valueType), runtimeHelper_(runtimeHelper) {}
#endif
Args &&...args)
: BaseClass(std::forward<Args>(args)...), runtimeHelper_(runtimeHelper) {}
jsi::Value getJSValue(jsi::Runtime &rt) {
if (runtimeHelper_->isRNRuntime(rt)) {
// TODO: it is suboptimal to generate new object every time getJS is
// called on host runtime – the objects we are generating already exists
// and we should possibly just grab a hold of such object and use it here
// instead of creating a new JS representation. As far as I understand the
// only case where it can be realistically called this way is when a
// shared value is created and then accessed on the same runtime
return BaseClass::toJSValue(rt);
} else if (remoteValue_ == nullptr) {
auto value = BaseClass::toJSValue(rt);
remoteValue_ = std::make_unique<jsi::Value>(rt, value);
return value;
}
return jsi::Value(rt, *remoteValue_);
}
~RetainingShareable() {
if (runtimeHelper_->uiRuntimeDestroyed) {
// The below use of unique_ptr.release prevents the smart pointer from
// calling the destructor of the kept object. This effectively results in
// leaking some memory. We do this on purpose, as sometimes we would keep
// references to JSI objects past the lifetime of its runtime (e.g.,
// shared values references from the RN VM holds reference to JSI objects
// on the UI runtime). When the UI runtime is terminated, the orphaned JSI
// objects would crash the app when their destructors are called, because
// they call into a memory that's managed by the terminated runtime. We
// accept the tradeoff of leaking memory here, as it has a limited impact.
// This scenario can only occur when the React instance is torn down which
// happens in development mode during app reloads, or in production when
// the app is being shut down gracefully by the system. An alternative
// solution would require us to keep track of all JSI values that are in
// use which would require additional data structure and compute spent on
// bookkeeping that only for the sake of destroying the values in time
// before the runtime is terminated. Note that the underlying memory that
// jsi::Value refers to is managed by the VM and gets freed along with the
// runtime.
remoteValue_.release();
}
}
};

class ShareableJSRef : public jsi::HostObject {
Expand Down Expand Up @@ -210,12 +233,9 @@ std::shared_ptr<T> extractShareableOrThrow(
return res;
}

class ShareableArray : public RetainingShareable {
class ShareableArray : public Shareable {
public:
ShareableArray(
const std::shared_ptr<JSRuntimeHelper> &runtimeHelper,
jsi::Runtime &rt,
const jsi::Array &array);
ShareableArray(jsi::Runtime &rt, const jsi::Array &array);

jsi::Value toJSValue(jsi::Runtime &rt) override {
auto size = data_.size();
Expand All @@ -230,12 +250,9 @@ class ShareableArray : public RetainingShareable {
std::vector<std::shared_ptr<Shareable>> data_;
};

class ShareableObject : public RetainingShareable {
class ShareableObject : public Shareable {
public:
ShareableObject(
const std::shared_ptr<JSRuntimeHelper> &runtimeHelper,
jsi::Runtime &rt,
const jsi::Object &object);
ShareableObject(jsi::Runtime &rt, const jsi::Object &object);
jsi::Value toJSValue(jsi::Runtime &rt) override {
auto obj = jsi::Object(rt);
for (size_t i = 0, size = data_.size(); i < size; i++) {
Expand Down Expand Up @@ -271,12 +288,15 @@ class ShareableShadowNodeWrapper : public Shareable {
#endif

class ShareableWorklet : public ShareableObject {
private:
std::shared_ptr<JSRuntimeHelper> runtimeHelper_;

public:
ShareableWorklet(
const std::shared_ptr<JSRuntimeHelper> &runtimeHelper,
jsi::Runtime &rt,
const jsi::Object &worklet)
: ShareableObject(runtimeHelper, rt, worklet) {
: ShareableObject(rt, worklet), runtimeHelper_(runtimeHelper) {
valueType_ = WorkletType;
}
jsi::Value toJSValue(jsi::Runtime &rt) override {
Expand All @@ -286,18 +306,20 @@ class ShareableWorklet : public ShareableObject {
};

class ShareableRemoteFunction
: public RetainingShareable,
: public Shareable,
public std::enable_shared_from_this<ShareableRemoteFunction> {
private:
jsi::Function function_;
std::shared_ptr<JSRuntimeHelper> runtimeHelper_;

public:
ShareableRemoteFunction(
const std::shared_ptr<JSRuntimeHelper> &runtimeHelper,
jsi::Runtime &rt,
jsi::Function &&function)
: RetainingShareable(runtimeHelper, rt, RemoteFunctionType),
function_(std::move(function)) {}
: Shareable(RemoteFunctionType),
function_(std::move(function)),
runtimeHelper_(runtimeHelper) {}
jsi::Value toJSValue(jsi::Runtime &rt) override {
if (runtimeHelper_->isUIRuntime(rt)) {
#ifdef DEBUG
Expand Down Expand Up @@ -326,8 +348,7 @@ class ShareableHandle : public Shareable {
jsi::Runtime &rt,
const jsi::Object &initializerObject)
: Shareable(HandleType), runtimeHelper_(runtimeHelper) {
initializer_ =
std::make_unique<ShareableObject>(runtimeHelper, rt, initializerObject);
initializer_ = std::make_unique<ShareableObject>(rt, initializerObject);
}
~ShareableHandle() {
if (runtimeHelper_->uiRuntimeDestroyed) {
Expand Down
Loading