lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit 519afbb6c25f02aa33b9f05f39639fb3459445ee
parent 8d63f0086c607ba3f7d02e1aca2b65c9f80c0c2e
Author: triesap <tyson@radroots.org>
Date:   Tue,  4 Aug 2026 07:58:17 +0000

runtime: bind actor waits to native scheduler

- execute mailbox receipt timeouts on the owned Tokio runtime
- preserve a safe closed result when a scheduled wait terminates
- isolate native JVM tests behind a development-only data root
- cover generated recovery through the real Kotlin and UniFFI boundary

Diffstat:
Mcrates/studio_ffi/src/commands.rs | 9+++++++--
Mcrates/studio_storage/src/runtime_actor.rs | 12+++++++++---
2 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/crates/studio_ffi/src/commands.rs b/crates/studio_ffi/src/commands.rs @@ -23,6 +23,7 @@ const DATABASE_QUALIFIER: &str = "org"; const DATABASE_ORGANIZATION: &str = "radroots"; const DATABASE_APPLICATION: &str = "studio"; const DATABASE_FILENAME: &str = "studio.sqlite3"; +const DEVELOPMENT_DATA_DIR_ENVIRONMENT: &str = "RADROOTS_STUDIO_DEVELOPMENT_DATA_DIR"; pub(crate) const ACTOR_MAILBOX_CAPACITY: usize = 64; pub const FFI_CONTRACT_MAJOR: u16 = 2; pub const FFI_CONTRACT_MINOR: u16 = 0; @@ -229,7 +230,7 @@ impl StudioAppCore { expectation: CompatibilityExpectation, development_mode: bool, ) -> Result<Arc<Self>, StudioError> { - let path = canonical_database_path()?; + let path = application_database_path(development_mode)?; Self::open_path_compatible(&path, &expectation, development_mode) } @@ -540,7 +541,11 @@ impl Clock for SystemClock { } } -fn canonical_database_path() -> Result<PathBuf, StudioError> { +fn application_database_path(development_mode: bool) -> Result<PathBuf, StudioError> { + if development_mode && let Some(directory) = std::env::var_os(DEVELOPMENT_DATA_DIR_ENVIRONMENT) + { + return Ok(PathBuf::from(directory).join(DATABASE_FILENAME)); + } ProjectDirs::from( DATABASE_QUALIFIER, DATABASE_ORGANIZATION, diff --git a/crates/studio_storage/src/runtime_actor.rs b/crates/studio_storage/src/runtime_actor.rs @@ -115,6 +115,7 @@ pub struct RuntimeActorHandle { mailbox: ActorMailbox<RuntimeCommand, RuntimeCommandValue>, adapter: Arc<PersistentAppCore>, lifecycle: Arc<Mutex<LifecycleGate>>, + runtime: Handle, next_request: Arc<AtomicU64>, session_generation: Arc<AtomicU64>, foreground_session: Arc<Mutex<Option<ForegroundSessionBinding>>>, @@ -234,6 +235,7 @@ impl RuntimeActorHandle { mailbox, adapter, lifecycle, + runtime: runtime.clone(), next_request: Arc::new(AtomicU64::new(1)), session_generation, foreground_session, @@ -560,9 +562,13 @@ impl RuntimeActorHandle { let receipt = match self.mailbox.submit(context, command) { CommandSubmission::Accepted(ticket) => { let remaining = deadline.saturating_duration_since(Instant::now()); - match tokio::time::timeout(remaining, ticket.receipt()).await { - Ok(receipt) => receipt, - Err(_) => CommandReceipt::new(request_id, CommandResult::TimedOut), + let waiting = self + .runtime + .spawn(async move { tokio::time::timeout(remaining, ticket.receipt()).await }); + match waiting.await { + Ok(Ok(receipt)) => receipt, + Ok(Err(_)) => CommandReceipt::new(request_id, CommandResult::TimedOut), + Err(_) => CommandReceipt::new(request_id, CommandResult::Closed), } } CommandSubmission::Rejected(receipt) => receipt,