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, ScopedTasks can be kept alive after the scope is terminated.

Typically, the only situations in which drop is not executed are:

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]

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]

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> Future for ScopedTask<'a, T>[src]

type Output = T

The type of value produced on completion.

Auto Trait Implementations

impl<'a, T> RefUnwindSafe for ScopedTask<'a, T> where
    T: RefUnwindSafe

impl<'a, T> !Send for ScopedTask<'a, T>

impl<'a, T> !Sync for ScopedTask<'a, T>

impl<'a, T> Unpin for ScopedTask<'a, T>

impl<'a, T> UnwindSafe for ScopedTask<'a, T> where
    T: RefUnwindSafe + UnwindSafe

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> From<T> for T[src]

impl<F> FutureExt for F where
    F: Future + ?Sized
[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<F> IntoFuture for F where
    F: Future
[src]

type Output = <F as Future>::Output

🔬 This is a nightly-only experimental API. (into_future)

The output that the future will produce on completion.

type Future = F

🔬 This is a nightly-only experimental API. (into_future)

Which kind of future are we turning this into?

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<F, T, E> TryFuture for F where
    F: Future<Output = Result<T, E>> + ?Sized

type Ok = T

The type of successful values yielded by this future

type Error = E

The type of failures yielded by this future

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.