diff --git a/cpp/includes/UniffiCallInvoker.h b/cpp/includes/UniffiCallInvoker.h index 520f462d..bdfaf407 100644 --- a/cpp/includes/UniffiCallInvoker.h +++ b/cpp/includes/UniffiCallInvoker.h @@ -6,6 +6,7 @@ #pragma once #include #include +#include #include #include #include @@ -58,26 +59,19 @@ class UniffiCallInvoker { if (std::this_thread::get_id() == threadId_) { func(rt); } else { - std::mutex mtx; - std::condition_variable cv; - bool done = false; + std::promise promise; + auto future = promise.get_future(); // The runtime argument was added to CallFunc in // https://github.com/facebook/react-native/pull/43375 // // This can be changed once that change is released. - // react::CallFunc wrapper = [&func, &mtx, &cv, &done](jsi::Runtime &rt) { - std::function wrapper = [&func, &rt, &mtx, &cv, &done]() { + // react::CallFunc wrapper = [&func, &promise](jsi::Runtime &rt) { + std::function wrapper = [&func, &promise, &rt]() { func(rt); - { - std::lock_guard lock(mtx); - done = true; - } - cv.notify_one(); + promise.set_value(); }; callInvoker_->invokeAsync(std::move(wrapper)); - - std::unique_lock lock(mtx); - cv.wait(lock, [&done] { return done; }); + future.wait(); } } diff --git a/fixtures/callbacks-regression/Cargo.toml b/fixtures/callbacks-regression/Cargo.toml new file mode 100644 index 00000000..0839c845 --- /dev/null +++ b/fixtures/callbacks-regression/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "uniffi-example-callbacks" +edition = "2021" +version = "0.22.0" +license = "MPL-2.0" +publish = false + +[lib] +crate-type = ["lib", "cdylib"] +name = "uniffi_callbacks" + +[dependencies] +uniffi = { workspace = true } +thiserror = "1.0" + +[build-dependencies] +uniffi = { workspace = true, features = ["build"] } + +[dev-dependencies] +uniffi = { workspace = true, features = ["bindgen-tests"] } diff --git a/fixtures/callbacks-regression/README.md b/fixtures/callbacks-regression/README.md new file mode 100644 index 00000000..4c57c1d5 --- /dev/null +++ b/fixtures/callbacks-regression/README.md @@ -0,0 +1,5 @@ +This is an example of UniFFI "callbacks". + +Callbacks are the ability to implement Rust traits in foreign languages. These are defined by +[normal UniFFI traits](../../docs/manual/src/foreign_traits.md) or via ["callback" definitions](../../docs/manual/src/udl/callback_interfaces.md). + diff --git a/fixtures/callbacks-regression/build.rs b/fixtures/callbacks-regression/build.rs new file mode 100644 index 00000000..1ef3bb78 --- /dev/null +++ b/fixtures/callbacks-regression/build.rs @@ -0,0 +1,7 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/ + */ + +fn main() {} diff --git a/fixtures/callbacks-regression/src/lib.rs b/fixtures/callbacks-regression/src/lib.rs new file mode 100644 index 00000000..e2e323e0 --- /dev/null +++ b/fixtures/callbacks-regression/src/lib.rs @@ -0,0 +1,35 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/ + */ + +use std::{sync::Arc, thread::spawn}; + +uniffi::setup_scaffolding!(); + +#[uniffi::export(with_foreign)] +pub trait EventListener: Send + Sync { + fn on_event(&self, message: String, number: i32); +} + +#[derive(uniffi::Object)] +struct EventSource { + listener: Arc, +} + +#[uniffi::export] +impl EventSource { + #[uniffi::constructor] + fn new(listener: Arc) -> Arc { + Arc::new(Self { listener }) + } + fn start(self: Arc, message: String, until: i32) { + let listener = self.listener.clone(); + spawn(move || { + for i in 0..until { + listener.on_event(message.clone(), i); + } + }); + } +} diff --git a/fixtures/callbacks-regression/tests/bindings/test_callbacks_regression.ts b/fixtures/callbacks-regression/tests/bindings/test_callbacks_regression.ts new file mode 100644 index 00000000..30ead8df --- /dev/null +++ b/fixtures/callbacks-regression/tests/bindings/test_callbacks_regression.ts @@ -0,0 +1,53 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/ + */ + +/* +fixture=fixtures/callbacks-regression +cargo xtask run \ + --crate $fixture \ + --ts-dir $fixture/generated \ + --cpp-dir $fixture/generated/cpp \ + $fixture/tests/bindings/test_callbacks_regression.ts +*/ + +import generated, { + EventSource, + EventListener, +} from "../../generated/uniffi_callbacks"; +import { asyncTest, AsyncAsserts } from "@/asserts"; +import { console } from "@/hermes"; + +// This is only needed in tests. +generated.initialize(); + +class EventListenerImpl implements EventListener { + constructor( + private t: AsyncAsserts, + private max: number, + ) {} + onEvent(message: string, number: number): void { + // console.log("--", message, number); + if (number === this.max - 1) { + console.log("-- Done!", this.max); + this.t.end(); + } + } +} + +async function testToMax(max: number, t: AsyncAsserts) { + const listener = new EventListenerImpl(t, max); + const source = new EventSource(listener); + source.start(`Going to ${max}, now at:`, max); +} + +(async () => { + for (let i = 1; i <= 1024; i *= 2) { + await asyncTest( + `Full tilt test up to ${i}`, + async (t) => await testToMax(i, t), + ); + } +})(); diff --git a/fixtures/callbacks-regression/tests/test_generated_bindings.rs b/fixtures/callbacks-regression/tests/test_generated_bindings.rs new file mode 100644 index 00000000..24ffd28d --- /dev/null +++ b/fixtures/callbacks-regression/tests/test_generated_bindings.rs @@ -0,0 +1,5 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/ + */ diff --git a/fixtures/callbacks-regression/uniffi.toml b/fixtures/callbacks-regression/uniffi.toml new file mode 100644 index 00000000..e69de29b