Awaiting a Number of Futures Unknown at Compile Time - rust

I want to leverage Tokio's runtime to handle a variable amount of async futures. Since the count of futures is unknown at compile time, it seems FuturesUnordered is my best option (macros such as select! require specifying your branches at compile time; join_all might be possible but the docs recommend FuturesUnordered "in a lot of cases" when order doesn't matter).
The logic of this snippet is a recv() loop getting pushed to the bucket of futures, which should always run. When new data arrives, its parsing/processing gets pushed to the futures bucket too (instead of being processed immediately). This ensures the receiver maintains low latency in responding to new events, and data processing (potentially computationally expensive decryption) occurs concurrently with all other data processing async blocks (plus the listening receiver).
This thread explains why the futures get .boxed(), by the way.
The problem is this cryptic error:
error[E0277]: `dyn futures::Future<Output = ()> + std::marker::Send` cannot be shared between threads safely
--> src/main.rs:27:8
|
27 | }).boxed());
| ^^^^^ `dyn futures::Future<Output = ()> + std::marker::Send` cannot be shared between threads safely
|
= help: the trait `Sync` is not implemented for `dyn futures::Future<Output = ()> + std::marker::Send`
= note: required because of the requirements on the impl of `Sync` for `Unique<dyn futures::Future<Output = ()> + std::marker::Send>`
= note: required because it appears within the type `Box<dyn futures::Future<Output = ()> + std::marker::Send>`
= note: required because it appears within the type `Pin<Box<dyn futures::Future<Output = ()> + std::marker::Send>>`
= note: required because of the requirements on the impl of `Sync` for `FuturesUnordered<Pin<Box<dyn futures::Future<Output = ()> + std::marker::Send>>>`
= note: required because of the requirements on the impl of `std::marker::Send` for `&FuturesUnordered<Pin<Box<dyn futures::Future<Output = ()> + std::marker::Send>>>`
= note: required because it appears within the type `[static generator#src/main.rs:16:25: 27:6 _]`
= note: required because it appears within the type `from_generator::GenFuture<[static generator#src/main.rs:16:25: 27:6 _]>`
= note: required because it appears within the type `impl futures::Future`
It looks like pushing to an UnorderedFutures "recursively" (not really I guess, but what else would you call it?) doesn't work, but I'm not sure why. This error indicates some Sync trait requirement isn't met for the Box'd & Pin'd async blocks being tended to by the FuturesUnordered -- a requirement I guess is only imposed because &FuturesUnordered (used during futures.push(...) because that method borrows &self) needs it for its Send trait... or something?
use std::error::Error;
use tokio::sync::mpsc::{self, Receiver, Sender};
use futures::stream::futures_unordered::FuturesUnordered;
use futures::FutureExt;
#[tokio::main]
pub async fn main() -> Result<(), Box<dyn Error>> {
let mut futures = FuturesUnordered::new();
let (tx, rx) = mpsc::channel(32);
tokio::spawn( foo(tx) ); // Only the receiver is relevant; its transmitter is
// elsewhere, occasionally sending data.
futures.push((async { // <--- NOTE: futures.push()
loop {
match rx.recv().await {
Some(data) => {
futures.push((async move { // <--- NOTE: nested futures.push()
let _ = data; // TODO: replace with code that processes 'data'
}).boxed());
},
None => {}
}
}
}).boxed());
while let Some(_) = futures.next().await {}
Ok(())
}

I will leave the low-level error for another answer, but I believe a more idiomatic way to solve the high-level problem here would be to combine the use of FuturesUnordered with something like tokio::select! as follows:
use tokio::sync::mpsc;
use futures::stream::FuturesUnordered;
use futures::StreamExt;
#[tokio::main]
pub async fn main() {
let mut futures = FuturesUnordered::new();
let (tx, mut rx) = mpsc::channel(32);
//turn foo into something more concrete
tokio::spawn(async move {
let _ = tx.send(42i32).await;
});
loop {
tokio::select! {
Some(data) = rx.recv() => {
futures.push(async move {
data.to_string()
});
},
Some(result) = futures.next() => {
println!("{}", result)
},
else => break,
}
}
}
You can read more about the select macro here: https://tokio.rs/tokio/tutorial/select

When you box the future created by the async block with the boxed method, you are trying to coerce it to a dyn Future + Send:
pub fn boxed<'a>(
self
) -> Pin<Box<dyn Future<Output = Self::Output> + 'a + Send>>
However, the created future is not Send. Why? Because inside of it, you try to push to the FuturesUnordered, which borrows it:
pub fn push(&self, future: Fut)
This means that the async block captures a &FuturesUnordered. For a type to be Send, all it's fields must be Send, so for the generated future to be Send, &FuturesUnordered must be Send.
For a reference to be Send, the type must also be Sync:
impl<'_, T> Send for &'_ T where
T: Sync
And for FuturesUnordered to be Sync, the stored futures must also be Sync:
impl<Fut: Sync> Sync for FuturesUnordered<Fut> {}
However, the future returned by boxed is not necessarily Sync:
pub fn boxed<'a>(
self
) -> Pin<Box<dyn Future<Output = Self::Output> + 'a + Send>>
Which means that the async generator is not Send, so you cannot coerce it to a dyn Future + Send, and you get a confusing error message.
The solution is to add a Sync bound to the future, and Box::pin manually:
type BoxedFuture = Pin<Box<dyn Future<Output = ()> + Send + Sync>>;
let mut futures = FuturesUnordered::<BoxedFuture>::new();
futures.push(Box::pin(async {
loop {
match rx.recv().await {
Some(data) => {
futures.push(Box::pin(async move {
let _ = data;
}));
}
None => {}
}
}
}));
However, you will then run into a bunch of borrowing issues. A better solution would be to use tokio::select! instead of the outer push, as explained by Michael's answer.

Related

How to wrap std::io::Write in Mutex for tracing-subscriber?

In the documentation for Mutex, it says that it implements Send and Sync -- which makes sense, because a Mutex is designed to be accessed from multiple threads that are locking, using the resource it protects, then unlocking.
However, in my code below, I get a compiler error that, as far as I can tell, complains that the Mutex doesn't implement Send/Sync:
error[E0599]: the method `try_init` exists for struct `SubscriberBuilder<DefaultFields, Format, tracing::level_filters::LevelFilter, std::sync::Mutex<MultiWriter>>`, but its trait bounds were not satisfied
--> src/main.rs:131:10
|
131 | .try_init().expect("setting default subscriber failed");
| ^^^^^^^^ method cannot be called on `SubscriberBuilder<DefaultFields, Format, tracing::level_filters::LevelFilter, std::sync::Mutex<MultiWriter>>` due to unsatisfied trait bounds
|
::: /Users/sean/.cargo/registry/src/github.com-1ecc6299db9ec823/tracing-subscriber-0.3.16/src/fmt/fmt_layer.rs:62:1
|
62 | / pub struct Layer<
63 | | S,
64 | | N = format::DefaultFields,
65 | | E = format::Format<format::Full>,
66 | | W = fn() -> io::Stdout,
67 | | > {
| | -
| | |
| |_doesn't satisfy `_: std::marker::Send`
| doesn't satisfy `_: std::marker::Sync`
|
= note: the following trait bounds were not satisfied:
`tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, std::sync::Mutex<MultiWriter>>: std::marker::Send`
`tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, std::sync::Mutex<MultiWriter>>: std::marker::Sync`
If I remove the line .with_writer(mw) from my code below, the error goes away. Clearly the problem is related to the writer, but I'm not sure how to do this correctly.
The goal of the code is to write the logs from the tracing framework to both stderr and a file specified from dotenvy if a file name is specified (it's optional).
NB: I'm using the latest stable Rust and the released version of each crate used below, and compiling with std, libc, alloc, etc. (full Rust, not embedded) on MacOS, but the code is expected to work on the "multi-platform x86(_64) desktop" environment (Windows/MacOS/desktop Linux). For Tokio I have features = ["full"].
use std::fs::File;
use std::io::Write;
use std::sync::Mutex;
use dotenvy::var;
se std::sync::Arc;
use tracing::Level;
struct MultiWriter {
writers: Vec<Arc<dyn Write>>,
}
impl Write for MultiWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
for writer in self.writers.iter_mut() {
writer.write(buf)?;
}
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
for writer in self.writers.iter_mut() {
writer.flush()?;
}
Ok(())
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut writers: Vec<Arc<dyn Write>> = vec![(Arc::new(std::io::stderr()))];
if let Some(log_file) = var("log_file").ok() {
writers.push(Arc::new(File::create(log_file).unwrap()));
}
let mw = Mutex::new(MultiWriter { writers });
let tsb = tracing_subscriber::FmtSubscriber::builder()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env()).with_ansi(false)
.with_writer(mw);
if let Ok(log_level) = var("log_level") {
match log_level.to_uppercase().as_str() {
"TRACE" => tsb.with_max_level(Level::TRACE),
"DEBUG" => tsb.with_max_level(Level::DEBUG),
"INFO" => tsb.with_max_level(Level::INFO),
"WARN" => tsb.with_max_level(Level::WARN),
"ERROR" => tsb.with_max_level(Level::ERROR),
_ => tsb.with_max_level(Level::INFO)
}
.try_init().expect("setting default subscriber failed");
}
}
In the documentation for Mutex, it says that it implements Send and Sync
That's not completely true:
impl<T: ?Sized + Send> Send for Mutex<T>
impl<T: ?Sized + Send> Sync for Mutex<T>
This means that a Mutex is Send and Sync only if T is Send (the reason for this is described in this question.
However, T isn't Send here:
T is a struct MultiWriter
struct MultiWriter contains a dyn Write
dyn Write is not Send (at least not always)
in turn, struct MultiWriter isn't either.
To fix this, replace dyn Write by dyn Write + Send, and it should work.
I think it needs to be emphasized that the OP is doing Mutex<Arc<MyType>> that this is different than Arc<Mutex<MyType>>. If a type is Send and wrapped in a Mutex, the whole thing is Send + Sync, but since Arc needs both, that's important here. But if Mutex is on the outside (like it is here) then Arc isn't satisfied by what's inside of it.
As Elias cites (correctly), Mutex only requires it's contained value to be Send, not both Send + Sync. The OP's problem (one of them) is that Mutex is on the outside, and Arc demands both Sync + Send which dyn Write isn't.
Arc:
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
Mutex:
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
Ultimately #ChayimFriedman is right that for the OP's block of code, they need both (dyn + Write + Send + Sync), but I wanted to put down here even more as to why.

Tokio / Tonic - How to fix this error: `self` has lifetime `'life0` but it needs to satisfy a `'static` lifetime requirement

I'm building a gRPC server using Rust and tonic and having some issues with a function that returns the stream. So far, the only examples I've seen conveniently create the tx and rx channels within the function - but this isn't a lot of help if you need to receive data from some other part of the application. I have the following code, but I'm getting an error.
Code
use std::sync::Arc;
use std::sync::Mutex;
use futures::{Stream, StreamExt};
use tokio::sync::mpsc;
use tokio::sync::mpsc::{Sender, Receiver};
use tokio_stream::wrappers::ReceiverStream;
use tonic::transport::Server;
use tonic::{Request, Response, Status};use resourcemanager::{LineRequest, LineResponse, Position};
use resourcemanager::resource_manager_server::{ResourceManager, ResourceManagerServer};
pub mod resourcemanager {
tonic::include_proto!("resourcemanager");
}
#[derive(Debug)]
pub struct ResourceManagerService {
linear_rx: mpsc::Receiver<Position>,
linear_tx: mpsc::Sender<Position>
}
#[tonic::async_trait]
impl ResourceManager for ResourceManagerService {
async fn draw_line(&self, request: Request<LineRequest>) -> Result<Response<LineResponse>, Status> {
Ok(Response::new(LineResponse::default()))
}
type StreamLinearMotorMovementStream = ReceiverStream<Result<Position, Status>>;
async fn stream_linear_motor_movement(
&self,
request: Request<()>
) -> Result<Response<Self::StreamLinearMotorMovementStream>, Status> {
println!("Streaming motor movements");
let (tx, mut rx) = mpsc::channel(1);
tokio::spawn(async move {
while let Some(received) = self.linear_rx.recv().await {
tx.send(Ok(received.clone())).await.unwrap();
}
});
Ok(Response::new(ReceiverStream::new(rx)))
}
}
fn main() {
println!("Hello, world!");
}
Error
error[E0759]: `self` has lifetime `'life0` but it needs to satisfy a `'static` lifetime requirement
--> src/main.rs:30:10
|
30 | &self,
| ^^^^ this data with lifetime `'life0`...
...
36 | tokio::spawn(async move {
| ------------ ...is used and required to live as long as `'static` here
|
note: `'static` lifetime requirement introduced by this bound
--> /Users/xxxxxxxx/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.18.2/src/task/spawn.rs:127:28
|
127 | T: Future + Send + 'static,
| ^^^^^^^
This error is shown underneath &self in:
async fn stream_linear_motor_movement(
&self,
request: Request<()>
)
The error message basically says it all. Abbreviated:
async fn stream_linear_motor_movement(&self) {
let (tx, mut rx) = mpsc::channel(1);
tokio::spawn(async move {
while let Some(received) = self.linear_rx.recv().await {}
});
}
The fragment self.linear_rx.recv().await inside the newly spawned task forces the compiler to move self into that closure, so the closure can access self.linear_rx. However, as the new task could run forever, it requires its captured context to have a lifetime of 'static, while &self has a limited, possibly shorter than 'static, lifetime of life0 (whatever that turns out to be). What that means is that you can't access self (or anything derived from it) from within the newly spawned task, because there is no guarantee that it will be around while the task is executing.
What you can do is to move linear_rx in ResourceManagerService into an Arc, .clone() that Arc in stream_linear_motor_movement and move that clone into the closure. Depending on what you are trying to accomplish, you can also move linear_rx into an Option and .take() that Option in stream_linear_motor_movement, leaving None in its place. In both cases, you transfer an owned object into the newly spawned task, which has no lifetime shorter than 'static. Notice that Arc would allow one to call stream_linear_motor_movement many times, while Option would only allow one to call it exactly once (because linear_rx is moved away on the first call).

Why do I get an error that "Sync is not satisfied" when moving self, which contains an Arc, into a new thread?

I have a struct which holds an Arc<Receiver<f32>> and I'm trying to add a method which takes ownership of self, and moves the ownership into a new thread and starts it. However, I'm getting the error
error[E0277]: the trait bound `std::sync::mpsc::Receiver<f32>: std::marker::Sync` is not satisfied
--> src/main.rs:19:9
|
19 | thread::spawn(move || {
| ^^^^^^^^^^^^^ `std::sync::mpsc::Receiver<f32>` cannot be shared between threads safely
|
= help: the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Receiver<f32>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<std::sync::mpsc::Receiver<f32>>`
= note: required because it appears within the type `Foo`
= note: required because it appears within the type `[closure#src/main.rs:19:23: 22:10 self:Foo]`
= note: required by `std::thread::spawn`
If I change the struct to hold an Arc<i32> instead, or just a Receiver<f32>, it compiles, but not with a Arc<Receiver<f32>>. How does this work? The error doesn't make sense to me as I'm not trying to share it between threads (I'm moving it, not cloning it).
Here is the full code:
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
use std::thread;
pub struct Foo {
receiver: Arc<Receiver<f32>>,
}
impl Foo {
pub fn new() -> (Foo, Sender<f32>) {
let (sender, receiver) = channel::<f32>();
let sink = Foo {
receiver: Arc::new(receiver),
};
(sink, sender)
}
pub fn run_thread(self) -> thread::JoinHandle<()> {
thread::spawn(move || {
println!("Thread spawned by 'run_thread'");
self.run(); // <- This line gives the error
})
}
fn run(mut self) {
println!("Executing 'run'")
}
}
fn main() {
let (example, sender) = Foo::new();
let handle = example.run_thread();
handle.join();
}
How does this work?
Let's check the requirements of thread::spawn again:
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
F: FnOnce() -> T,
F: Send + 'static, // <-- this line is important for us
T: Send + 'static,
Since Foo contains an Arc<Receiver<_>>, let's check if and how Arc implements Send:
impl<T> Send for Arc<T>
where
T: Send + Sync + ?Sized,
So Arc<T> implements Send if T implements Send and Sync. And while Receiver implements Send, it does not implement Sync.
So why does Arc have such strong requirements for T? T also has to implement Send because Arc can act like a container; if you could just hide something that doesn't implement Send in an Arc, send it to another thread and unpack it there... bad things would happen. The interesting part is to see why T also has to implement Sync, which is apparently also the part you are struggling with:
The error doesn't make sense to me as I'm not trying to share it between threads (I'm moving it, not cloning it).
The compiler can't know that the Arc in Foo is in fact not shared. Consider if you would add a #[derive(Clone)] to Foo later (which is possible without a problem):
fn main() {
let (example, sender) = Foo::new();
let clone = example.clone();
let handle = example.run_thread();
clone.run();
// oopsie, now the same `Receiver` is used from two threads!
handle.join();
}
In the example above there is only one Receiver which is shared between threads. And this is no good, since Receiver does not implement Sync!
To me this code raises the question: why the Arc in the first place? As you noticed, without the Arc, it works without a problem: you clearly state that Foo is the only owner of the Receiver. And if you are "not trying to share [the Receiver]" anyway, there is no point in having multiple owners.

How do you cast trait objects with an Arc<Mutex<...>>?

I am trying to call a function which takes a pointer to a Mutex of some trait object, and I want to be able to implement the trait for the Mutex of that trait object to allow treating the Mutex as an instance of the trait object for abstraction.
To give an example of this, imagine an Event listener setup as such:
use std::sync::{Arc, Mutex, Weak};
// Define a simple event
trait Event: Send + Sync + 'static {}
impl Event for String {}
// Define the listener interface
trait Listener<E: Event> {
fn notify(&self, event: &E);
}
// Extend the listener interface to listenrs wrapped by a mutex
impl<E: Event> Listener<E> for Mutex<Listener<E>> {
fn notify(&self, event: &E) {
self.lock().unwrap().notify(event);
}
}
// Contrived thing to listen for messages
struct Console;
impl Listener<String> for Console {
fn notify(&self, event: &String) {
println!("{}", event);
}
}
// Simple function which may be called asynchronously and then sends a message
// when it is complete
fn do_stuff(l: Arc<Listener<String>>) {
// Would normally cast to a Weak<...> and then store in a list of listneners
// For some sort of object
let m = String::from("I did stuff!");
l.notify(&m);
}
fn main() {
let l: Arc<Mutex<Console>> = Arc::new(Mutex::new(Console));
let t1 = Arc::clone(&l) as Arc<Mutex<Listener<String>>>; //this part is ok
// Here is where we run into issues... This *should* be equvlient to
// do_stuff(t1), but with the corercion explicit
let t2 = Arc::clone(&t1) as Arc<Listener<String>>;
do_stuff(t2);
// This is a simple, working example of it interpreting a Mutex<Listener<E>>
// as just a Listener<E>
let m = String::from("Somthing else...");
(l as Arc<Mutex<Listener<String>>>).notify(&m);
}
(Playground)
The issue is:
error[E0277]: the trait bound `Listener<std::string::String>: std::marker::Sized` is not satisfied in `std::sync::Mutex<Listener<std::string::String>>`
--> src/main.rs:45:14
|
45 | let t2 = Arc::clone(&t1) as Arc<Listener<String>>;
| ^^^^^^^^^^^^^^^ `Listener<std::string::String>` does not have a constant size known at compile-time
|
= help: within `std::sync::Mutex<Listener<std::string::String>>`, the trait `std::marker::Sized` is not implemented for `Listener<std::string::String>`
= note: required because it appears within the type `std::sync::Mutex<Listener<std::string::String>>`
= note: required for the cast to the object type `Listener<std::string::String>`
Why this is the case? Since an Arc is a pointer to data, from my understanding, it should be able to point to a Listener<String> which happens to be a Listener<Mutex<String>>.
I see at least two ways to avoid this, the first is to simply impl Listener<String> for Mutex<Listener<String>>, however, in the actual code this may require interdependence which should be avoided because the trait can only be implemented where the trait or structure is defined (and Mutex is not defined in my code).
The second is to move the Mutex into the Listener object so the caller does not need to cast it at all. This would work, and may be a better solution. Despite this, I am curious as to either why the casting proposed does not work and or what can be changed to make it work.
Since an Arc is a pointer to data, from my understanding, it should be able to point to a Listener<String>
Yes, that's true. I believe your problem is that you are (accidentally?) requiring that you have a Mutex<Listener<String>> at some point. This is not valid to have because the value inside the Mutex isn't behind a pointer, thus making the whole type unsized.
It's fine to have an Arc<Mutex<Listener<String>>> though.
Instead, I'd implement the trait for a Mutex of any kind that implements the same trait. I'd do the same for references and Boxed trait objects of the trait as well. In all cases, I'd remove the Sized bound to allow for trait objects:
use std::sync::{Arc, Mutex};
trait Event: Send + Sync + 'static {}
impl Event for String {}
trait Listener<E: Event> {
fn notify(&self, event: &E);
}
impl<L, E> Listener<E> for Mutex<L>
where
L: ?Sized + Listener<E>,
E: Event,
{
fn notify(&self, event: &E) {
self.lock().unwrap().notify(event);
}
}
impl<'a, L, E> Listener<E> for &'a L
where
L: ?Sized + Listener<E>,
E: Event,
{
fn notify(&self, event: &E) {
(**self).notify(event);
}
}
struct Console;
impl Listener<String> for Console {
fn notify(&self, event: &String) {
println!("{}", event);
}
}
fn do_stuff(l: Arc<Listener<String>>) {
let m = String::from("I did stuff!");
l.notify(&m);
}
fn main() {
let l: Arc<Mutex<Console>> = Arc::new(Mutex::new(Console));
let l2 = Arc::clone(&l) as Arc<Listener<String>>;
let l3 = Arc::clone(&l) as Arc<Listener<String>>;
do_stuff(l);
do_stuff(l2);
let m = String::from("Something else...");
l3.notify(&m);
}

How can I send a function to another thread?

I am attempting to write a simpler unit test runner for my Rust project. I have created a TestFixture trait that my test fixture structs will implement, similar to inheriting from the unit test base class in other testing frameworks. The trait is fairly simple. This is my test fixture
pub trait TestFixture {
fn setup(&mut self) -> () {}
fn teardown(&mut self) -> () {}
fn before_each(&mut self) -> () {}
fn after_each(&mut self) -> () {}
fn tests(&mut self) -> Vec<Box<Fn(&mut Self)>>
where Self: Sized {
Vec::new()
}
}
My test running function is as follows
pub fn test_fixture_runner<T: TestFixture>(fixture: &mut T) {
fixture.setup();
let _r = fixture.tests().iter().map(|t| {
let handle = thread::spawn(move || {
fixture.before_each();
t(fixture);
fixture.after_each();
});
if let Err(_) = handle.join() {
println!("Test failed!")
}
});
fixture.teardown();
}
I get the error
src/tests.rs:73:22: 73:35 error: the trait `core::marker::Send` is not implemented for the type `T` [E0277]
src/tests.rs:73 let handle = thread::spawn(move || {
^~~~~~~~~~~~~
note: in expansion of closure expansion
src/tests.rs:69:41: 84:6 note: expansion site
src/tests.rs:73:22: 73:35 note: `T` cannot be sent between threads safely
src/tests.rs:73 let handle = thread::spawn(move || {
^~~~~~~~~~~~~
note: in expansion of closure expansion
src/tests.rs:69:41: 84:6 note: expansion site
src/tests.rs:73:22: 73:35 error: the trait `core::marker::Sync` is not implemented for the type `for<'r> core::ops::Fn(&'r mut T)` [E0277]
src/tests.rs:73 let handle = thread::spawn(move || {
^~~~~~~~~~~~~
note: in expansion of closure expansion
src/tests.rs:69:41: 84:6 note: expansion site
src/tests.rs:73:22: 73:35 note: `for<'r> core::ops::Fn(&'r mut T)` cannot be shared between threads safely
src/tests.rs:73 let handle = thread::spawn(move || {
^~~~~~~~~~~~~
note: in expansion of closure expansion
I have tried adding Arcs around the types being sent to the thread, no dice, same error.
pub fn test_fixture_runner<T: TestFixture>(fixture: &mut T) {
fixture.setup();
let fix_arc = Arc::new(Mutex::new(fixture));
let _r = fixture.tests().iter().map(|t| {
let test_arc = Arc::new(Mutex::new(t));
let fix_arc_clone = fix_arc.clone();
let test_arc_clone = test_arc.clone();
let handle = thread::spawn(move || {
let thread_test = test_arc_clone.lock().unwrap();
let thread_fix = fix_arc_clone.lock().unwrap();
(*thread_fix).before_each();
(*thread_test)(*thread_fix);
(*thread_fix).after_each();
});
if let Err(_) = handle.join() {
println!("Test failed!")
}
});
fixture.teardown();
}
A sample test fixture would be something like
struct BuiltinTests {
pwd: PathBuf
}
impl TestFixture for BuiltinTests {
fn setup(&mut self) {
let mut pwd = env::temp_dir();
pwd.push("pwd");
fs::create_dir(&pwd);
self.pwd = pwd;
}
fn teardown(&mut self) {
fs::remove_dir(&self.pwd);
}
fn tests(&mut self) -> Vec<Box<Fn(&mut BuiltinTests)>> {
vec![Box::new(BuiltinTests::cd_with_no_args)]
}
}
impl BuiltinTests {
fn new() -> BuiltinTests {
BuiltinTests {
pwd: PathBuf::new()
}
}
}
fn cd_with_no_args(&mut self) {
let home = String::from("/");
env::set_var("HOME", &home);
let mut cd = Cd::new();
cd.run(&[]);
assert_eq!(env::var("PWD"), Ok(home));
}
#[test]
fn cd_tests() {
let mut builtin_tests = BuiltinTests::new();
test_fixture_runner(&mut builtin_tests);
}
My whole intention of using threads is isolation from the test runner. If a test fails an assertion it causes a panic which kills the runner. Thanks for any insight, I'm willing to change my design if that will fix the panic problem.
There are several problems with your code, I'll show you how to fix them one by one.
The first problem is that you're using map() to iterate over an iterator. It won't work correctly because map() is lazy - unless you consume the iterator, the closure you passed to it won't run. The correct way is to use for loop:
for t in fixture().tests().iter() {
Second, you're iterating the vector of closures by reference:
fixture.tests().iter().map(|t| {
iter() on a Vec<T> returns an iterator yielding items of type &T, so your t will be of type &Box<Fn(&mut Self)>. However, Box<Fn(&mut T)> does not implement Sync by default (it is a trait object which have no information about the underlying type except that you specified explicitly), so &Box<Fn(&mut T)> can't be used across multiple threads. That's what the second error you see is about.
Most likely you don't want to use these closures by reference; you probably want to move them to the spawned thread entirely. For this you need to use into_iter() instead of iter():
for t in fixture.tests().into_iter() {
Now t will be of type Box<Fn(&mut T)>. However, it still can't be sent across threads. Again, it is a trait object, and the compiler does not know if the type contained inside is Send. For this you need to add Send bound to the type of the closure:
fn tests(&mut self) -> Vec<Box<Fn(&mut Self)+Send>>
Now the error about Fn is gone.
The last error is about Send not being implemented for T. We need to add a Send bound on T:
pub fn test_fixture_runner<T: TestFixture+Send>(fixture: &mut T) {
And now the error becomes more comprehensible:
test.rs:18:22: 18:35 error: captured variable `fixture` does not outlive the enclosing closure
test.rs:18 let handle = thread::spawn(move || {
^~~~~~~~~~~~~
note: in expansion of closure expansion
test.rs:18:5: 28:6 note: expansion site
test.rs:15:66: 31:2 note: captured variable is valid for the anonymous lifetime #1 defined on the block at 15:65
test.rs:15 pub fn test_fixture_runner<T: TestFixture+Send>(fixture: &mut T) {
test.rs:16 fixture.setup();
test.rs:17
test.rs:18 for t in fixture.tests().into_iter() {
test.rs:19 let handle = thread::spawn(move || {
test.rs:20 fixture.before_each();
...
note: closure is valid for the static lifetime
This error happens because you're trying to use a reference in a spawn()ed thread. spawn() requires its closure argument to have 'static bound, that is, its captured environment must not contain references with non-'static lifetimes. But that's exactly what happens here - &mut T is not 'static. spawn() design does not prohibit avoiding joining, so it is explicitly written to disallow passing non-'static references to the spawned thread.
Note that while you're using &mut T, this error is unavoidable, even if you put &mut T in Arc, because then the lifetime of &mut T would be "stored" in Arc and so Arc<Mutex<&mut T>> also won't be 'static.
There are two ways to do what you want.
First, you can use the unstable thread::scoped() API. It is unstable because it is shown to allow memory unsafety in safe code, and the plan is to provide some kind of replacement for it in the future. However, you can use it in nightly Rust (it won't cause memory unsafety by itself, only in specifically crafted situations):
pub fn test_fixture_runner<T: TestFixture+Send>(fixture: &mut T) {
fixture.setup();
let tests = fixture.lock().unwrap().tests();
for t in tests.into_iter() {
let f = &mut *fixture;
let handle = thread::scoped(move || {
f.before_each();
t(f);
f.after_each();
});
handle.join();
}
fixture.teardown();
}
This code compiles because scoped() is written in such a way that it guarantees (in most cases) that the thread won't outlive all captured references. I had to reborrow fixture because otherwise (because &mut references aren't copyable) it would be moved into the thread and fixture.teardown() would be prohibited. Also I had to extract tests variable because otherwise the mutex will be locked by the main thread for the duration of the for loop which would naturally disallow locking it in the child threads.
However, with scoped() you can't isolate the panic in the child thread. If the child thread panics, this panic will be rethrown from join() call. This may or may not be a problem in general, but I think it is a problem for your code.
Another way is to refactor your code to hold the fixture in Arc<Mutex<..>> from the beginning:
pub fn test_fixture_runner<T: TestFixture + Send + 'static>(fixture: Arc<Mutex<T>>) {
fixture.lock().unwrap().setup();
for t in fixture.lock().unwrap().tests().into_iter() {
let fixture = fixture.clone();
let handle = thread::spawn(move || {
let mut fixture = fixture.lock().unwrap();
fixture.before_each();
t(&mut *fixture);
fixture.after_each();
});
if let Err(_) = handle.join() {
println!("Test failed!")
}
}
fixture.lock().unwrap().teardown();
}
Note that now T has also to be 'static, again, because otherwise it couldn't be used with thread::spawn() as it requires 'static. fixture inside the inner closure is not &mut T but a MutexGuard<T>, and so it has to be explicitly converted to &mut T in order to pass it to t.
This may seem overly and unnecessarily complex, however, such design of a programming language does prevent you from making many errors in multithreaded programming. Each of the above errors we have seen is valid - each of them would be a potential cause of memory unsafety or data races if it was ignored.
As stated in the Rust HandBook's Concurrency section:
When a type T implements Send, it indicates to the compiler that something of this type is able to have ownership transferred safely between threads.
If you do not implement Send, ownership cannot be transfered between threads.

Resources