operation_queue/lib.rs
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5// Turn the nightly `doc_cfg` attribute on for docs.rs, so it mentions which
6// types/modules are gated behind specific features.
7#![cfg_attr(docsrs, feature(doc_cfg))]
8
9//! This crate contains the queueing logic for asynchronous operations.
10//!
11//! It also contains helpers for synchronizing operations such as error handling
12//! across futures, in the [`line_token`] module.
13//!
14//! # The operation queue
15//!
16//! The queueing of operations is handled by the [`OperationQueue`] struct. It
17//! runs a given number of parallel runners, to which it dispatches operations
18//! on a "first come, first served" basis.
19//!
20//! An operation is a data structure that implements the [`QueuedOperation`]
21//! trait, and is started by the queue calling its `perform` method. Because
22//! this method is asynchronous, thus breaking [dyn compatibility], another
23//! trait that is dyn-compatible ([`ErasedQueuedOperation`]) is used by the
24//! queue. However, `ErasedQueuedOperation` is implemented by any type that
25//! implements `QueuedOperation`, so consumers usually don't need to bother with
26//! it.
27//!
28//! [`OperationQueue`] is runtime-agnostic, meaning it is not designed to work
29//! only with a specific asynchronous runtime. However, it still needs to spawn
30//! a task for each of its runners. This is why [`OperationQueue::new`] takes a
31//! function as its sole argument, which is given the future for a runner's
32//! loop. For example, creating a new queue with the `tokio` crate could look
33//! like this:
34//!
35//! ```
36//! # use operation_queue::OperationQueue;
37//! let queue = OperationQueue::new(|fut| {
38//! let _ = tokio::task::spawn_local(fut);
39//! });
40//! ```
41//!
42//! The queue is started by [`OperationQueue::start`], and stopped by
43//! [`OperationQueue::stop`]. When starting the queue, the number of runners
44//! provided as the function's argument are created and started. A runner is a
45//! small stateful `struct` with an infinite asynchronous loop. Upon stopping,
46//! the queue terminates and clears all current runners. Note that, once
47//! stopped, a queue cannot be started again.
48//!
49//! Queuing operations is done with [`OperationQueue::enqueue`]. The operation
50//! is pushed to the back of the queue, and will be performed whenenever the
51//! previous operations have also been performed and a runner becomes available.
52//!
53//! # Multithreading
54//!
55//! The synchronization helpers in the [`line_token`] module are thread-safe.
56//!
57//! However, in order to maintain compatibility with the current Thunderbird
58//! code-base, the operation queue's runner cannot be sent between threads.
59//! This is something we plan to address in the future.
60//!
61//! [dyn compatibility]:
62//! <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
63
64#[cfg(feature = "line_token")]
65pub mod line_token;
66
67// The queue is the main feature from this crate, so expose it at the top-level.
68mod error;
69mod operation_queue;
70pub use error::*;
71pub use operation_queue::*;