Global executors 2

I want to make a proposal about the global executors: firstly, the main points in previous such thread were: How to deal with multiple executors? How to deal with generalization of executors API? So, I want to propose a mechanism which solves some parts of these questions. First requirement of corner case is to be able to specify which executor will handle the task, so we'd write something like executor::pick(name).spawn(future). There some questions arise: How to check if name is valid? How to handle registration of executor named name?. More tricky questions are: How to allow runtime formed executors? How to deal with default behaviour for current code, where executor is unspecified? How much of the question we should leave on final user, library author?

The interface of #[global_executor] macro have to allow naming of executor, maybe #[global_executor(name)], thus it could be renamed, due to it not so global nature. The default executor can then be called with just task::spawn and have to be selected inside of main function. Note that there I proposing only interface for selecting executors, not their interface.

Why name, not a reference to an instance?

I think it would be enough to have a common trait for executors, which would allow passing the executors (or handles) to where they're needed. The trait could be implemented on a zero-sized type to use the same interface for a global executor.

2 Likes

Yes, the trait approach is already in use by runtime agnostic libraries, e.g. this is a small snippet from code I have integrating hyper on top of smol

#[derive(Debug, Clone)]
pub struct SmolExecutor;

impl<F> hyper::rt::Executor<F> for SmolExecutor
    where F : Future<Output = ()> + Send + Sync + 'static
{
    fn execute(&self, future: F) {
        smol::Task::spawn(future).detach();
    }
}

That’s literally all it takes to integrate the executor half of the runtime. Integrating the IO is the much more complicated part.

This also leads to supporting more useful implementations than can easily be done via global executors. For example I have another bit of code that wraps the SmolExecutor in an adaptor that sets up a task-local logger instance for tracing all messages with a task-id and executor-id. That was trivial to setup given that the executor was just a value I pass into hyper, trying to configure something similar with only “global” executors seems like it would be much more complicated.

4 Likes