Struct glommio::ScopedTask [−][src]
#[must_use = "scoped tasks get canceled when dropped, use a standard Task and `.detach()` to run \ them in the background"]pub struct ScopedTask<'a, T>(_, _);
A spawned future that cannot be detached, and has a predictable lifetime.
Because their lifetimes are bounded, you don’t need to make sure that data
you pass to the ScopedTask
is 'static
, which can be cheaper (no need to
reference count). If you, however, would like to .detach
this task and
have it run in the background, consider using Task
instead.
Tasks are also futures themselves and yield the output of the spawned future.
When a task is dropped, its gets canceled and won’t be polled again. To
cancel a task a bit more gracefully and wait until it stops running, use the
cancel()
method.
Tasks that panic get immediately canceled. Awaiting a canceled task also causes a panic.
Safety
ScopedTask
is safe to use so long as it is guaranteed to be either awaited
or dropped. Rust does not guarantee that destructors will be called, and if
they are not, ScopedTask
s can be kept alive after the scope is terminated.
Typically, the only situations in which drop
is not executed are:
- If you manually choose not to, with
std::mem::forget
orManuallyDrop
. - If cyclic reference counts prevents the task from being destroyed.
If you believe any of the above situations are present (the first one is,
of course, considerably easier to spot), avoid using the ScopedTask
.
Examples
use glommio::{LocalExecutor, ScopedTask}; let ex = LocalExecutor::default(); ex.run(async { let a = 2; let task = unsafe { ScopedTask::local(async { println!("Hello from a task!"); 1 + a // this is a reference, and it works just fine }) }; assert_eq!(task.await, 3); });
The usual borrow checker rules apply. A ScopedTask
can acquire a mutable
reference to a variable just fine:
let mut a = 2; let task = unsafe { ScopedTask::local(async { a = 3; }) }; task.await; assert_eq!(a, 3);
But until the task completes, the reference is mutably held so we can no longer immutably reference it:
let mut a = 2; let task = unsafe { ScopedTask::local(async { a = 3; }) }; assert_eq!(a, 3); // task hasn't completed yet! task.await;
You can still use Cell
and RefCell
normally to work around this.
Just keep in mind that there is no guarantee of ordering for execution of
tasks, and if the task has not yet finished the value may or may not have
changed (as with any interior mutability)
let a = Cell::new(2); let task = unsafe { ScopedTask::local(async { a.set(3); }) }; assert!(a.get() == 3 || a.get() == 2); // impossible to know if it will be 2 or 3 task.await; assert_eq!(a.get(), 3); // The task finished now. //
The following code, however, will access invalid memory as drop is never executed
{ let a = &mut "mayhem"; let task = unsafe { ScopedTask::local(async { *a = "doom"; }) }; std::mem::forget(task); }
Implementations
impl<'a, T> ScopedTask<'a, T>
[src]
impl<'a, T> ScopedTask<'a, T>
[src]pub unsafe fn local(future: impl Future<Output = T> + 'a) -> Self
[src]
Spawns a task onto the current single-threaded executor.
If called from a LocalExecutor
, the task is spawned on it.
Otherwise, this method panics.
Safety
ScopedTask
depends on drop
running or .await
being called for
safety. See the struct ScopedTask
for details.
Examples
use glommio::{LocalExecutor, ScopedTask}; let local_ex = LocalExecutor::default(); local_ex.run(async { let non_static = 2; let task = unsafe { ScopedTask::local(async { 1 + non_static }) }; assert_eq!(task.await, 3); });
pub unsafe fn local_into(
future: impl Future<Output = T> + 'a,
handle: TaskQueueHandle
) -> Result<Self, ()>
[src]
future: impl Future<Output = T> + 'a,
handle: TaskQueueHandle
) -> Result<Self, ()>
Spawns a task onto the current single-threaded executor, in a particular task queue
If called from a LocalExecutor
, the task is spawned on it.
Otherwise, this method panics.
Safety
ScopedTask
depends on drop
running or .await
being called for
safety. See the struct ScopedTask
for details.
Examples
use glommio::{Local, LocalExecutor, ScopedTask, Shares}; let local_ex = LocalExecutor::default(); local_ex.run(async { let handle = Local::create_task_queue( Shares::default(), glommio::Latency::NotImportant, "test_queue", ); let non_static = 2; let task = unsafe { ScopedTask::<usize>::local_into(async { 1 + non_static }, handle) .expect("failed to spawn task") }; assert_eq!(task.await, 3); })
pub async fn cancel(self) -> Option<T>
[src]
Cancels the task and waits for it to stop running.
Returns the task’s output if it was completed just before it got
canceled, or None
if it didn’t complete.
While it’s possible to simply drop the ScopedTask
to cancel it, this
is a cleaner way of canceling because it also waits for the task to
stop running.
Examples
use futures_lite::future; use glommio::{LocalExecutor, ScopedTask}; let ex = LocalExecutor::default(); ex.run(async { let task = unsafe { ScopedTask::local(async { loop { println!("Even though I'm in an infinite loop, you can still cancel me!"); future::yield_now().await; } }) }; task.cancel().await; });
Trait Implementations
impl<'a, T: Debug> Debug for ScopedTask<'a, T>
[src]
impl<'a, T: Debug> Debug for ScopedTask<'a, T>
[src]Auto Trait Implementations
impl<'a, T> RefUnwindSafe for ScopedTask<'a, T> where
T: RefUnwindSafe,
impl<'a, T> RefUnwindSafe for ScopedTask<'a, T> where
T: RefUnwindSafe,
impl<'a, T> !Send for ScopedTask<'a, T>
impl<'a, T> !Send for ScopedTask<'a, T>
impl<'a, T> !Sync for ScopedTask<'a, T>
impl<'a, T> !Sync for ScopedTask<'a, T>
impl<'a, T> Unpin for ScopedTask<'a, T>
impl<'a, T> Unpin for ScopedTask<'a, T>
impl<'a, T> UnwindSafe for ScopedTask<'a, T> where
T: RefUnwindSafe + UnwindSafe,
impl<'a, T> UnwindSafe for ScopedTask<'a, T> where
T: RefUnwindSafe + UnwindSafe,
Blanket Implementations
impl<F> FutureExt for F where
F: Future + ?Sized,
[src]
impl<F> FutureExt for F where
F: Future + ?Sized,
[src]pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output> where
Self: Unpin,
[src]
Self: Unpin,
pub fn or<F>(self, other: F) -> Or<Self, F> where
F: Future<Output = Self::Output>,
[src]
F: Future<Output = Self::Output>,
pub fn race<F>(self, other: F) -> Race<Self, F> where
F: Future<Output = Self::Output>,
[src]
F: Future<Output = Self::Output>,
pub fn catch_unwind(self) -> CatchUnwind<Self> where
Self: UnwindSafe,
[src]
Self: UnwindSafe,
pub fn boxed<'a>(
self
) -> Pin<Box<dyn Future<Output = Self::Output> + 'a + Send, Global>> where
Self: Send + 'a,
[src]
self
) -> Pin<Box<dyn Future<Output = Self::Output> + 'a + Send, Global>> where
Self: Send + 'a,
pub fn boxed_local<'a>(
self
) -> Pin<Box<dyn Future<Output = Self::Output> + 'a, Global>> where
Self: 'a,
[src]
self
) -> Pin<Box<dyn Future<Output = Self::Output> + 'a, Global>> where
Self: 'a,
impl<F> IntoFuture for F where
F: Future,
[src]
impl<F> IntoFuture for F where
F: Future,
[src]type Output = <F as Future>::Output
into_future
)The output that the future will produce on completion.
type Future = F
into_future
)Which kind of future are we turning this into?