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};
 | 
			
		||||
 | 
			
		||||
        tokio::spawn(async move {
 | 
			
		||||
            tx.send(fut.await).unwrap()
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        rx.await.unwrap()
 | 
			
		||||
    })
 | 
			
		||||
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 {
 | 
			
		||||
        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")
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    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