Struct Builder

struct Builder { ... }

Builds Tokio Runtime with custom configuration values.

Methods can be chained in order to set the configuration values. The Runtime is constructed by calling build.

New instances of Builder are obtained via Builder::new_multi_thread or Builder::new_current_thread.

See function level documentation for details on the various configuration settings.

Examples

use tokio::runtime::Builder;

fn main() {
    // build runtime
    let runtime = Builder::new_multi_thread()
        .worker_threads(4)
        .thread_name("my-custom-name")
        .thread_stack_size(3 * 1024 * 1024)
        .build()
        .unwrap();

    // use runtime ...
}

Implementations

impl Builder

fn enable_io(self: &mut Self) -> &mut Self

Enables the I/O driver.

Doing this enables using net, process, signal, and some I/O types on the runtime.

Examples

use tokio::runtime;

let rt = runtime::Builder::new_multi_thread()
    .enable_io()
    .build()
    .unwrap();
fn max_io_events_per_tick(self: &mut Self, capacity: usize) -> &mut Self

Enables the I/O driver and configures the max number of events to be processed per tick.

Examples

use tokio::runtime;

let rt = runtime::Builder::new_current_thread()
    .enable_io()
    .max_io_events_per_tick(1024)
    .build()
    .unwrap();

impl Builder

fn new_current_thread() -> Builder

Returns a new builder with the current thread scheduler selected.

Configuration methods can be chained on the return value.

To spawn non-Send tasks on the resulting runtime, combine it with a LocalSet.

fn new_multi_thread() -> Builder

Returns a new builder with the multi thread scheduler selected.

Configuration methods can be chained on the return value.

fn enable_all(self: &mut Self) -> &mut Self

Enables both I/O and time drivers.

Doing this is a shorthand for calling enable_io and enable_time individually. If additional components are added to Tokio in the future, enable_all will include these future components.

Examples

use tokio::runtime;

let rt = runtime::Builder::new_multi_thread()
    .enable_all()
    .build()
    .unwrap();
fn worker_threads(self: &mut Self, val: usize) -> &mut Self

Sets the number of worker threads the Runtime will use.

This can be any number above 0 though it is advised to keep this value on the smaller side.

This will override the value read from environment variable TOKIO_WORKER_THREADS.

Default

The default value is the number of cores available to the system.

When using the current_thread runtime this method has no effect.

Examples

Multi threaded runtime with 4 threads

use tokio::runtime;

// This will spawn a work-stealing runtime with 4 worker threads.
let rt = runtime::Builder::new_multi_thread()
    .worker_threads(4)
    .build()
    .unwrap();

rt.spawn(async move {});

Current thread runtime (will only run on the current thread via Runtime::block_on)

use tokio::runtime;

// Create a runtime that _must_ be driven from a call
// to `Runtime::block_on`.
let rt = runtime::Builder::new_current_thread()
    .build()
    .unwrap();

// This will run the runtime and future on the current thread
rt.block_on(async move {});

Panics

This will panic if val is not larger than 0.

fn max_blocking_threads(self: &mut Self, val: usize) -> &mut Self

Specifies the limit for additional threads spawned by the Runtime.

These threads are used for blocking operations like tasks spawned through spawn_blocking, this includes but is not limited to:

Unlike the worker_threads, they are not always active and will exit if left idle for too long. You can change this timeout duration with thread_keep_alive.

It's recommended to not set this limit too low in order to avoid hanging on operations requiring spawn_blocking.

The default value is 512.

Queue Behavior

When a blocking task is submitted, it will be inserted into a queue. If available, one of the idle threads will be notified to run the task. Otherwise, if the threshold set by this method has not been reached, a new thread will be spawned. If no idle thread is available and no more threads are allowed to be spawned, the task will remain in the queue until one of the busy threads pick it up. Note that since the queue does not apply any backpressure, it could potentially grow unbounded.

Panics

This will panic if val is not larger than 0.

Upgrading from 0.x

In old versions max_threads limited both blocking and worker threads, but the current max_blocking_threads does not include async worker threads in the count.

fn thread_name<impl Into<String>: Into<String>>(self: &mut Self, val: impl Into<String>) -> &mut Self

Sets name of threads spawned by the Runtime's thread pool.

The default name is "tokio-runtime-worker".

Examples

# use tokio::runtime;

# pub fn main() {
let rt = runtime::Builder::new_multi_thread()
    .thread_name("my-pool")
    .build();
# }
fn thread_name_fn<F>(self: &mut Self, f: F) -> &mut Self
where
    F: Fn() -> String + Send + Sync + 'static

Sets a function used to generate the name of threads spawned by the Runtime's thread pool.

The default name fn is || "tokio-runtime-worker".into().

Examples

# use tokio::runtime;
# use std::sync::atomic::{AtomicUsize, Ordering};
# pub fn main() {
let rt = runtime::Builder::new_multi_thread()
    .thread_name_fn(|| {
       static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0);
       let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst);
       format!("my-pool-{}", id)
    })
    .build();
# }
fn thread_stack_size(self: &mut Self, val: usize) -> &mut Self

Sets the stack size (in bytes) for worker threads.

The actual stack size may be greater than this value if the platform specifies minimal stack size.

The default stack size for spawned threads is 2 MiB, though this particular stack size is subject to change in the future.

Examples

# use tokio::runtime;

# pub fn main() {
let rt = runtime::Builder::new_multi_thread()
    .thread_stack_size(32 * 1024)
    .build();
# }
fn on_thread_start<F>(self: &mut Self, f: F) -> &mut Self
where
    F: Fn() + Send + Sync + 'static

Executes function f after each thread is started but before it starts doing work.

This is intended for bookkeeping and monitoring use cases.

Examples

# use tokio::runtime;
# pub fn main() {
let runtime = runtime::Builder::new_multi_thread()
    .on_thread_start(|| {
        println!("thread started");
    })
    .build();
# }
fn on_thread_stop<F>(self: &mut Self, f: F) -> &mut Self
where
    F: Fn() + Send + Sync + 'static

Executes function f before each thread stops.

This is intended for bookkeeping and monitoring use cases.

Examples

# use tokio::runtime;
# pub fn main() {
let runtime = runtime::Builder::new_multi_thread()
    .on_thread_stop(|| {
        println!("thread stopping");
    })
    .build();
# }
fn on_thread_park<F>(self: &mut Self, f: F) -> &mut Self
where
    F: Fn() + Send + Sync + 'static

Executes function f just before a thread is parked (goes idle). f is called within the Tokio context, so functions like tokio::spawn can be called, and may result in this thread being unparked immediately.

This can be used to start work only when the executor is idle, or for bookkeeping and monitoring purposes.

Note: There can only be one park callback for a runtime; calling this function more than once replaces the last callback defined, rather than adding to it.

Examples

Multithreaded executor

# use std::sync::Arc;
# use std::sync::atomic::{AtomicBool, Ordering};
# use tokio::runtime;
# use tokio::sync::Barrier;
# pub fn main() {
let once = AtomicBool::new(true);
let barrier = Arc::new(Barrier::new(2));

let runtime = runtime::Builder::new_multi_thread()
    .worker_threads(1)
    .on_thread_park({
        let barrier = barrier.clone();
        move || {
            let barrier = barrier.clone();
            if once.swap(false, Ordering::Relaxed) {
                tokio::spawn(async move { barrier.wait().await; });
           }
        }
    })
    .build()
    .unwrap();

runtime.block_on(async {
   barrier.wait().await;
})
# }

Current thread executor

# use std::sync::Arc;
# use std::sync::atomic::{AtomicBool, Ordering};
# use tokio::runtime;
# use tokio::sync::Barrier;
# pub fn main() {
let once = AtomicBool::new(true);
let barrier = Arc::new(Barrier::new(2));

let runtime = runtime::Builder::new_current_thread()
    .on_thread_park({
        let barrier = barrier.clone();
        move || {
            let barrier = barrier.clone();
            if once.swap(false, Ordering::Relaxed) {
                tokio::spawn(async move { barrier.wait().await; });
           }
        }
    })
    .build()
    .unwrap();

runtime.block_on(async {
   barrier.wait().await;
})
# }
fn on_thread_unpark<F>(self: &mut Self, f: F) -> &mut Self
where
    F: Fn() + Send + Sync + 'static

Executes function f just after a thread unparks (starts executing tasks).

This is intended for bookkeeping and monitoring use cases; note that work in this callback will increase latencies when the application has allowed one or more runtime threads to go idle.

Note: There can only be one unpark callback for a runtime; calling this function more than once replaces the last callback defined, rather than adding to it.

Examples

# use tokio::runtime;
# pub fn main() {
let runtime = runtime::Builder::new_multi_thread()
    .on_thread_unpark(|| {
        println!("thread unparking");
    })
    .build();

runtime.unwrap().block_on(async {
   tokio::task::yield_now().await;
   println!("Hello from Tokio!");
})
# }
fn build(self: &mut Self) -> Result<Runtime>

Creates the configured Runtime.

The returned Runtime instance is ready to spawn tasks.

Examples

use tokio::runtime::Builder;

let rt  = Builder::new_multi_thread().build().unwrap();

rt.block_on(async {
    println!("Hello from the Tokio runtime");
});
fn thread_keep_alive(self: &mut Self, duration: Duration) -> &mut Self

Sets a custom timeout for a thread in the blocking pool.

By default, the timeout for a thread is set to 10 seconds. This can be overridden using .thread_keep_alive().

Example

# use tokio::runtime;
# use std::time::Duration;
# pub fn main() {
let rt = runtime::Builder::new_multi_thread()
    .thread_keep_alive(Duration::from_millis(100))
    .build();
# }
fn global_queue_interval(self: &mut Self, val: u32) -> &mut Self

Sets the number of scheduler ticks after which the scheduler will poll the global task queue.

A scheduler "tick" roughly corresponds to one poll invocation on a task.

By default the global queue interval is 31 for the current-thread scheduler. Please see the module documentation for the default behavior of the multi-thread scheduler.

Schedulers have a local queue of already-claimed tasks, and a global queue of incoming tasks. Setting the interval to a smaller value increases the fairness of the scheduler, at the cost of more synchronization overhead. That can be beneficial for prioritizing getting started on new work, especially if tasks frequently yield rather than complete or await on further I/O. Conversely, a higher value prioritizes existing work, and is a good choice when most tasks quickly complete polling.

Panics

This function will panic if 0 is passed as an argument.

Examples

# use tokio::runtime;
# pub fn main() {
let rt = runtime::Builder::new_multi_thread()
    .global_queue_interval(31)
    .build();
# }
fn event_interval(self: &mut Self, val: u32) -> &mut Self

Sets the number of scheduler ticks after which the scheduler will poll for external events (timers, I/O, and so on).

A scheduler "tick" roughly corresponds to one poll invocation on a task.

By default, the event interval is 61 for all scheduler types.

Setting the event interval determines the effective "priority" of delivering these external events (which may wake up additional tasks), compared to executing tasks that are currently ready to run. A smaller value is useful when tasks frequently spend a long time in polling, or frequently yield, which can result in overly long delays picking up I/O events. Conversely, picking up new events requires extra synchronization and syscall overhead, so if tasks generally complete their polling quickly, a higher event interval will minimize that overhead while still keeping the scheduler responsive to events.

Examples

# use tokio::runtime;
# pub fn main() {
let rt = runtime::Builder::new_multi_thread()
    .event_interval(31)
    .build();
# }

impl Builder

fn enable_time(self: &mut Self) -> &mut Self

Enables the time driver.

Doing this enables using tokio::time on the runtime.

Examples

use tokio::runtime;

let rt = runtime::Builder::new_multi_thread()
    .enable_time()
    .build()
    .unwrap();

impl Debug for Builder

fn fmt(self: &Self, fmt: &mut Formatter<'_>) -> Result

impl Freeze for Builder

impl RefUnwindSafe for Builder

impl Send for Builder

impl Sync for Builder

impl Unpin for Builder

impl UnsafeUnpin for Builder

impl UnwindSafe for Builder

impl<T> Any for Builder

fn type_id(self: &Self) -> TypeId

impl<T> Borrow for Builder

fn borrow(self: &Self) -> &T

impl<T> BorrowMut for Builder

fn borrow_mut(self: &mut Self) -> &mut T

impl<T> From for Builder

fn from(t: T) -> T

Returns the argument unchanged.

impl<T, U> Into for Builder

fn into(self: Self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of [From]<T> for U chooses to do.

impl<T, U> TryFrom for Builder

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

impl<T, U> TryInto for Builder

fn try_into(self: Self) -> Result<U, <U as TryFrom<T>>::Error>