/agnostik

Executor Agnostic Runtime that can run your futures with your favourite Executor

Primary LanguageRustApache License 2.0Apache-2.0

Agnostik

Crates.io doc CI

Agnostik is a layer between your application and the executor for your async stuff. It lets you switch the executors smooth and easy without having to change your applications code.

Features

  • Run futures and wait for them to finish
  • Spawn Futures using the underlying executor
  • Spawn blocking tasks using special threads that are able to execute blocking code

Get started

Check the tests for simple examples.

If you have cargo-edit installed, you can just execute this:

cargo add agnostik

otherwise, add this to your Cargo.toml file

agnostik = "0.2"

Usage

Switching executors

Note: Libraries should not enable any runtime feature. You can choose the executor, by using cargo features. There can only be one enabled runtime. Valid features are:

E.g. to use the Tokio runtime, add the following line to your Cargo.toml

agnostik = { version = "0.2", features = ["runtime_tokio"]}

Examples

Agnostiks API is very easy and only has a few methods to use. Here's an example with the bastion-executor.

use agnostik::prelude::*;

fn main() {
    let runtime = Agnostik::bastion();

    let future = runtime.spawn(async {
        println!("Hello from bastions executor!");
    })
    runtime.block_on(future)
    
    let future = runtime.spawn_blocking(|| {
        expensive_blocking_method();
    })
    runtime.block_on(future)
}

There's also a global executor instance that can be used to spawn futures without creating and storing your own executor. If you specify multiple runtimes, the global executor will be the following:

  • smol if tokio and smol are enabled
  • bastion if async_std, smol and / or tokio is enabled
fn main() {
    let future = agnostik::spawn(async { println!("Hello from bastion executor!"); 1 });
    let result = agnostik::block_on(future);
    assert_eq!(result, 1);
}

If you want to use another executor, you just have to replace the Agnostik::bastion() method call, with the method that corresponds to your executor.

Use

  • Agnostik::bastion() for bastion
  • Agnostik::async_std() for async std
  • Agnostik::tokio() for tokio. Warning: See "How to use tokio runtime"
  • Agnostik::tokio_with_runtime(runtime) if you want to use your own tokio::runtime::Runtime object. Warning: See "How to use tokio runtime"
  • Agnostik::no_std() (coming soon) to create an exeutor that works in a nostd environment

How to use tokio runtime

It's not supported to use the tokio::main macro together with agnostik, because Agnostik requires a Runtime object, which is created by calling Runtime::new(). If your are using the tokio::main macro, there will be a panic, because you can't create a runtime inside a runtime.

Here's how to fix it:

use agnostik::prelude::*;

#[tokio::main]
async fn main() {
    let runtime = Agnostik::tokio();
    
    let result = runtime.spawn(async_task()).await;

    println!("The result is {}", result)
}

This would fail with a panic. How to do it correctly:

use agnostik::prelude::*;
use tokio::runtime::Runtime;

fn main() {
    // see tokio docs for more methods to create a runtime
    let runtime = Runtime::new().expect("Failed to create a runtime"); // 1
    let runtime = Agnostik::tokio_with_runtime(runtime); // 2

    let result = runtime.spawn(async_task());
    let result = runtime.block_on(result);

    println!("The result is {}", result)
}

You can replace 1 and 2 with Agnostik::tokio(), because this method call will create a Runtime object using Runtime::new().

Getting Help

Please head to our Discord.

License

This project is licensed under the Apache2 or MIT License.