introduce new runtime tokio helpers
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
		| @ -8,10 +8,12 @@ use std::convert::TryFrom; | ||||
| use chrono::offset::{TimeZone, Local}; | ||||
|  | ||||
| use proxmox::tools::io::ReadExt; | ||||
| use proxmox::sys::error::io_err_other; | ||||
|  | ||||
| use crate::pxar::catalog::BackupCatalogWriter; | ||||
| use crate::pxar::{MatchPattern, MatchPatternSlice, MatchType}; | ||||
| use crate::backup::file_formats::PROXMOX_CATALOG_FILE_MAGIC_1_0; | ||||
| use crate::tools::runtime::block_on; | ||||
|  | ||||
| #[repr(u8)] | ||||
| #[derive(Copy,Clone,PartialEq)] | ||||
| @ -384,12 +386,12 @@ impl SenderWriter { | ||||
|  | ||||
| impl Write for SenderWriter { | ||||
|     fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> { | ||||
|         tokio::task::block_in_place(|| { | ||||
|             futures::executor::block_on(async move { | ||||
|                 self.0.send(Ok(buf.to_vec())).await | ||||
|                     .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err.to_string()))?; | ||||
|                 Ok(buf.len()) | ||||
|             }) | ||||
|         block_on(async move { | ||||
|             self.0 | ||||
|                 .send(Ok(buf.to_vec())) | ||||
|                 .await | ||||
|                 .map_err(io_err_other) | ||||
|                 .and(Ok(buf.len())) | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -57,7 +57,7 @@ async fn run() -> Result<(), Error> { | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() { | ||||
|     if let Err(err) = run().await { | ||||
|     if let Err(err) = proxmox_backup::tools::runtime::main(run()) { | ||||
|         eprintln!("ERROR: {}", err); | ||||
|     } | ||||
|     println!("DONE"); | ||||
|  | ||||
| @ -69,8 +69,11 @@ fn send_request( | ||||
|         }) | ||||
| } | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Error> { | ||||
| fn main() -> Result<(), Error> { | ||||
|     proxmox_backup::tools::runtime::main(run()) | ||||
| } | ||||
|  | ||||
| async fn run() -> Result<(), Error> { | ||||
|  | ||||
|     let start = std::time::SystemTime::now(); | ||||
|  | ||||
|  | ||||
| @ -67,8 +67,11 @@ fn send_request( | ||||
|         }) | ||||
| } | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Error> { | ||||
| fn main() -> Result<(), Error> { | ||||
|     proxmox_backup::tools::runtime::main(run()) | ||||
| } | ||||
|  | ||||
| async fn run() -> Result<(), Error> { | ||||
|     let start = std::time::SystemTime::now(); | ||||
|  | ||||
|     let conn = | ||||
|  | ||||
| @ -10,8 +10,11 @@ use proxmox_backup::configdir; | ||||
|  | ||||
| // Simple H2 server to test H2 speed with h2s-client.rs | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Error> { | ||||
| fn main() -> Result<(), Error> { | ||||
|     proxmox_backup::tools::runtime::main(run()) | ||||
| } | ||||
|  | ||||
| async fn run() -> Result<(), Error> { | ||||
|     let key_path = configdir!("/proxy.key"); | ||||
|     let cert_path = configdir!("/proxy.pem"); | ||||
|  | ||||
|  | ||||
| @ -8,8 +8,11 @@ use tokio::io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| use proxmox_backup::client::pipe_to_stream::PipeToSendStream; | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Error> { | ||||
| fn main() -> Result<(), Error> { | ||||
|     proxmox_backup::tools::runtime::main(run()) | ||||
| } | ||||
|  | ||||
| async fn run() -> Result<(), Error> { | ||||
|     let mut listener = TcpListener::bind(std::net::SocketAddr::from(([127,0,0,1], 8008))).await?; | ||||
|  | ||||
|     println!("listening on {:?}", listener.local_addr()); | ||||
|  | ||||
| @ -13,9 +13,8 @@ use proxmox_backup::auth_helpers::*; | ||||
| use proxmox_backup::config; | ||||
| use proxmox_backup::buildcfg; | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() { | ||||
|     if let Err(err) = run().await { | ||||
| fn main() { | ||||
|     if let Err(err) = proxmox_backup::tools::runtime::main(run()) { | ||||
|         eprintln!("Error: {}", err); | ||||
|         std::process::exit(-1); | ||||
|     } | ||||
|  | ||||
| @ -15,9 +15,8 @@ use proxmox_backup::tools::daemon; | ||||
| use proxmox_backup::server::{ApiConfig, rest::*}; | ||||
| use proxmox_backup::auth_helpers::*; | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() { | ||||
|     if let Err(err) = run().await { | ||||
| fn main() { | ||||
|     if let Err(err) = proxmox_backup::tools::runtime::main(run()) { | ||||
|         eprintln!("Error: {}", err); | ||||
|         std::process::exit(-1); | ||||
|     } | ||||
|  | ||||
| @ -12,9 +12,8 @@ use proxmox_backup::backup::*; | ||||
| // | ||||
| // Note: I can currently get about 830MB/s | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() { | ||||
|     if let Err(err) = run().await { | ||||
| fn main() { | ||||
|     if let Err(err) = proxmox_backup::tools::runtime::main(run()) { | ||||
|         panic!("ERROR: {}", err); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -21,9 +21,8 @@ async fn upload_speed() -> Result<usize, Error> { | ||||
|     Ok(res) | ||||
| } | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main()  { | ||||
|     match upload_speed().await { | ||||
| fn main()  { | ||||
|     match proxmox_backup::tools::runtime::main(upload_speed()) { | ||||
|         Ok(mbs) => { | ||||
|             println!("average upload speed: {} MB/s", mbs); | ||||
|         } | ||||
|  | ||||
| @ -5,6 +5,7 @@ use failure::*; | ||||
|  | ||||
| use super::BackupReader; | ||||
| use crate::backup::{ReadChunk, DataBlob, CryptConfig}; | ||||
| use crate::tools::runtime::block_on; | ||||
|  | ||||
| /// Read chunks from remote host using ``BackupReader`` | ||||
| pub struct RemoteChunkReader { | ||||
| @ -35,7 +36,14 @@ impl ReadChunk for RemoteChunkReader { | ||||
|  | ||||
|         let mut chunk_data = Vec::with_capacity(4*1024*1024); | ||||
|  | ||||
|         tokio::task::block_in_place(|| futures::executor::block_on(self.client.download_chunk(&digest, &mut chunk_data)))?; | ||||
|         //tokio::task::block_in_place(|| futures::executor::block_on(self.client.download_chunk(&digest, &mut chunk_data)))?; | ||||
|         block_on(async { | ||||
|             // download_chunk returns the writer back to us, but we need to return a 'static value | ||||
|             self.client | ||||
|                 .download_chunk(&digest, &mut chunk_data) | ||||
|                 .await | ||||
|                 .map(drop) | ||||
|         })?; | ||||
|  | ||||
|         let chunk = DataBlob::from_raw(chunk_data)?; | ||||
|         chunk.verify_crc()?; | ||||
|  | ||||
| @ -1,20 +1,131 @@ | ||||
| //! Helpers for quirks of the current tokio runtime. | ||||
|  | ||||
| use std::cell::RefCell; | ||||
| use std::future::Future; | ||||
|  | ||||
| pub fn main<F, T>(fut: F) -> T | ||||
| where | ||||
|     F: Future<Output = T> + Send + 'static, | ||||
|     T: std::fmt::Debug + Send + 'static, | ||||
| { | ||||
|     let mut rt = tokio::runtime::Runtime::new().unwrap(); | ||||
|     rt.block_on(async { | ||||
|         let (tx, rx) = tokio::sync::oneshot::channel(); | ||||
| use lazy_static::lazy_static; | ||||
| use tokio::runtime::{self, Runtime}; | ||||
|  | ||||
| thread_local! { | ||||
|     static HAS_RUNTIME: RefCell<bool> = RefCell::new(false); | ||||
|     static IN_TOKIO: RefCell<bool> = RefCell::new(false); | ||||
| } | ||||
|  | ||||
| fn is_in_tokio() -> bool { | ||||
|     IN_TOKIO.with(|v| *v.borrow()) | ||||
| } | ||||
|  | ||||
| fn has_runtime() -> bool { | ||||
|     HAS_RUNTIME.with(|v| *v.borrow()) | ||||
| } | ||||
|  | ||||
| struct RuntimeGuard(bool); | ||||
|  | ||||
| impl RuntimeGuard { | ||||
|     fn enter() -> Self { | ||||
|         Self(HAS_RUNTIME.with(|v| { | ||||
|             let old = *v.borrow(); | ||||
|             *v.borrow_mut() = true; | ||||
|             old | ||||
|         })) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Drop for RuntimeGuard { | ||||
|     fn drop(&mut self) { | ||||
|         HAS_RUNTIME.with(|v| { | ||||
|             *v.borrow_mut() = self.0; | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| lazy_static! { | ||||
|     static ref RUNTIME: Runtime = { | ||||
|         runtime::Builder::new() | ||||
|             .threaded_scheduler() | ||||
|             .enable_all() | ||||
|             .on_thread_start(|| IN_TOKIO.with(|v| *v.borrow_mut() = true)) | ||||
|             .build() | ||||
|             .expect("failed to spawn tokio runtime") | ||||
|     }; | ||||
| } | ||||
|  | ||||
| /// Get or create the current main tokio runtime. | ||||
| /// | ||||
| /// This makes sure that tokio's worker threads are marked for us so that we know whether we | ||||
| /// can/need to use `block_in_place` in our `block_on` helper. | ||||
| pub fn get_runtime() -> &'static Runtime { | ||||
|     &RUNTIME | ||||
| } | ||||
|  | ||||
| /// Associate the current newly spawned thread with the main tokio runtime. | ||||
| pub fn enter_runtime<R>(f: impl FnOnce() -> R) -> R { | ||||
|     let _guard = RuntimeGuard::enter(); | ||||
|     get_runtime().enter(f) | ||||
| } | ||||
|  | ||||
| /// Block on a synchronous piece of code. | ||||
| pub fn block_in_place<R>(fut: impl FnOnce() -> R) -> R { | ||||
|     if is_in_tokio() { | ||||
|         // we are in an actual tokio worker thread, block it: | ||||
|         tokio::task::block_in_place(fut) | ||||
|     } else { | ||||
|         // we're not inside a tokio worker, so just run the code: | ||||
|         fut() | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Block on a future in this thread. | ||||
| pub fn block_on<R, F>(fut: F) -> R | ||||
| where | ||||
|     R: Send + 'static, | ||||
|     F: Future<Output = R> + Send, | ||||
| { | ||||
|  | ||||
|     if is_in_tokio() { | ||||
|         // inside a tokio worker we need to tell tokio that we're about to really block: | ||||
|         tokio::task::block_in_place(move || futures::executor::block_on(fut)) | ||||
|     } else if has_runtime() { | ||||
|         // we're already associated with a runtime, but we're not a worker-thread, we can just | ||||
|         // block this thread directly | ||||
|         // This is not strictly necessary, but it's a bit quicker tha the else branch below. | ||||
|         futures::executor::block_on(fut) | ||||
|     } else { | ||||
|         // not a worker thread, not associated with a runtime, make sure we have a runtime (spawn | ||||
|         // it on demand if necessary), then enter it: | ||||
|         enter_runtime(move || futures::executor::block_on(fut)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
| fn block_on_impl<F>(mut fut: F) -> F::Output | ||||
| where | ||||
|     F: Future + Send, | ||||
|     F::Output: Send + 'static, | ||||
| { | ||||
|     let (tx, rx) = tokio::sync::oneshot::channel(); | ||||
|     let fut_ptr = &mut fut as *mut F as usize; // hack to not require F to be 'static | ||||
|     tokio::spawn(async move { | ||||
|             tx.send(fut.await).unwrap() | ||||
|         let fut: F = unsafe { std::ptr::read(fut_ptr as *mut F) }; | ||||
|         tx | ||||
|             .send(fut.await) | ||||
|             .map_err(drop) | ||||
|             .expect("failed to send block_on result to channel") | ||||
|     }); | ||||
|  | ||||
|         rx.await.unwrap() | ||||
|     futures::executor::block_on(async move { | ||||
|         rx.await.expect("failed to receive block_on result from channel") | ||||
|     }) | ||||
|     std::mem::forget(fut); | ||||
| } | ||||
| */ | ||||
|  | ||||
| /// This used to be our tokio main entry point. Now this just calls out to `block_on` for | ||||
| /// compatibility, which will perform all the necessary tasks on-demand anyway. | ||||
| pub fn main<F>(fut: F) -> F::Output | ||||
| where | ||||
|     F: Future + Send, | ||||
|     F::Output: Send + 'static, | ||||
| { | ||||
|     block_on(fut) | ||||
| } | ||||
|  | ||||
| @ -2,9 +2,10 @@ use std::io::{self, Read}; | ||||
| use std::pin::Pin; | ||||
| use std::task::{Context, Poll}; | ||||
|  | ||||
| use tokio::task::block_in_place; | ||||
| use futures::stream::Stream; | ||||
|  | ||||
| use crate::tools::runtime::block_in_place; | ||||
|  | ||||
| pub struct WrappedReaderStream<R: Read + Unpin> { | ||||
|     reader: R, | ||||
|     buffer: Vec<u8>, | ||||
|  | ||||
		Reference in New Issue
	
	Block a user