From b2a9c8213d0520ea644ab95069138e3f3521a0a3 Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Fri, 3 Apr 2026 12:12:59 -0300 Subject: [PATCH] fix: use std::thread::spawn for sync-to-async bridges to avoid nested block_on panic Root cause: new_current_thread().block_on() panics when called from within an existing tokio runtime (including from spawn_blocking). Tokio doesn't allow nested block_on() calls. Fix: Use std::thread::spawn to create a completely separate OS thread with its own runtime, communicating via mpsc channel. This works from any context: async, spawn_blocking, or sync. --- src/core/shared/utils.rs | 42 +++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/core/shared/utils.rs b/src/core/shared/utils.rs index da022403..372895cf 100644 --- a/src/core/shared/utils.rs +++ b/src/core/shared/utils.rs @@ -53,17 +53,19 @@ pub async fn get_database_url() -> Result { pub fn get_database_url_sync() -> Result { let guard = SECRETS_MANAGER.read().map_err(|e| anyhow::anyhow!("Lock poisoned: {}", e))?; if let Some(ref manager) = *guard { - if tokio::runtime::Handle::try_current().is_ok() { + let manager_clone = manager.clone(); + let (tx, rx) = std::sync::mpsc::channel(); + std::thread::spawn(move || { let rt = tokio::runtime::Builder::new_current_thread() .enable_all() - .build() - .map_err(|e| anyhow::anyhow!("Failed to create runtime: {}", e))?; - return rt.block_on(manager.get_database_url()); - } else { - let rt = tokio::runtime::Runtime::new() - .map_err(|e| anyhow::anyhow!("Failed to create runtime: {}", e))?; - return rt.block_on(manager.get_database_url()); - } + .build(); + let result = match rt { + Ok(rt) => rt.block_on(manager_clone.get_database_url()), + Err(e) => Err(anyhow::anyhow!("Failed to create runtime: {}", e)), + }; + let _ = tx.send(result); + }); + return rx.recv().map_err(|e| anyhow::anyhow!("Channel error: {}", e))?; } Err(anyhow::anyhow!( @@ -84,15 +86,19 @@ pub fn get_secrets_manager_sync() -> Option { pub fn get_work_path() -> String { let sm = get_secrets_manager_sync(); if let Some(sm) = sm { - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .ok(); - match rt { - Some(rt) => rt.block_on(sm.get_value("gbo/app", "work_path")) - .unwrap_or_else(|_| "./work".to_string()), - None => "./work".to_string(), - } + let (tx, rx) = std::sync::mpsc::channel(); + std::thread::spawn(move || { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build(); + let result = match rt { + Some(rt) => rt.block_on(sm.get_value("gbo/app", "work_path")) + .unwrap_or_else(|_| "./work".to_string()), + None => "./work".to_string(), + }; + let _ = tx.send(result); + }); + rx.recv().unwrap_or_else(|_| "./work".to_string()) } else { "./work".to_string() }