From a3225e5cd7300152e001ea2225bb06a5432ef483 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Thu, 5 Jun 2025 02:07:01 -0400 Subject: [PATCH] feat(wasm): Wasm equivalent of get_runtime_handle and corresponding tokio types (#5089) Adds a Wasm equivalent of the get_runtime_handle method provided by tokio, as well as Handle/Runtime types that can be used on either Wasm or non-Wasm platforms interchangeably. Dependent on https://github.com/matrix-org/matrix-rust-sdk/pull/5088 - [ ] Public API changes documented in changelogs (optional) Signed-off-by: Daniel Salinas --------- Signed-off-by: Daniel Salinas Co-authored-by: Daniel Salinas Co-authored-by: Daniel Salinas Co-authored-by: Jonas Platte Co-authored-by: Ivan Enderlin --- Cargo.lock | 2 + Cargo.toml | 1 + bindings/matrix-sdk-ffi/src/client.rs | 5 +- bindings/matrix-sdk-ffi/src/client_builder.rs | 3 +- bindings/matrix-sdk-ffi/src/encryption.rs | 3 +- bindings/matrix-sdk-ffi/src/room.rs | 3 +- .../src/room_directory_search.rs | 3 +- bindings/matrix-sdk-ffi/src/room_list.rs | 3 +- .../src/session_verification.rs | 3 +- bindings/matrix-sdk-ffi/src/sync_service.rs | 3 +- bindings/matrix-sdk-ffi/src/timeline/mod.rs | 2 +- bindings/matrix-sdk-ffi/src/utils.rs | 2 +- bindings/matrix-sdk-ffi/src/widget.rs | 3 +- crates/matrix-sdk-common/Cargo.toml | 4 +- crates/matrix-sdk-common/src/lib.rs | 1 + crates/matrix-sdk-common/src/runtime.rs | 110 ++++++++++++++++++ 16 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 crates/matrix-sdk-common/src/runtime.rs diff --git a/Cargo.lock b/Cargo.lock index 514f067c4..b91c72b4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2980,8 +2980,10 @@ version = "0.11.0" dependencies = [ "assert_matches", "assert_matches2", + "async-compat", "eyeball-im", "futures-core", + "futures-executor", "futures-util", "getrandom 0.2.15", "gloo-timers", diff --git a/Cargo.toml b/Cargo.toml index 7cfd3828b..feaae18c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ as_variant = "1.3.0" assert-json-diff = "2.0.2" assert_matches = "1.5.0" assert_matches2 = "0.1.2" +async-compat = "0.2.4" async-rx = "0.1.3" async-stream = "0.3.5" async-trait = "0.1.85" diff --git a/bindings/matrix-sdk-ffi/src/client.rs b/bindings/matrix-sdk-ffi/src/client.rs index 93bbb3164..557ec061a 100644 --- a/bindings/matrix-sdk-ffi/src/client.rs +++ b/bindings/matrix-sdk-ffi/src/client.rs @@ -7,7 +7,6 @@ use std::{ }; use anyhow::{anyhow, Context as _}; -use async_compat::get_runtime_handle; use futures_util::pin_mut; use matrix_sdk::{ authentication::oauth::{ @@ -41,7 +40,9 @@ use matrix_sdk::{ AuthApi, AuthSession, Client as MatrixClient, SessionChange, SessionTokens, STATE_STORE_DATABASE_NAME, }; -use matrix_sdk_common::{stream::StreamExt, SendOutsideWasm, SyncOutsideWasm}; +use matrix_sdk_common::{ + runtime::get_runtime_handle, stream::StreamExt, SendOutsideWasm, SyncOutsideWasm, +}; use matrix_sdk_ui::{ notification_client::{ NotificationClient as MatrixNotificationClient, diff --git a/bindings/matrix-sdk-ffi/src/client_builder.rs b/bindings/matrix-sdk-ffi/src/client_builder.rs index ffff170af..1bc37176b 100644 --- a/bindings/matrix-sdk-ffi/src/client_builder.rs +++ b/bindings/matrix-sdk-ffi/src/client_builder.rs @@ -1,6 +1,5 @@ use std::{fs, num::NonZeroUsize, path::Path, sync::Arc, time::Duration}; -use async_compat::get_runtime_handle; use futures_util::StreamExt; use matrix_sdk::{ authentication::oauth::qrcode::{self, DeviceCodeErrorResponseType, LoginFailureReason}, @@ -19,7 +18,7 @@ use matrix_sdk::{ Client as MatrixClient, ClientBuildError as MatrixClientBuildError, HttpError, IdParseError, RumaApiError, SqliteStoreConfig, }; -use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm}; +use matrix_sdk_common::{runtime::get_runtime_handle, SendOutsideWasm, SyncOutsideWasm}; use ruma::api::error::{DeserializationError, FromHttpResponseError}; use tracing::{debug, error}; use zeroize::Zeroizing; diff --git a/bindings/matrix-sdk-ffi/src/encryption.rs b/bindings/matrix-sdk-ffi/src/encryption.rs index 94759be59..640259603 100644 --- a/bindings/matrix-sdk-ffi/src/encryption.rs +++ b/bindings/matrix-sdk-ffi/src/encryption.rs @@ -1,12 +1,11 @@ use std::sync::Arc; -use async_compat::get_runtime_handle; use futures_util::StreamExt; use matrix_sdk::{ encryption, encryption::{backups, recovery}, }; -use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm}; +use matrix_sdk_common::{runtime::get_runtime_handle, SendOutsideWasm, SyncOutsideWasm}; use thiserror::Error; use tracing::{error, info}; use zeroize::Zeroize; diff --git a/bindings/matrix-sdk-ffi/src/room.rs b/bindings/matrix-sdk-ffi/src/room.rs index 146d69404..d5345b0f9 100644 --- a/bindings/matrix-sdk-ffi/src/room.rs +++ b/bindings/matrix-sdk-ffi/src/room.rs @@ -1,7 +1,6 @@ use std::{collections::HashMap, pin::pin, sync::Arc}; use anyhow::{Context, Result}; -use async_compat::get_runtime_handle; use futures_util::{pin_mut, StreamExt}; use matrix_sdk::{ crypto::LocalTrust, @@ -13,7 +12,7 @@ use matrix_sdk::{ PredecessorRoom as SdkPredecessorRoom, RoomHero as SdkRoomHero, RoomMemberships, RoomState, SuccessorRoom as SdkSuccessorRoom, }; -use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm}; +use matrix_sdk_common::{runtime::get_runtime_handle, SendOutsideWasm, SyncOutsideWasm}; use matrix_sdk_ui::{ timeline::{default_event_filter, RoomExt, TimelineBuilder}, unable_to_decrypt_hook::UtdHookManager, diff --git a/bindings/matrix-sdk-ffi/src/room_directory_search.rs b/bindings/matrix-sdk-ffi/src/room_directory_search.rs index efa1b35f1..ea22b5cf3 100644 --- a/bindings/matrix-sdk-ffi/src/room_directory_search.rs +++ b/bindings/matrix-sdk-ffi/src/room_directory_search.rs @@ -15,11 +15,10 @@ use std::{fmt::Debug, sync::Arc}; -use async_compat::get_runtime_handle; use eyeball_im::VectorDiff; use futures_util::StreamExt; use matrix_sdk::room_directory_search::RoomDirectorySearch as SdkRoomDirectorySearch; -use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm}; +use matrix_sdk_common::{runtime::get_runtime_handle, SendOutsideWasm, SyncOutsideWasm}; use ruma::ServerName; use tokio::sync::RwLock; diff --git a/bindings/matrix-sdk-ffi/src/room_list.rs b/bindings/matrix-sdk-ffi/src/room_list.rs index 4f0dc127d..b66efe76a 100644 --- a/bindings/matrix-sdk-ffi/src/room_list.rs +++ b/bindings/matrix-sdk-ffi/src/room_list.rs @@ -2,7 +2,6 @@ use std::{fmt::Debug, mem::MaybeUninit, ptr::addr_of_mut, sync::Arc, time::Duration}; -use async_compat::get_runtime_handle; use eyeball_im::VectorDiff; use futures_util::{pin_mut, StreamExt}; use matrix_sdk::{ @@ -12,7 +11,7 @@ use matrix_sdk::{ }, Room as SdkRoom, }; -use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm}; +use matrix_sdk_common::{runtime::get_runtime_handle, SendOutsideWasm, SyncOutsideWasm}; use matrix_sdk_ui::{ room_list_service::filters::{ new_filter_all, new_filter_any, new_filter_category, new_filter_deduplicate_versions, diff --git a/bindings/matrix-sdk-ffi/src/session_verification.rs b/bindings/matrix-sdk-ffi/src/session_verification.rs index 155dbeb8d..f36dcb466 100644 --- a/bindings/matrix-sdk-ffi/src/session_verification.rs +++ b/bindings/matrix-sdk-ffi/src/session_verification.rs @@ -1,6 +1,5 @@ use std::sync::{Arc, RwLock}; -use async_compat::get_runtime_handle; use futures_util::StreamExt; use matrix_sdk::{ encryption::{ @@ -11,7 +10,7 @@ use matrix_sdk::{ ruma::events::key::verification::VerificationMethod, Account, }; -use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm}; +use matrix_sdk_common::{runtime::get_runtime_handle, SendOutsideWasm, SyncOutsideWasm}; use ruma::UserId; use tracing::{error, warn}; diff --git a/bindings/matrix-sdk-ffi/src/sync_service.rs b/bindings/matrix-sdk-ffi/src/sync_service.rs index 492f8c4fb..753235ec7 100644 --- a/bindings/matrix-sdk-ffi/src/sync_service.rs +++ b/bindings/matrix-sdk-ffi/src/sync_service.rs @@ -14,10 +14,9 @@ use std::{fmt::Debug, sync::Arc}; -use async_compat::get_runtime_handle; use futures_util::pin_mut; use matrix_sdk::Client; -use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm}; +use matrix_sdk_common::{runtime::get_runtime_handle, SendOutsideWasm, SyncOutsideWasm}; use matrix_sdk_ui::{ sync_service::{ State as MatrixSyncServiceState, SyncService as MatrixSyncService, diff --git a/bindings/matrix-sdk-ffi/src/timeline/mod.rs b/bindings/matrix-sdk-ffi/src/timeline/mod.rs index 468193cfd..b4736a520 100644 --- a/bindings/matrix-sdk-ffi/src/timeline/mod.rs +++ b/bindings/matrix-sdk-ffi/src/timeline/mod.rs @@ -16,7 +16,6 @@ use std::{collections::HashMap, fmt::Write as _, fs, panic, sync::Arc}; use anyhow::{Context, Result}; use as_variant::as_variant; -use async_compat::get_runtime_handle; use eyeball_im::VectorDiff; use futures_util::{pin_mut, StreamExt as _}; use matrix_sdk::{ @@ -31,6 +30,7 @@ use matrix_sdk::{ reply::{EnforceThread, Reply}, }, }; +use matrix_sdk_common::runtime::get_runtime_handle; use matrix_sdk_ui::timeline::{ self, AttachmentSource, EventItemOrigin, Profile, TimelineDetails, TimelineUniqueId as SdkTimelineUniqueId, diff --git a/bindings/matrix-sdk-ffi/src/utils.rs b/bindings/matrix-sdk-ffi/src/utils.rs index 2ddec8095..700150518 100644 --- a/bindings/matrix-sdk-ffi/src/utils.rs +++ b/bindings/matrix-sdk-ffi/src/utils.rs @@ -14,7 +14,7 @@ use std::{mem::ManuallyDrop, ops::Deref}; -use async_compat::get_runtime_handle; +use matrix_sdk_common::runtime::get_runtime_handle; use ruma::{MilliSecondsSinceUnixEpoch, UInt}; use tracing::warn; diff --git a/bindings/matrix-sdk-ffi/src/widget.rs b/bindings/matrix-sdk-ffi/src/widget.rs index 0de925b75..f35cf738b 100644 --- a/bindings/matrix-sdk-ffi/src/widget.rs +++ b/bindings/matrix-sdk-ffi/src/widget.rs @@ -1,12 +1,11 @@ use std::sync::{Arc, Mutex}; -use async_compat::get_runtime_handle; use language_tags::LanguageTag; use matrix_sdk::{ async_trait, widget::{MessageLikeEventFilter, StateEventFilter, ToDeviceEventFilter}, }; -use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm}; +use matrix_sdk_common::{runtime::get_runtime_handle, SendOutsideWasm, SyncOutsideWasm}; use ruma::events::MessageLikeEventType; use tracing::error; diff --git a/crates/matrix-sdk-common/Cargo.toml b/crates/matrix-sdk-common/Cargo.toml index e714f458a..b03c9b62f 100644 --- a/crates/matrix-sdk-common/Cargo.toml +++ b/crates/matrix-sdk-common/Cargo.toml @@ -25,6 +25,7 @@ uniffi = ["dep:uniffi"] test-send-sync = [] [dependencies] +async-compat.workspace = true eyeball-im.workspace = true futures-core.workspace = true futures-util.workspace = true @@ -41,9 +42,10 @@ uniffi = { workspace = true, optional = true } tokio = { workspace = true, features = ["rt", "time", "macros"] } [target.'cfg(target_family = "wasm")'.dependencies] +futures-executor.workspace = true futures-util = { workspace = true, features = ["channel"] } gloo-timers = { workspace = true, features = ["futures"] } -tokio = { workspace = true, features = ["macros"] } +tokio = { workspace = true, features = ["sync", "macros", "time"] } tracing-subscriber = { workspace = true, features = ["fmt", "ansi"] } wasm-bindgen.workspace = true wasm-bindgen-futures = { version = "0.4.33", optional = true } diff --git a/crates/matrix-sdk-common/src/lib.rs b/crates/matrix-sdk-common/src/lib.rs index 491239b36..952be3f4a 100644 --- a/crates/matrix-sdk-common/src/lib.rs +++ b/crates/matrix-sdk-common/src/lib.rs @@ -28,6 +28,7 @@ pub mod failures_cache; pub mod linked_chunk; pub mod locks; pub mod ring_buffer; +pub mod runtime; pub mod serde_helpers; pub mod sleep; pub mod store_locks; diff --git a/crates/matrix-sdk-common/src/runtime.rs b/crates/matrix-sdk-common/src/runtime.rs new file mode 100644 index 000000000..9c07319ee --- /dev/null +++ b/crates/matrix-sdk-common/src/runtime.rs @@ -0,0 +1,110 @@ +// Copyright 2025 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Runtime abstractions for cross-platform async execution. This provides +//! a stand-in for tokio's `get_runtime_handle` method that will work on +//! both Wasm and non-Wasm platforms. It also provides corresponding types +//! that can be used in place of tokio's `Handle` and `Runtime` types. + +#[cfg(not(target_family = "wasm"))] +mod sys { + pub use tokio::runtime::{Handle, Runtime}; + + /// Get a runtime handle appropriate for the current target platform. + /// + /// This function returns a unified `Handle` type that works across both + /// Wasm and non-Wasm platforms, allowing code to be written that is + /// agnostic to the platform-specific runtime implementation. + /// + /// Returns: + /// - A `tokio::runtime::Handle` on non-Wasm platforms + /// - A `WasmRuntimeHandle` on Wasm platforms + pub fn get_runtime_handle() -> Handle { + async_compat::get_runtime_handle() + } +} + +#[cfg(target_family = "wasm")] +mod sys { + use std::future::Future; + + use crate::executor::{spawn, JoinHandle}; + + /// A dummy guard that does nothing when dropped. + /// This is used for the Wasm implementation to match + /// tokio::runtime::EnterGuard. + #[derive(Debug)] + pub struct RuntimeGuard; + + /// A runtime handle implementation for WebAssembly targets. + /// + /// This implements a minimal subset of the tokio::runtime::Handle API + /// that is needed for the matrix-rust-sdk to function on Wasm. + #[derive(Default, Debug)] + pub struct Handle; + pub type Runtime = Handle; + + impl Handle { + /// Spawns a future in the wasm32 bindgen runtime. + #[track_caller] + pub fn spawn(&self, future: F) -> JoinHandle + where + F: Future + 'static, + F::Output: 'static, + { + spawn(future) + } + + /// Runs the provided function on an executor dedicated to blocking + /// operations. + #[track_caller] + pub fn spawn_blocking(&self, func: F) -> JoinHandle + where + F: FnOnce() -> R + 'static, + R: 'static, + { + spawn(async move { func() }) + } + + /// Runs a future to completion on the current thread. + pub fn block_on(&self, future: F) -> T + where + F: Future, + { + futures_executor::block_on(future) + } + + /// Enters the runtime context. + /// + /// For WebAssembly, this is a no-op that returns a dummy guard. + pub fn enter(&self) -> RuntimeGuard { + RuntimeGuard + } + } + + /// Get a runtime handle appropriate for the current target platform. + /// + /// This function returns a unified `Handle` type that works across both + /// Wasm and non-Wasm platforms, allowing code to be written that is + /// agnostic to the platform-specific runtime implementation. + /// + /// Returns: + /// - A `tokio::runtime::Handle` on non-Wasm platforms + /// - A `WasmRuntimeHandle` on Wasm platforms + pub fn get_runtime_handle() -> Handle { + Handle + } +} + +pub use sys::*;