Create a thread-pool/-library agnostic crate | Threads need to be scoped

Hello Rust community,

Problem

I'm currently developing a crate (library) that is using rayon's scoped threads.

I've seen that crossbeam has the possibility to spawn scoped threads, too, so I am wondering now:

Since both libraries use different thread pools (I assume) under the hood and the consumer of my crate could use either rayon or crossbeam (or any other thread-pool-library really) in their application, how can I make my crate "thread-pool-library agnostic", meaning using the same thread pool that my consumer uses/wants to use?

Possible solution

The only solution that comes to my mind is using cargo features, where one feature is e.g. rayon and another is crossbeam and depending on the chosen feature I use rayon or crossbeam internally in my crate.

What is the best approach here in your opinion? If I end up using the cargo feature approach, is there any popular threading library with their own thread pool I've forgotten?

I really appreciate your help. :heart:

I'm pretty sure you can write a trait for this and provide a default implementation for both rayon and crossbeam.

@alice Thank you for your reply. :heart:

I'm not quite sure how to provide a default implementation only with traits, if that's what you mean.

What I've come up with now is using structs which implement the same trait (one struct for rayon, one for crossbeam). Is this what you roughly mean? (link to playground):


trait DoIt {
    fn do_it() -> String;
}

struct DoItWithCrossbeam;
impl DoIt for DoItWithCrossbeam {
    fn do_it() -> String {
        String::from("crossbeam")
    }
}

struct DoItWithRayon;
impl DoIt for DoItWithRayon {
    fn do_it() -> String {
        String::from("rayon")
    }
}

struct Foo;

impl Foo {
    pub fn do_it<T: DoIt>(&self) -> String {
        T::do_it()
    }
}

fn main() {
    
    let foo = Foo;
    println!("{}", foo.do_it::<DoItWithCrossbeam>());
    println!("{}", foo.do_it::<DoItWithRayon>());
}

In the end, my crate library needs to depend both on rayon and crossbeam, right? Might this be seen as bad (some people don't like to use many dependencies)?

Yes, the implementations would be on a struct. Your crate can have a feature for each thread pool implementation, which the user can disable if they don't want that dependency.

You probably want your methods to take an actual value of the struct to allow use of things like a rayon::Threadpool instead of rayon's global pool.

1 Like

Ah, I see, so combining the cargo feature approach with the trait approach to get the best of both. :+1: :tada:

And thank you for the heads-up on passing the structs as a parameter to the method, so the consumer can configure the pool (or pass an existing pool). That makes sense.

You helped me a lot! Thank you for your quick replies. :hugs:

To add info here: rayon uses a thread pool, but crossbeam always spawns full OS threads.

1 Like

@CAD97 Thank you for the heads-up and jumping in here! I haven't expected that. Good to know. :+1: :slightly_smiling_face: