Struct glommio::ScopedTask
source · pub struct ScopedTask<'a, T>(/* private fields */);
Expand description
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;
let ex = LocalExecutor::default();
ex.run(async {
let a = 2;
let task = unsafe {
glommio::spawn_scoped_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 {
glommio::spawn_scoped_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 {
glommio::scoped_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 {
glommio::spawn_scoped_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 {
glommio::spawn_scoped_local(async {
*a = "doom";
})
};
std::mem::forget(task);
}
Implementations§
source§impl<'a, T> ScopedTask<'a, T>
impl<'a, T> ScopedTask<'a, T>
sourcepub async fn cancel(self) -> Option<T>
pub async fn cancel(self) -> Option<T>
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;
let ex = LocalExecutor::default();
ex.run(async {
let task = unsafe {
glommio::spawn_scoped_local(async {
loop {
println!("Even though I'm in an infinite loop, you can still cancel me!");
future::yield_now().await;
}
})
};
task.cancel().await;
});