At gitoxide, a git implementation in Rust, most code is blocking as it relies heavily on operations which aren't available in 'real' async or are CPU bound.
When implementing the server side or clients it becomes evident that due to the ecosystem one won't get around using async, and those using gitoxide for custom clients or on the server will have to integrate with async crates or framework.
To get started with this and make what's there more flexible the client side is now available in async and a prototype client program exists to test the blocking/async interoperation from start to end.
As all git repository interactions are blocking, async client code coming from the transport or protocol layers will have to call into that. The async parts must not block the executor, and parts of or the entire operation have to be unblocked.
With the current API this looks like this:
let transport = net::connect(url.as_bytes(), protocol.unwrap_or_default().into()).await?;
let mut delegate = CloneDelegate {
ctx,
directory,
refs_directory,
ref_filter: None,
};
blocking::unblock(move || {
futures_lite::future::block_on(protocol::fetch(
transport,
delegate,
protocol::credentials::helper,
progress,
))
})
.await?;
Fully async portions remain async, but those who are at least partially blocking must be entirely unblocked. The need for futures_lite::future::block_on seems avoidable especially when looking at the underlying implementation which relies on Executor::spawn(future) receiving a future.
pub async fn unblock<T, F>(f: F) -> T
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
Executor::spawn(async move { f() }).await
}
If there was a function that takes a future, the code above could be simplified to the following:
blocking::spawn(protocol::fetch(
transport,
delegate,
protocol::credentials::helper,
progress,
))
.await?;
I would love to hear your thoughts about this usecase, and if there is interest will be happy to contribute.
At
gitoxide, a git implementation in Rust, most code is blocking as it relies heavily on operations which aren't available in 'real' async or are CPU bound.When implementing the server side or clients it becomes evident that due to the ecosystem one won't get around using
async, and those usinggitoxidefor custom clients or on the server will have to integrate withasynccrates or framework.To get started with this and make what's there more flexible the client side is now available in
asyncand a prototype client program exists to test the blocking/async interoperation from start to end.As all git repository interactions are blocking,
asyncclient code coming from thetransportorprotocollayers will have to call into that. The async parts must not block the executor, and parts of or the entire operation have to be unblocked.With the current API this looks like this:
Fully async portions remain async, but those who are at least partially blocking must be entirely unblocked. The need for
futures_lite::future::block_onseems avoidable especially when looking at the underlying implementation which relies onExecutor::spawn(future)receiving a future.If there was a function that takes a future, the code above could be simplified to the following:
I would love to hear your thoughts about this usecase, and if there is interest will be happy to contribute.