Qubit Tokio Executor
Tokio-backed executor services for Rust.
Overview
Qubit Tokio Executor adapts the Qubit executor abstractions to Tokio. It provides
executor-service semantics for blocking functions submitted through
tokio::task::spawn_blocking, and a separate service for async futures submitted
through tokio::spawn.
The crate does not create or own a Tokio runtime. Calls that spawn Tokio work must be made from inside an existing Tokio runtime configured by the application.
Features
TokioExecutorfor strategy-level Tokio blocking execution.TokioExecutorServicefor managed blocking work backed byspawn_blocking.TokioBlockingExecutorServicealias for naming the Tokio blocking domain explicitly.TokioIoExecutorServicefor asyncFuturework backed bytokio::spawn.TokioBlockingTaskHandlefor tracked blocking tasks with pre-start cancellation.TokioTaskHandlefor async IO tasks with Tokio abort-based cancellation.- Shared
ExecutorService,SubmissionError,StopReport, andCancelResultre-exports for convenient imports.
Runtime Requirement
This crate assumes a Tokio runtime already exists. In applications, enable the
Tokio runtime features you need in Cargo.toml:
[]
= "0.6.1"
= { = "1.52", = ["macros", "rt-multi-thread", "time"] }
If a method internally uses tokio::spawn or tokio::task::spawn_blocking, it
must be called while a Tokio runtime is entered. Calling it without a runtime is
rejected with SubmissionError::WorkerSpawnFailed.
Blocking vs IO Tasks
Use TokioExecutorService or TokioBlockingExecutorService for synchronous
functions that may block an OS thread. These tasks run through Tokio's blocking
pool and should not be used for async IO futures.
Use TokioIoExecutorService for non-blocking futures. These tasks run on
Tokio's async scheduler and should not perform long blocking operations inside
the future body.
Shutdown and Cancellation
A successful submit or spawn means only that the service accepted the task.
Blocking callable submissions report results through the shared TaskHandle;
tracked blocking submissions return TokioBlockingTaskHandle, which combines
the shared tracked-task state with Tokio's abort handle for queued blocking
tasks. Async IO submissions use TokioTaskHandle because they wrap Tokio
JoinHandles directly.
shutdown rejects new tasks and lets accepted tasks finish. stop
rejects new tasks and requests cancellation or abort for tracked Tokio tasks.
Async IO task cancellation sends a best-effort Tokio abort request;
CancelResult::Cancelled means the request was sent, and the final outcome is
the result produced by awaiting the returned TokioTaskHandle. Blocking tasks
submitted through Tokio can be cancelled only before their blocking closure
starts. Queued tracked blocking tasks are removed from service accounting
immediately after successful cancellation; already running blocking code cannot
be forcibly stopped by Rust, and service termination waits for that code to
return.
For TokioExecutorService, StopReport.cancelled counts blocking tasks that
were actually cancelled while still queued. Running blocking tasks are reported
through StopReport.running and are not counted as cancelled, even though
stop requests abort for their Tokio handles. For TokioIoExecutorService,
StopReport.cancelled counts active async tasks for which a Tokio abort request
was sent.
TokioExecutor returns the standard TrackedTask. Cancelling that handle can
prevent the user callable from running if it wins before the task starts, but it
does not remove the already submitted Tokio spawn_blocking wrapper from
Tokio's blocking queue. Use tracked submissions through TokioExecutorService
when queued Tokio blocking work must be aborted directly.
TokioExecutorService exposes both blocking wait_termination and async
await_termination service-level waiting. TokioIoExecutorService intentionally
does not expose service-level async waiting; await the task handles returned by
spawn when the caller needs to observe async task completion.
Quick Start
Tokio blocking work
use io;
use ;
async
Async IO futures
use io;
use TokioIoExecutorService;
async
Choosing an Executor
Use qubit-tokio-executor when your application is already Tokio-based and you
need execution services that integrate with Tokio scheduling. Use
qubit-thread-pool for runtime-independent OS-thread execution, and use
qubit-rayon-executor for CPU-bound Rayon work.
For application-level wiring across blocking, CPU-bound, Tokio blocking, and
async IO domains, use qubit-execution-services.
Testing
A minimal local run:
To mirror what continuous integration enforces, run the repository scripts from
the project root: ./align-ci.sh brings local tooling and configuration in line
with CI, then ./ci-check.sh runs the same checks the pipeline uses. For test
coverage, use ./coverage.sh to generate or open reports.
Contributing
Issues and pull requests are welcome.
- Open an issue for bug reports, design questions, or larger feature proposals when it helps align on direction.
- Keep pull requests scoped to one behavior change, fix, or documentation update when practical.
- Before submitting, run
./align-ci.shand then./ci-check.shso your branch matches CI rules and passes the same checks as the pipeline. - Add or update tests when you change runtime behavior, and update this README or public rustdoc when user-visible API behavior changes.
- If you change runtime, shutdown, or cancellation behavior, cover both blocking and async IO services when applicable.
By contributing, you agree to license your contributions under the Apache License, Version 2.0, the same license as this project.
License
Copyright (c) 2026. Haixing Hu.
This project is licensed under the Apache License, Version 2.0. See the LICENSE file in the repository for the full text.
Author
Haixing Hu — Qubit Co. Ltd.
| Repository | github.com/qubit-ltd/rs-tokio-executor |
| Documentation | docs.rs/qubit-tokio-executor |
| Crate | crates.io/crates/qubit-tokio-executor |