Skip to content
Draft
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
38 changes: 38 additions & 0 deletions apps/benchmark/src/benchmarks/suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,20 @@ function createObjectBenchmarks(
largeBuffer,
'bounce'
),
createBufferBenchmark(
object,
implementation,
'bounce-native-4-kib',
object.copyBuffer(smallBuffer),
'bounce'
),
createBufferBenchmark(
object,
implementation,
'bounce-native-1-mib',
object.copyBuffer(largeBuffer),
'bounce'
),
createBufferBenchmark(
object,
implementation,
Expand Down Expand Up @@ -339,6 +353,30 @@ function createObjectBenchmarks(
return assertNumber(checksum, 'promiseReturnsInstantly')
},
},
{
id: `${prefix}/promise/deferred-worker-with-trigger`,
version: 1,
family: 'promise',
implementation,
kind: 'async',
maxChunkIterations: 5_000,
collectNativeGarbage:
implementation === 'nitro-platform' && Platform.OS === 'android'
? collectJavaGarbage
: undefined,
expectedChecksum: (iterations) => iterations * 55,
async run(iterations) {
let checksum = 0
for (let index = 0; index < iterations; index++) {
const promise = object.createPendingPromise()
// Includes the trigger call: completion cannot race ahead of the
// pending Promise's conversion to JS in createPendingPromise().
object.resolvePendingPromiseOnWorker()
checksum += await promise
}
return assertNumber(checksum, 'resolvePendingPromiseOnWorker')
},
},
]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,22 @@ import com.margelo.nitro.core.NullType
import com.margelo.nitro.core.Promise
import com.margelo.nitro.core.resolved
import com.margelo.nitro.test.external.HybridSomeExternalObjectSpec
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.math.BigDecimal
import java.time.Instant

@Keep
@DoNotStrip
class HybridTestObjectKotlin : HybridTestObjectSwiftKotlinSpec() {
private var pendingPromise: Promise<Double>? = null

private companion object {
val promiseScope = CoroutineScope(Dispatchers.Default)
}

override var numberValue: Double = 0.0
override var boolValue: Boolean = false
override var stringValue: String = ""
Expand Down Expand Up @@ -316,6 +325,22 @@ class HybridTestObjectKotlin : HybridTestObjectSwiftKotlinSpec() {
}
}

override fun createPendingPromise(): Promise<Double> {
check(pendingPromise == null) { "A pending Promise is already waiting for completion." }
val promise = Promise<Double>()
pendingPromise = promise
return promise
}

override fun resolvePendingPromiseOnWorker() {
val promise = checkNotNull(pendingPromise) { "No pending Promise is waiting for completion." }
// Only JS calls access the slot; the worker owns the extracted Promise.
pendingPromise = null
promiseScope.launch {
promise.resolve(55.0)
}
}

override fun promiseThatResolvesVoidInstantly(): Promise<Unit> {
return Promise.resolved()
}
Expand Down
18 changes: 18 additions & 0 deletions packages/react-native-nitro-test/cpp/HybridTestObjectCpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "HybridTestObjectCpp.hpp"
#include <NitroModules/AnyMap.hpp>
#include <NitroModules/NitroLogger.hpp>
#include <NitroModules/ThreadPool.hpp>

Check failure on line 11 in packages/react-native-nitro-test/cpp/HybridTestObjectCpp.cpp

View workflow job for this annotation

GitHub Actions / Build iOS Example App (static_frameworks, static)

'NitroModules/ThreadPool.hpp' file not found
#include <chrono>
#include <sstream>
#include <thread>
Expand Down Expand Up @@ -611,6 +612,23 @@
return Promise<double>::async([=]() { return 55; });
}

std::shared_ptr<Promise<double>> HybridTestObjectCpp::createPendingPromise() {
if (_pendingPromise) {
throw std::runtime_error("A pending Promise is already waiting for completion.");
}
_pendingPromise = Promise<double>::create();
return _pendingPromise;
}

void HybridTestObjectCpp::resolvePendingPromiseOnWorker() {
if (!_pendingPromise) {
throw std::runtime_error("No pending Promise is waiting for completion.");
}
// Only JS calls access the slot; the worker owns the extracted Promise.
auto promise = std::move(_pendingPromise);
ThreadPool::shared().run([promise = std::move(promise)]() { promise->resolve(55); });
}

std::shared_ptr<Promise<void>> HybridTestObjectCpp::promiseThatResolvesVoidInstantly() {
return Promise<void>::resolved();
}
Expand Down
3 changes: 3 additions & 0 deletions packages/react-native-nitro-test/cpp/HybridTestObjectCpp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class HybridTestObjectCpp : public HybridTestObjectCppSpec {
std::optional<std::function<void(double)>> _optionalCallback;
bool _hasBooleanWritable;
bool _isBooleanWritable;
std::shared_ptr<Promise<double>> _pendingPromise;

private:
static inline uint64_t calculateFibonacci(int count) noexcept {
Expand Down Expand Up @@ -205,6 +206,8 @@ class HybridTestObjectCpp : public HybridTestObjectCppSpec {
std::shared_ptr<Promise<void>> promiseThrows() override;
std::shared_ptr<Promise<double>> promiseReturnsInstantly() override;
std::shared_ptr<Promise<double>> promiseReturnsInstantlyAsync() override;
std::shared_ptr<Promise<double>> createPendingPromise() override;
void resolvePendingPromiseOnWorker() override;
std::shared_ptr<Promise<void>> promiseThatResolvesVoidInstantly() override;
std::shared_ptr<Promise<std::optional<double>>> promiseThatResolvesToUndefined() override;
std::shared_ptr<Promise<std::optional<double>>> awaitNullablePromise() override;
Expand Down
23 changes: 23 additions & 0 deletions packages/react-native-nitro-test/ios/HybridTestObjectSwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
// Created by Marc Rousavy on 11.08.24.
//

import Dispatch
import NitroModules
import NitroTestExternal

class HybridTestObjectSwift: HybridTestObjectSwiftKotlinSpec {
private var pendingPromise: Promise<Double>?

var optionalArray: [String]? = []

var someVariant: Variant_Double_String = .first(55)
Expand Down Expand Up @@ -464,6 +467,26 @@ class HybridTestObjectSwift: HybridTestObjectSwiftKotlinSpec {
}
}

func createPendingPromise() throws -> Promise<Double> {
guard pendingPromise == nil else {
throw RuntimeError.error(withMessage: "A pending Promise is already waiting for completion.")
}
let promise = Promise<Double>()
pendingPromise = promise
return promise
}

func resolvePendingPromiseOnWorker() throws {
guard let promise = pendingPromise else {
throw RuntimeError.error(withMessage: "No pending Promise is waiting for completion.")
}
// Only JS calls access the slot; the worker owns the extracted Promise.
pendingPromise = nil
DispatchQueue.global().async {
promise.resolve(withResult: 55.0)
}
}

func promiseThatResolvesVoidInstantly() throws -> Promise<Void> {
return Promise.resolved()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,26 @@ namespace margelo::nitro::test {
return __promise;
}();
}
std::shared_ptr<Promise<double>> JHybridTestObjectSwiftKotlinSpec::createPendingPromise() {
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>()>("createPendingPromise");
auto __result = method(_javaPart);
return [&]() {
auto __promise = Promise<double>::create();
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& __boxedResult) {
auto __result = jni::static_ref_cast<jni::JDouble>(__boxedResult);
__promise->resolve(__result->value());
});
__result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
jni::JniException __jniError(__throwable);
__promise->reject(std::make_exception_ptr(__jniError));
});
return __promise;
}();
}
void JHybridTestObjectSwiftKotlinSpec::resolvePendingPromiseOnWorker() {
static const auto method = _javaPart->javaClassStatic()->getMethod<void()>("resolvePendingPromiseOnWorker");
method(_javaPart);
}
std::shared_ptr<Promise<void>> JHybridTestObjectSwiftKotlinSpec::promiseThatResolvesVoidInstantly() {
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>()>("promiseThatResolvesVoidInstantly");
auto __result = method(_javaPart);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ namespace margelo::nitro::test {
std::shared_ptr<Promise<void>> promiseThrows() override;
std::shared_ptr<Promise<double>> promiseReturnsInstantly() override;
std::shared_ptr<Promise<double>> promiseReturnsInstantlyAsync() override;
std::shared_ptr<Promise<double>> createPendingPromise() override;
void resolvePendingPromiseOnWorker() override;
std::shared_ptr<Promise<void>> promiseThatResolvesVoidInstantly() override;
std::shared_ptr<Promise<std::optional<double>>> promiseThatResolvesToUndefined() override;
std::shared_ptr<Promise<std::optional<double>>> awaitNullablePromise() override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,14 @@ abstract class HybridTestObjectSwiftKotlinSpec: HybridObject() {
@Keep
abstract fun promiseReturnsInstantlyAsync(): Promise<Double>

@DoNotStrip
@Keep
abstract fun createPendingPromise(): Promise<Double>

@DoNotStrip
@Keep
abstract fun resolvePendingPromiseOnWorker(): Unit

@DoNotStrip
@Keep
abstract fun promiseThatResolvesVoidInstantly(): Promise<Unit>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,20 @@ namespace margelo::nitro::test {
auto __value = std::move(__result.value());
return __value;
}
inline std::shared_ptr<Promise<double>> createPendingPromise() override {
auto __result = _swiftPart.createPendingPromise();
if (__result.hasError()) [[unlikely]] {
std::rethrow_exception(__result.error());
}
auto __value = std::move(__result.value());
return __value;
}
inline void resolvePendingPromiseOnWorker() override {
auto __result = _swiftPart.resolvePendingPromiseOnWorker();
if (__result.hasError()) [[unlikely]] {
std::rethrow_exception(__result.error());
}
}
inline std::shared_ptr<Promise<void>> promiseThatResolvesVoidInstantly() override {
auto __result = _swiftPart.promiseThatResolvesVoidInstantly();
if (__result.hasError()) [[unlikely]] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ public protocol HybridTestObjectSwiftKotlinSpec_protocol: HybridObject {
func promiseThrows() throws -> Promise<Void>
func promiseReturnsInstantly() throws -> Promise<Double>
func promiseReturnsInstantlyAsync() throws -> Promise<Double>
func createPendingPromise() throws -> Promise<Double>
func resolvePendingPromiseOnWorker() throws -> Void
func promiseThatResolvesVoidInstantly() throws -> Promise<Void>
func promiseThatResolvesToUndefined() throws -> Promise<Double?>
func awaitNullablePromise() throws -> Promise<Double?>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1564,6 +1564,36 @@ open class HybridTestObjectSwiftKotlinSpec_cxx {
}
}

@inline(__always)
public final func createPendingPromise() -> bridge.Result_std__shared_ptr_Promise_double___ {
do {
let __result = try self.__implementation.createPendingPromise()
let __resultCpp = { () -> bridge.std__shared_ptr_Promise_double__ in
let __promise = bridge.create_std__shared_ptr_Promise_double__()
let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_double__(__promise)
__result
.then({ __result in __promiseHolder.resolve(__result) })
.catch({ __error in __promiseHolder.reject(__error.toCpp()) })
return __promise
}()
return bridge.create_Result_std__shared_ptr_Promise_double___(__resultCpp)
} catch (let __error) {
let __exceptionPtr = __error.toCpp()
return bridge.create_Result_std__shared_ptr_Promise_double___(__exceptionPtr)
}
}

@inline(__always)
public final func resolvePendingPromiseOnWorker() -> bridge.Result_void_ {
do {
try self.__implementation.resolvePendingPromiseOnWorker()
return bridge.create_Result_void_()
} catch (let __error) {
let __exceptionPtr = __error.toCpp()
return bridge.create_Result_void_(__exceptionPtr)
}
}

@inline(__always)
public final func promiseThatResolvesVoidInstantly() -> bridge.Result_std__shared_ptr_Promise_void___ {
do {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ namespace margelo::nitro::test {
prototype.registerHybridMethod("promiseThrows", &HybridTestObjectCppSpec::promiseThrows);
prototype.registerHybridMethod("promiseReturnsInstantly", &HybridTestObjectCppSpec::promiseReturnsInstantly);
prototype.registerHybridMethod("promiseReturnsInstantlyAsync", &HybridTestObjectCppSpec::promiseReturnsInstantlyAsync);
prototype.registerHybridMethod("createPendingPromise", &HybridTestObjectCppSpec::createPendingPromise);
prototype.registerHybridMethod("resolvePendingPromiseOnWorker", &HybridTestObjectCppSpec::resolvePendingPromiseOnWorker);
prototype.registerHybridMethod("promiseThatResolvesVoidInstantly", &HybridTestObjectCppSpec::promiseThatResolvesVoidInstantly);
prototype.registerHybridMethod("promiseThatResolvesToUndefined", &HybridTestObjectCppSpec::promiseThatResolvesToUndefined);
prototype.registerHybridMethod("awaitNullablePromise", &HybridTestObjectCppSpec::awaitNullablePromise);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ namespace margelo::nitro::test {
virtual std::shared_ptr<Promise<void>> promiseThrows() = 0;
virtual std::shared_ptr<Promise<double>> promiseReturnsInstantly() = 0;
virtual std::shared_ptr<Promise<double>> promiseReturnsInstantlyAsync() = 0;
virtual std::shared_ptr<Promise<double>> createPendingPromise() = 0;
virtual void resolvePendingPromiseOnWorker() = 0;
virtual std::shared_ptr<Promise<void>> promiseThatResolvesVoidInstantly() = 0;
virtual std::shared_ptr<Promise<std::optional<double>>> promiseThatResolvesToUndefined() = 0;
virtual std::shared_ptr<Promise<std::optional<double>>> awaitNullablePromise() = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ namespace margelo::nitro::test {
prototype.registerHybridMethod("promiseThrows", &HybridTestObjectSwiftKotlinSpec::promiseThrows);
prototype.registerHybridMethod("promiseReturnsInstantly", &HybridTestObjectSwiftKotlinSpec::promiseReturnsInstantly);
prototype.registerHybridMethod("promiseReturnsInstantlyAsync", &HybridTestObjectSwiftKotlinSpec::promiseReturnsInstantlyAsync);
prototype.registerHybridMethod("createPendingPromise", &HybridTestObjectSwiftKotlinSpec::createPendingPromise);
prototype.registerHybridMethod("resolvePendingPromiseOnWorker", &HybridTestObjectSwiftKotlinSpec::resolvePendingPromiseOnWorker);
prototype.registerHybridMethod("promiseThatResolvesVoidInstantly", &HybridTestObjectSwiftKotlinSpec::promiseThatResolvesVoidInstantly);
prototype.registerHybridMethod("promiseThatResolvesToUndefined", &HybridTestObjectSwiftKotlinSpec::promiseThatResolvesToUndefined);
prototype.registerHybridMethod("awaitNullablePromise", &HybridTestObjectSwiftKotlinSpec::awaitNullablePromise);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ namespace margelo::nitro::test {
virtual std::shared_ptr<Promise<void>> promiseThrows() = 0;
virtual std::shared_ptr<Promise<double>> promiseReturnsInstantly() = 0;
virtual std::shared_ptr<Promise<double>> promiseReturnsInstantlyAsync() = 0;
virtual std::shared_ptr<Promise<double>> createPendingPromise() = 0;
virtual void resolvePendingPromiseOnWorker() = 0;
virtual std::shared_ptr<Promise<void>> promiseThatResolvesVoidInstantly() = 0;
virtual std::shared_ptr<Promise<std::optional<double>>> promiseThatResolvesToUndefined() = 0;
virtual std::shared_ptr<Promise<std::optional<double>>> awaitNullablePromise() = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ interface SharedTestObjectProps {
promiseThrows(): Promise<void>
promiseReturnsInstantly(): Promise<number>
promiseReturnsInstantlyAsync(): Promise<number>
// Stays pending until the separate JS call schedules its native completion.
// Only one Promise may be waiting for that trigger on each TestObject.
createPendingPromise(): Promise<number>
resolvePendingPromiseOnWorker(): void
promiseThatResolvesVoidInstantly(): Promise<void>
promiseThatResolvesToUndefined(): Promise<number | undefined>
awaitNullablePromise(): Promise<number | undefined>
Expand Down
4 changes: 4 additions & 0 deletions scripts/performance/report-markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ const OPERATION_NAMES: Readonly<Record<string, string>> = {
'ascii-short': 'short ASCII string',
'bounce-1-mib': 'bounce(1 MiB)',
'bounce-4-kib': 'bounce(4 KiB)',
'bounce-native-4-kib': 'bounce native-owned buffer (4 KiB)',
'bounce-native-1-mib': 'bounce native-owned buffer (1 MiB)',
'deferred-worker-with-trigger':
'deferred worker Promise (includes trigger call)',
'copy-1-mib': 'copy(1 MiB)',
'copy-4-kib': 'copy(4 KiB)',
'create': 'create()',
Expand Down
Loading