use crate::error::TimeoutError;
use crate::global::BuiltinExecutor;
use crate::{
AbortableJoinHandle, CommunicationTask, Executor, ExecutorBlockOn, ExecutorBlocking,
ExecutorTimeout, JoinHandle, Scope, ScopeExecutor, UnboundedCommunicationTask,
};
use futures::channel::mpsc::{Receiver, UnboundedReceiver};
use parking_lot::{Condvar, Mutex};
use std::sync::LazyLock;
struct ExecutorState {
executor: BuiltinExecutor,
active: usize,
}
struct ExecutorLock {
state: Mutex<ExecutorState>,
available: Condvar,
}
static EXECUTOR: LazyLock<ExecutorLock> = LazyLock::new(|| ExecutorLock {
state: Mutex::new(ExecutorState {
executor: BuiltinExecutor::default(),
active: 0,
}),
available: Condvar::new(),
});
pub(crate) fn executor() -> BuiltinExecutor {
EXECUTOR.state.lock().executor
}
pub(crate) struct ExecutorGuard {
_private: (),
}
impl Drop for ExecutorGuard {
fn drop(&mut self) {
let mut state = EXECUTOR.state.lock();
state.active -= 1;
if state.active == 0 {
EXECUTOR.available.notify_all();
}
}
}
pub(crate) fn set_executor(executor: BuiltinExecutor) -> ExecutorGuard {
let mut state = EXECUTOR.state.lock();
while state.active != 0 && (state.executor != executor || executor.is_exclusive()) {
EXECUTOR.available.wait(&mut state);
}
if state.active == 0 {
state.executor = executor;
}
state.active += 1;
ExecutorGuard { _private: () }
}
pub fn runtime_type() -> Option<&'static str> {
executor().runtime_type()
}
pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
executor().spawn(future)
}
pub fn spawn_blocking<F, T>(future: F) -> JoinHandle<T>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
executor().spawn_blocking(future)
}
pub fn spawn_abortable<F>(future: F) -> AbortableJoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
executor().spawn_abortable(future)
}
pub fn spawn_timeout<F>(
duration: std::time::Duration,
future: F,
) -> JoinHandle<Result<F::Output, TimeoutError>>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
executor().spawn_timeout(duration, future)
}
pub fn spawn_delay<F>(duration: std::time::Duration, future: F) -> JoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
executor().spawn_delay(duration, future)
}
pub fn spawn_abortable_timeout<F>(
duration: std::time::Duration,
future: F,
) -> AbortableJoinHandle<Result<F::Output, TimeoutError>>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
executor().spawn_abortable_timeout(duration, future)
}
pub fn spawn_abortable_delay<F>(
duration: std::time::Duration,
future: F,
) -> AbortableJoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
executor().spawn_abortable_delay(duration, future)
}
pub fn dispatch<F>(future: F)
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
executor().dispatch(future);
}
pub fn spawn_coroutine<T, F, Fut>(f: F) -> CommunicationTask<T>
where
F: FnMut(T) -> Fut + Send + 'static,
Fut: Future<Output = ()> + Send + 'static,
T: Send + 'static,
{
executor().spawn_coroutine(f)
}
pub fn spawn_coroutine_with_buffer<T, F, Fut>(buffer: usize, f: F) -> CommunicationTask<T>
where
F: FnMut(T) -> Fut + Send + 'static,
Fut: Future<Output = ()> + Send + 'static,
T: Send + 'static,
{
executor().spawn_coroutine_with_buffer(buffer, f)
}
pub fn spawn_unbounded_coroutine<T, F, Fut>(f: F) -> UnboundedCommunicationTask<T>
where
F: FnMut(T) -> Fut + Send + 'static,
Fut: Future<Output = ()> + Send + 'static,
T: Send + 'static,
{
executor().spawn_unbounded_coroutine(f)
}
pub fn spawn_coroutine_with_context<T, C, F, Fut>(context: C, f: F) -> CommunicationTask<T>
where
F: FnMut(&mut C, T) -> Fut + Send + 'static,
Fut: Future<Output = ()> + Send + 'static,
C: Send + 'static,
T: Send + 'static,
{
executor().spawn_coroutine_with_context(context, f)
}
pub fn spawn_coroutine_with_buffer_and_context<T, C, F, Fut>(
context: C,
buffer: usize,
f: F,
) -> CommunicationTask<T>
where
F: FnMut(&mut C, T) -> Fut + Send + 'static,
Fut: Future<Output = ()> + Send + 'static,
C: Send + 'static,
T: Send + 'static,
{
executor().spawn_coroutine_with_buffer_and_context(context, buffer, f)
}
pub fn spawn_unbounded_coroutine_with_context<T, C, F, Fut>(
context: C,
f: F,
) -> UnboundedCommunicationTask<T>
where
F: FnMut(&mut C, T) -> Fut + Send + 'static,
Fut: Future<Output = ()> + Send + 'static,
C: Send + 'static,
T: Send + 'static,
{
executor().spawn_unbounded_coroutine_with_context(context, f)
}
pub fn spawn_coroutine_with_receiver<T, F, Fut>(f: F) -> CommunicationTask<T>
where
F: FnMut(Receiver<T>) -> Fut,
Fut: Future<Output = ()> + Send + 'static,
{
executor().spawn_coroutine_with_receiver(f)
}
pub fn spawn_coroutine_with_receiver_and_buffer<T, F, Fut>(
buffer: usize,
f: F,
) -> CommunicationTask<T>
where
F: FnMut(Receiver<T>) -> Fut,
Fut: Future<Output = ()> + Send + 'static,
{
executor().spawn_coroutine_with_receiver_and_buffer(buffer, f)
}
pub fn spawn_coroutine_with_receiver_and_context<T, F, C, Fut>(
context: C,
f: F,
) -> CommunicationTask<T>
where
F: FnMut(C, Receiver<T>) -> Fut,
Fut: Future<Output = ()> + Send + 'static,
{
executor().spawn_coroutine_with_receiver_and_context(context, f)
}
pub fn spawn_coroutine_with_receiver_buffer_and_context<T, F, C, Fut>(
context: C,
buffer: usize,
f: F,
) -> CommunicationTask<T>
where
F: FnMut(C, Receiver<T>) -> Fut,
Fut: Future<Output = ()> + Send + 'static,
{
executor().spawn_coroutine_with_receiver_buffer_and_context(context, buffer, f)
}
pub fn spawn_unbounded_coroutine_with_receiver<T, F, Fut>(f: F) -> UnboundedCommunicationTask<T>
where
F: FnMut(UnboundedReceiver<T>) -> Fut,
Fut: Future<Output = ()> + Send + 'static,
{
executor().spawn_unbounded_coroutine_with_receiver(f)
}
pub fn spawn_unbounded_coroutine_with_receiver_and_context<T, F, C, Fut>(
context: C,
f: F,
) -> UnboundedCommunicationTask<T>
where
F: FnMut(C, UnboundedReceiver<T>) -> Fut,
Fut: Future<Output = ()> + Send + 'static,
{
executor().spawn_unbounded_coroutine_with_receiver_and_context(context, f)
}
pub fn scope<'env, F, T>(f: F) -> impl Future<Output = T>
where
F: for<'scope> AsyncFnOnce(&'scope Scope<'scope, 'env>) -> T,
{
crate::scoped::scope(f)
}
pub fn executor_scope<F, T>(f: F) -> impl Future<Output = T>
where
F: for<'scope> AsyncFnOnce(&ScopeExecutor<'scope, BuiltinExecutor>) -> T,
{
async move {
let executor = executor();
executor.executor_scope(f).await
}
}
pub fn block_on<F: Future>(f: F) -> F::Output {
executor().block_on(f)
}
#[cfg(not(all(feature = "tokio", not(target_arch = "wasm32"))))]
#[derive(Default)]
struct Yield {
yielded: bool,
}
#[cfg(not(all(feature = "tokio", not(target_arch = "wasm32"))))]
impl core::future::Future for Yield {
type Output = ();
fn poll(
mut self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<()> {
if self.yielded {
return core::task::Poll::Ready(());
}
self.yielded = true;
cx.waker().wake_by_ref();
core::task::Poll::Pending
}
}
pub fn yield_now() -> impl Future<Output = ()> {
#[cfg(all(feature = "tokio", not(target_arch = "wasm32")))]
{
tokio::task::yield_now()
}
#[cfg(not(all(feature = "tokio", not(target_arch = "wasm32"))))]
{
Yield::default()
}
}
pub async fn yield_for(amount: usize) {
for _ in 0..amount {
yield_now().await;
}
}