If you use a new runtime and the lazy static is first used within the context of an existing runtime, like in this example:
use lazy_static::lazy_static;
use mongodb::Client;
lazy_static! {
static ref CLIENT: Client = {
tokio::runtime::Runtime::new().unwrap().block_on(async {
let uri = std::env::var("MONGO_URL").unwrap();
let client = Client::with_uri_str(&uri).await.unwrap();
client
})
};
}
#[tokio::main]
async fn main() {
let _db = CLIENT.database("local");
}
You'll get the error mentioned:
thread 'main' panicked at 'Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks.', C:\Users\kmdreko\.cargo\registry\src\github.com-1ecc6299db9ec823\tokio-1.6.1\src\runtime\enter.rs:39:9
You may be able to circumvent this by using a different runtime (futures
vs tokio
vs async-std
) but that's not ideal since it'd still be blocking the underlying one.
A relatively straight-forward way to solve this is not try to do it lazily and initialize it immediately in main
. That way you can utilize an asynchronous runtime directly and not worry about needing one elsewhere:
use mongodb::Client;
use once_cell::sync::OnceCell;
static CLIENT: OnceCell<Client> = OnceCell::new();
#[tokio::main]
async fn main() {
let uri = std::env::var("MONGO_URL").unwrap();
let client = Client::with_uri_str(&uri).await.unwrap();
CLIENT.set(client).unwrap();
let _db = CLIENT.get().unwrap().database("local");
}
This can be done via OnceCell
(as seen above) or something like a RwLock
if necessary.
The most direct answer to what you're trying to achieve would be to use the async_once crate, which makes it use the receiver's runtime to drive the asynchronous function.
use async_once::AsyncOnce;
use lazy_static::lazy_static;
use mongodb::Client;
lazy_static! {
static ref CLIENT: AsyncOnce<Client> = AsyncOnce::new(async {
let uri = std::env::var("MONGO_URL").unwrap();
let client = Client::with_uri_str(&uri).await.unwrap();
client
});
}
#[tokio::main]
async fn main() {
let _db = CLIENT.get().await.database("local");
}
This assumes all or nearly all uses of the client will be in asynchronous contexts.
block_on
function tokio provides instead ofawait
ing the result will get the job done. – Aiden4block_on
or use aOnceCell
. Why are the answers there insufficient? – kmdrekoOnceCell
wrapper that works likelazy_static!
, except it is initialized with anasync
block instead of a closure. However, in this case, it seems like themongodb
library is poorly coded and thewith_uri_str
method doesn't really need to beasync
, so I would just useblock_on
. – Coder-256OnceCell
,futures::executor::block_on
,tokio::runtime::Runtime::new
etc, they all create new problems, like “Cannot start a runtime from within a runtime” or "thread 'tokio-runtime-worker' panicked at 'cannot executeLocalPool
executor from within another executor: EnterError'" and much more... – Mohammed