Expand description

§Introduction

A struct for recording execution status of async tasks with lock-free and async methods.

Can host Futures and query whether they are not found, successful, failed, or running.

  • Depend on tokio with feature rt, so cannot use other async runtimes.
  • Depend on scc for lock-free and async HashSet.

Use this crate if:

  • Easy to generate an unique task_id (not necessarily String) for a future (task).
  • Tasks might fail, and then you want to run it again, while you don’t want it to success more then once.
  • Want to record and query all succeeded tasks and failed tasks.
  • Want to handling every task in the same state (e.g. success).

Example.

A recorder can only use one task_id type. The type of task_id should be:

  • Eq + Hash + Clone + Send + Sync + 'static
  • Cheap to clone (sometimes can use Arc).

And remember, you can add anything in the Future to achieve the functionality you want. For example:

  • Handle your Result in Future, and then return empty result Result<(),()>.
  • Send a message to a one shot channel at the end of the Future to notify upper level that “This task is done”. Don’t forget to consider using tokio::spawn when the channel may not complete sending immediately.
  • Set other callback functions.

It is recommended to directly look at the source code (about 100 line) if there is any confusion.

NOTE: This crate use three HashSet to make it easy to handle all tasks in the same state. But scc::HashSet have less contention in single access when it grows larger. Therefore, if you don’t need handling every tasks in the same state, then just use scc::HashMap (task_id -> task_status) to build a simpler implementation, which might have less contention and clone, but more expansive to iterator.

§Usage

Launch a task with a unique task_id and a Future by launch.

Query the state of the task with its task_id by query_task_state or query_task_state_quick.

§Theory & Design

§Abstract Model

Here is the three-level structure for thinking about tasks’ status:

  • Level 0: real_none, real_failed, real_working, real_success : Exact status of the tasks in the CPU (seen by God).
  • Level 1: failed_tasks, working_tasks, success_tasks : Containers to store task_ids (a task_id can be stored in 0 to 2 containers simultaneously).
  • Level 2: Not Found, Failed, Working, Success : States of the task that could be obtained by query_task_state.

§State Transition Diagram

  • Not Found ----> Working
  • Working <---> Failed
  • Working ----> Success

If you equivalent Not Found to Failed, then:

Failed <---> Working ----> Success

§Nature

§About Task

  1. A task is launched by passing a Future<Output=Result<R, E>> with unique task_id.
  2. A task is real_success when return Ok(R), and real_failed when return Err(E).
  3. Different future with the same task_id is considered the same task.
  4. The same task can only real_success once, e.g. a purchase process would never succeed more then once by launching with unique process id as task_id.

§About Task State

  1. If a task’s state is Success, it must be real_success, i.e. $\text{Success}(id) \rightarrow \text{real_success}(id)$.
  2. If a task’s state is Failed, it may be in any status, but mostly real_failed.
  3. If a task’s state is Working, it may be in any status, but mostly real_working.
  4. If a task’s state is Not Found, it may be in any status, but mostly real_none.

§About Task State Transition

  1. Any task’s state can be queried at any time.
  2. The initial state of the task is Not Found, and won’t change immediately after launch.
  3. Always, when a task whose state is Failed or NotFound is launched, it will be Working at some future moment.
  4. Always, when a task is Working, it would eventually be Fail or Success, i.e. $\Box (\text{Working}(id) \rightarrow \lozenge(\text{Fail}(id) \vee \text{Success}(id)))$.
  5. Always, when a task is Success, it would be Success forever, i.e. $\Box (\text{Success}(id) \rightarrow \Box \text{Success}(id))$.

§Other

Relationship between states and containers at query_task_state.

Further propositions and proofs at AsyncTasksRecoder.

Use query_task_state_quick for less contention.

Structs§

  • Arc was used internally, so after clone, the same TaskManager was used, which means you can share AsyncTasksRecoder by clone.
  • Thread-safe.

Enums§