diff --git a/rust/src/deferred.rs b/rust/src/deferred.rs index be0d536797..ff750cbddc 100644 --- a/rust/src/deferred.rs +++ b/rust/src/deferred.rs @@ -26,7 +26,7 @@ use pyo3::{ use tokio::sync::oneshot; use crate::reactor::Reactor; -use crate::runtime::RustRuntimeInner; +use crate::runtime::RustRuntime; create_exception!( synapse.synapse_rust.http_client, @@ -75,7 +75,7 @@ fn logging_context_module(py: Python<'_>) -> PyResult<&Bound<'_, PyAny>> { /// Does not handle deferred cancellation or contextvars. pub fn create_deferred<'py, F, O>( py: Python<'py>, - runtime: &Arc, + runtime: &RustRuntime, fut: F, ) -> PyResult> where @@ -91,7 +91,7 @@ where // Keep the runtime state (and, through it, the reactor) alive while the // task is in flight. - let runtime = Arc::clone(runtime); + let runtime = runtime.clone(); handle.spawn(async move { let res = task.await; diff --git a/rust/src/handlers/mod.rs b/rust/src/handlers/mod.rs index b5ad872bc1..3119113a0e 100644 --- a/rust/src/handlers/mod.rs +++ b/rust/src/handlers/mod.rs @@ -41,7 +41,7 @@ impl RustHandlers { // The per-homeserver Rust state, which gives us the tokio runtime // and the Twisted reactor. - let runtime = homeserver.rust_runtime(py)?; + let runtime = homeserver.get_rust_runtime(py)?; let db_pool = PythonDatabasePoolWrapper::new( &homeserver.main_database_pool(py)?, @@ -62,7 +62,7 @@ impl RustHandlers { versions::VersionsHandler { global_unstable_feature_map: Arc::clone(&global_unstable_feature_map), store: Arc::clone(&store), - runtime: Arc::clone(&runtime), + runtime: runtime.clone(), }, )?; diff --git a/rust/src/handlers/versions.rs b/rust/src/handlers/versions.rs index 01289291ce..7db91c4182 100644 --- a/rust/src/handlers/versions.rs +++ b/rust/src/handlers/versions.rs @@ -21,7 +21,7 @@ use serde::Serialize; use crate::config::{types::RoomCreationPreset, SynapseHomeServerConfig}; use crate::deferred::create_deferred; -use crate::runtime::RustRuntimeInner; +use crate::runtime::RustRuntime; use crate::storage::store::{PerUserExperimentalFeature, Store}; /// `GET /_matrix/client/versions` response @@ -48,7 +48,7 @@ pub struct VersionsHandler { pub store: Arc, /// The per-homeserver Rust state, used to bridge our `async` response /// back into a Twisted deferred that Python can `await`. - pub runtime: Arc, + pub runtime: RustRuntime, } #[pymethods] diff --git a/rust/src/homeserver.rs b/rust/src/homeserver.rs index d34ec00e13..499668564b 100644 --- a/rust/src/homeserver.rs +++ b/rust/src/homeserver.rs @@ -15,12 +15,10 @@ //! A typed wrapper around the Python `HomeServer`. -use std::sync::Arc; - use pyo3::{intern, prelude::*}; use crate::config::SynapseHomeServerConfig; -use crate::runtime::{RustRuntime, RustRuntimeInner}; +use crate::runtime::RustRuntime; /// The Python `HomeServer`, as seen from Rust. /// @@ -41,14 +39,12 @@ impl<'a, 'py> FromPyObject<'a, 'py> for HomeServer { impl HomeServer { /// The per-homeserver Rust state (`hs.get_rust_runtime()`), which gives /// access to the tokio runtime and the reactor. - pub fn rust_runtime(&self, py: Python<'_>) -> PyResult> { - let runtime: Bound<'_, RustRuntime> = self + pub fn get_rust_runtime(&self, py: Python<'_>) -> PyResult { + Ok(self .0 .bind(py) .call_method0(intern!(py, "get_rust_runtime"))? - .extract()?; - - Ok(Arc::clone(runtime.get().inner())) + .extract()?) } /// The Rust-side view of `hs.config`. @@ -58,7 +54,7 @@ impl HomeServer { /// The Synapse `Clock` (`hs.get_clock()`). // TODO: give the clock a typed wrapper of its own. - pub fn clock(&self, py: Python<'_>) -> PyResult> { + pub fn get_clock(&self, py: Python<'_>) -> PyResult> { Ok(self .0 .bind(py) diff --git a/rust/src/http_client.rs b/rust/src/http_client.rs index 1a88cf47d0..d500d7a261 100644 --- a/rust/src/http_client.rs +++ b/rust/src/http_client.rs @@ -13,7 +13,6 @@ */ use std::collections::HashMap; -use std::sync::Arc; use anyhow::Context; use http_body_util::BodyExt; @@ -22,7 +21,7 @@ use reqwest::RequestBuilder; use crate::deferred::create_deferred; use crate::errors::HttpResponseException; -use crate::runtime::{RustRuntime, RustRuntimeInner}; +use crate::runtime::RustRuntime; /// Called when registering modules with python. pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { @@ -43,7 +42,7 @@ pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> #[pyclass] struct HttpClient { client: reqwest::Client, - runtime: Arc, + runtime: RustRuntime, } #[pymethods] @@ -51,7 +50,7 @@ impl HttpClient { #[new] #[pyo3(signature = (runtime, user_agent, http2_only = false))] pub fn py_new( - runtime: &Bound<'_, RustRuntime>, + runtime: RustRuntime, user_agent: &str, http2_only: bool, ) -> PyResult { @@ -65,10 +64,7 @@ impl HttpClient { let client = builder.build().context("building reqwest client")?; - Ok(HttpClient { - client, - runtime: Arc::clone(runtime.get().inner()), - }) + Ok(HttpClient { client, runtime }) } pub fn get<'a>( diff --git a/rust/src/msc4388_rendezvous/mod.rs b/rust/src/msc4388_rendezvous/mod.rs index 943b147a30..ffd4f240d9 100644 --- a/rust/src/msc4388_rendezvous/mod.rs +++ b/rust/src/msc4388_rendezvous/mod.rs @@ -99,7 +99,7 @@ impl MSC4388RendezvousHandler { eviction_interval: u64, ttl: u64, ) -> PyResult> { - let clock = homeserver.clock(py)?; + let clock = homeserver.get_clock(py)?; // Construct a Python object so that we can get a reference to the // evict method and schedule it to run. diff --git a/rust/src/rendezvous/mod.rs b/rust/src/rendezvous/mod.rs index 54a2d259bb..64a5123f31 100644 --- a/rust/src/rendezvous/mod.rs +++ b/rust/src/rendezvous/mod.rs @@ -123,7 +123,7 @@ impl RendezvousHandler { let base = Uri::try_from(format!("{base}_synapse/client/rendezvous")) .map_err(|_| PyValueError::new_err("Invalid base URI"))?; - let clock = homeserver.clock(py)?; + let clock = homeserver.get_clock(py)?; let eviction_duration = SynapseDuration::from_milliseconds(eviction_interval); diff --git a/rust/src/runtime.rs b/rust/src/runtime.rs index 8675423d0d..943756b0ce 100644 --- a/rust/src/runtime.rs +++ b/rust/src/runtime.rs @@ -22,6 +22,7 @@ //! [`Arc`] at construction time and don't need the GIL (or //! the Python-facing object) to reach it afterwards. +use std::ops::Deref; use std::sync::{Arc, Mutex, Weak}; use std::time::Duration; @@ -119,18 +120,24 @@ impl Drop for RustRuntimeInner { } } -/// The Python-facing handle to the per-homeserver Rust state. +/// A cheaply-clonable handle to the per-homeserver Rust state, and the +/// Python-facing class for it. /// -/// Constructed by `HomeServer.get_rust_runtime()`, and passed to the Rust -/// classes that need it (which take a clone of the inner [`Arc`] and drop -/// this handle). -#[pyclass(frozen, name = "RustRuntime", module = "synapse.synapse_rust")] +/// One instance is constructed per homeserver by +/// `HomeServer.get_rust_runtime()`. Rust classes that need it take it as a +/// constructor argument — pyo3 extracts a `#[pyclass]` that is `Clone` by +/// cloning, which here is just an `Arc` refcount bump — and hold their own +/// clone. Derefs to [`RustRuntimeInner`]. +#[pyclass(frozen, from_py_object, module = "synapse.synapse_rust")] +#[derive(Clone)] pub struct RustRuntime { inner: Arc, } -impl RustRuntime { - pub fn inner(&self) -> &Arc { +impl Deref for RustRuntime { + type Target = RustRuntimeInner; + + fn deref(&self) -> &Self::Target { &self.inner } }