Rust mpsc::Sender cannot be shared between threads? - multithreading

I thought the whole purpose of a channel was to share data between threads. I have this code, based on this example:
let tx_thread = tx.clone();
let ctx = self;
thread::spawn(|| {
...
let result = ctx.method()
tx_thread.send((String::from(result), someOtherString)).unwrap();
})
Where tx is a mpsc::Sender<(String, String)>
error[E0277]: the trait bound `std::sync::mpsc::Sender<(std::string::String, std::string::String)>: std::marker::Sync` is not satisfied
--> src/my_module/my_file.rs:137:9
|
137 | thread::spawn(|| {
| ^^^^^^^^^^^^^
|
= note: `std::sync::mpsc::Sender<(std::string::String, std::string::String)>` cannot be shared between threads safely
= note: required because of the requirements on the impl of `std::marker::Send` for `&std::sync::mpsc::Sender<(std::string::String, std::string::String)>`
= note: required because it appears within the type `[closure#src/my_module/my_file.rs:137:23: 153:10 res:&&str, ctx:&&my_module::my_submodule::Reader, tx_thread:&std::sync::mpsc::Sender<(std::string::String, std::string::String)>]`
= note: required by `std::thread::spawn`
I'm confused where I went wrong. Unless I'm looking in the wrong place and my issue is actually my use of let ctx = self;?

Sender cannot be shared between threads, but it can be sent!
It implements the trait Send but not Sync (Sync: Safe to access shared reference to Sender across threads).
The design of channels intends that you .clone() the sender and pass it as a value to a thread (for each thread you have). You are missing the move keyword on the thread's closure, which instructs the closure to capture variables by taking ownership of them.
If you must share a single channel endpoint between several threads, it must be wrapped in a mutex. Mutex<Sender<T>> is Sync + Send where T: Send.
Interesting implementation note: The channel starts out for use as a stream where it has a single producer. The internal data structures are upgraded to a multi-producer implementation the first time a sender is cloned.

You may use std::sync::mpsc::SyncSender from the standard library. The diffrenece is that it implements the Sync trait but it will may block if there is no space in the internal buffer while sending a message.
For more information:
std::sync::mpsc::channel
std::sync::mpsc::sync_channel

Related

Basic multithreading: How to use struct without Send trait in thread move closure

I am learning Rust and trying to make a simple egui GUI app that polls a telnet host in a seperate thread, to avoid the main thread locking the GUI. I am using the telnet crate for the client.
Here is the code where I am having issues:
struct TelnetApp {
gui_status: AppView,
telnet_ip: String,
telnet_port: String,
telnet_connect_failed_display: bool,
telnet_connect_failed_message: String,
telnet_client: Arc<Mutex<Option<Telnet>>>,
telnet_result : Arc<Mutex<String>>,
}
impl TelnetApp {
// Called from gui after successfully connecting to Telnet host
fn start_telnet_loop(&mut self) {
let arc_telnet_result = self.telnet_result.clone();
let arc_telnet_client = self.telnet_client.clone();
let time = SystemTime::now();
thread::spawn(move || { // <---- ERROR: `(dyn Stream + 'static)` cannot be sent between threads safely
loop {
thread::sleep(Duration::from_millis(1000));
arc_telnet_client.lock().unwrap().unwrap().read(); // <--- This line causes error
{
// I can read and modify the String, presumably because it implements Send?
*arc_telnet_result.lock().unwrap() = String::from(format!("Time {}", time.elapsed().unwrap().as_micros()));
}
}
});
}
}
As i marked with a comment, the thread spawn line gives me an error, which seems to stem from the fact that arc_telnet_client does not implement the trait "Send", as the error goes away when removing the line:
arc_telnet_client.lock().unwrap().unwrap().read()
I read that wrapping in Arc<Mutex<>> is the recommended way to handle multithreading, but this does still not give the trait Send.
Why is my approach not allowed, even when I am using a mutex to lock it? How would you implement a simple polling thread like this?
Why is my approach not allowed, even when I am using a mutex to lock it?
Because !Send means the object is not safe to move to a different thread at all. It doesn't matter how you protect it, it's just not valid.
For instance it might be using threadlocal data, or kernel task resources, or some sort of unprotected global or shared state, or affinity mechanics. No matter how or why, if it's !Send, it can only be accessed from and by the thread where it was created, doesn't matter what you wrap it in. An example of that is MutexGuard:
impl<T: ?Sized> !Send for MutexGuard<'_, T>
That is because it's common for mutexes to only be unlockable from the thread which locked them (it's UB in posix, the release fails on windows)
As its traits describe, a Mutex is Sync (so can be shared between threads) if an object is Send:
impl<T: ?Sized + Send> Sync for Mutex<T>
That is because semantically, having a mutex around a Send is equivalent to moving the wrapped object from one thread to the next through a channel (the wrapped object will always be used from one thread at a time).
RwLock, on the other hand, requires Sync since the wrapped object can be concurrently accessed from different threads (in read mode):
impl<T: ?Sized + Send + Sync> Sync for RwLock<T>

`RefCell<std::string::String>` cannot be shared between threads safely?

This is a continuation of How to re-use a value from the outer scope inside a closure in Rust? , opened new Q for better presentation.
// main.rs
// The value will be modified eventually inside `main`
// and a http request should respond with whatever "current" value it holds.
let mut test_for_closure :Arc<RefCell<String>> = Arc::new(RefCell::from("Foo".to_string()));
// ...
// Handler for HTTP requests
// From https://docs.rs/hyper/0.14.8/hyper/service/fn.service_fn.html
let make_svc = make_service_fn(|_conn| async {
Ok::<_, Infallible>(service_fn(|req: Request<Body>| async move {
if req.version() == Version::HTTP_11 {
let foo:String = *test_for_closure.borrow();
Ok(Response::new(Body::from(foo.as_str())))
} else {
Err("not HTTP/1.1, abort connection")
}
}))
});
Unfortunately, I get RefCell<std::string::String> cannot be shared between threads safely:
RefCell only works on single threads. You will need to use Mutex which is similar but works on multiple threads. You can read more about Mutex here: https://doc.rust-lang.org/std/sync/struct.Mutex.html.
Here is an example of moving an Arc<Mutex<>> into a closure:
use std::sync::{Arc, Mutex};
fn main() {
let mut test: Arc<Mutex<String>> = Arc::new(Mutex::from("Foo".to_string()));
let mut test_for_closure = Arc::clone(&test);
let closure = || async move {
// lock it so it cant be used in other threads
let foo = test_for_closure.lock().unwrap();
println!("{}", foo);
};
}
The first error in your error message is that Sync is not implemented for RefCell<String>. This is by design, as stated by Sync's rustdoc:
Types that are not Sync are those that have “interior mutability” in a
non-thread-safe form, such as Cell and RefCell. These types allow for
mutation of their contents even through an immutable, shared
reference. For example the set method on Cell takes &self, so it
requires only a shared reference &Cell. The method performs no
synchronization, thus Cell cannot be Sync.
Thus it's not safe to share RefCells between threads, because you can cause a data race through a regular, shared reference.
But what if you wrap it in Arc ? Well, the rustdoc is quite clear again:
Arc will implement Send and Sync as long as the T implements Send
and Sync. Why can’t you put a non-thread-safe type T in an Arc to
make it thread-safe? This may be a bit counter-intuitive at first:
after all, isn’t the point of Arc thread safety? The key is this:
Arc makes it thread safe to have multiple ownership of the same
data, but it doesn’t add thread safety to its data. Consider
Arc<RefCell>. RefCell isn’t Sync, and if Arc was always Send,
Arc<RefCell> would be as well. But then we’d have a problem:
RefCell is not thread safe; it keeps track of the borrowing count
using non-atomic operations.
In the end, this means that you may need to pair Arc with some sort
of std::sync type, usually Mutex.
Arc<T> will not be Sync unless T is Sync because of the same reason. Given that, probably you should use std/tokio Mutex instead of RefCell

Rust ownership in loops

I'm trying to implement rabbitmq send/listen functionality in Rust and I have the following code:
struct RabbitMQ {
connection: Connection,
}
impl RabbitMQ {
fn connect() -> Self {
RabbitMQ {
connection: the created connection
}
}
}
impl MessageBroker for RabbitMQ {
async fn publish(&self, topic: &Topic) -> Result<PublisherConfirm, Error> {
let channel = self.connection.create_channel().await.unwrap();
RabbitMQ::create_exchange(&channel, &topic.exchange).await;
let payload = topic.message.as_bytes();
let res = channel.basic_publish(
topic.exchange.name.as_str(),
topic.queue.routing_key.as_str(),
topic.exchange.publish_options,
payload.to_vec(),
BasicProperties::default(),
);
res.await
}
}
So far so good!
Now I want to publish many messages in a for loop without waiting for the confirmation from the server, the problem is that when I spawn tokio async task I need to move my broker value and this makes it invalid for the next iteration of the loop:
let broker = RabbitMQ::connect(&connection_details).await;
for x in 1..10 {
tokio::spawn(async move {
let confirm = broker.publish(&my_topic).await.unwrap();
}).await.unwrap();
}
The above code won't compile with the following error:
error[E0382]: use of moved value: `broker`
--> src/main.rs:47:33
|
21 | let broker = RabbitMQ::connect(&connection_details).await;
| ------ move occurs because `broker` has type `message_broker::RabbitMQ`, which >does not implement the `Copy` trait
...
47 | tokio::spawn(async move {
| _________________________________^
48 | | let confirm = &broker.publish(&enable_cdn).await.unwrap();
| | ------ use occurs due to use in generator
49 | | }).await.unwrap();
| |_________^ value moved here, in previous iteration of loop
I can't implement the Copy trait as Connection isn't primitive and it seems that I can't use reference "&" to the broker.
My question is how can I accomplish this without writing n publish calls?
You're using an async move block, which means any name which is used in the block is moved into the future, regardless of the operations being performed. So writing
&broker.publish
inside the block makes no difference: first broker is moved, and the future (when polled with .await) takes an internal reference to it. So what you need to do is borrow outside the block then move that borrow inside:
let broker = RabbitMQ::connect(&connection_details).await;
for x in 1..10 {
let broker = &broker;
tokio::spawn(async move {
let confirm = broker.publish(&enable_cdn).await.unwrap();
}).await.unwrap();
}
but I think that's not going to work either: tokio::spawn is not scoped, so even though you're await-ing it, the compiler has no idea that it will not outlive broker. As far as it's concerned a tokio task can live as long as it wants. This means you're now probably going to get a lifetime error (the compiler will assume the borrow can outlive the enclosing function, and thus its origin).
An easy solution to that would be to put the Connection behind an Arc or something.
Alternatively, restructure your system to work better with the requirements of rabbitmq: no idea which you're using but amiquip states that connections are thread-safe, and channels while not thread-safe can be sent to other threads.
So rather than publish-ing to an implicit connection, in each iteration of the loop create a channel and move that into the task in order to actually perform the publication.
Also,
Now I want to publish many messages in a for loop without waiting for the confirmation from the server
aren't you still doing that since you're awaiting the result of tokio::spawn?

Communicate Rust pnet packets between threads using channels

I'm working on a simple Rust program that reads and parses network packets. For reading the network packets I'm using the pnet libary.
Because the parsing may take some time I'm using two separate threads for reading and parsing the packets.
My idea now was to pass the read packages from the first thread to the second thread via message passing (using mpsc::channel()).
Here's a simplified version of my code which I wrote based on the example given in the pnet doc:
extern crate pnet;
use std::sync::mpsc;
use std::thread;
use pnet::datalink;
use pnet::datalink::Channel::Ethernet;
fn main() {
let (sender, receiver) = mpsc::channel();
thread::spawn(move || {
for packet in receiver.recv() {
println!("{:?}", packet)
}
});
let interface = datalink::interfaces().into_iter()
.find(|interface| interface.name == "enp5s0")
.unwrap();
let (_, mut package_receiver) =
match datalink::channel(&interface, Default::default()) {
Ok(Ethernet(tx, rx)) => (tx, rx),
_ => panic!()
};
loop {
match package_receiver.next() {
Ok(packet) => {
// sender.send("foo"); // this works fine
sender.send(packet);
}
_ => panic!()
}
}
}
This works fine for sending primitive types or Strings over the channel, but not for the network packets. When I try to send a packet to the parser thread via the channel I get the following compiler error:
error[E0597]: `*package_receiver` does not live long enough
--> src/main.rs:28:15
|
28 | match package_receiver.next() {
| ^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
36 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
I'm pretty new to Rust and would really appreciate the help!
packet is &[u8] type, with some lifetime 'a that is also same as that of the reference taken to package_receiver in the next() call. The next() definition with lifetimes will look like this:
fn next(&'a mut self) -> Result<&'a [u8]>
You send the &[u8] to a thread. But the thread can outlive the references you send to it, leading to dangling references. As a result the compiler complains that they need to have 'static lifetime."foo" works because it is &'static str.
One way would be to take the ownership of the data and then send it as a value to another thread.
Ok(packet) => {
// sender.send("foo"); // this works fine
sender.send(packet.to_owned());
}
You can also have a look at using scoped threads with crossbeam
The package_receiver.next() call is defined as:
pub trait DataLinkReceiver: Send {
fn next(&mut self) -> Result<&[u8]>;
}
And the mspc::Sender defines send as:
pub fn send(&self, t: T) -> Result<(), SendError<T>>
So package_receiver.next() returns a result containing a reference to a slice of bytes, &[u8]. So when you then call sender.send(packet); that's saying you want to send the reference to the other thread. However, the match scope for package_receiver.next() doesn't promise that the reference lives longer than the end of the scope. So the other thread can't be guaranteed that the reference is still valid at the time it's accessing that data.
The str works because it's a static lifetime string. That memory will always be valid to read no matter what thread reads it.
If you change your call to:
sender.send(Vec::from(packet))
This creates a Vec variable, copies the packet slice into the new memory, then passes ownership of that variable to the other thread. This guarantees that the other receiving thread will always have clear access to that data. Because the ownership is clearly passed, the code in the receiving thread is known to the compiler as where the lifetime of received Vec variables ends.
There will also be some miscellaneous errors about using the result of the .send() which could be taken care of with something like this:
if sender.send(Vec::from(packet)).is_err() {
println!("Send error");
}
The other answers here do a great job of explaining why this happens, and it seems EthernetPacket (which derives the packet macro) is simply a wrapper around a reference of bytes. To make a "new" EthernetPacket variable from the reference of bytes, one could do the following:
let packet = EthernetPacket::owned(packet.to_owned()).unwrap();
sender.send(packet).unwrap();
This will clone the packet bytes and make send them through the channel.
Documentation for EthernetPacket::owned can be found here.

Can I safely multithread something which isn't meant to be multithreaded?

I'm using a trait which isn't designed around multithreading (Cursive).
Now, while it's using multithreading, it's going to be behind a mutex, so it won't be able to be used at two threads at the same time.
What is rust trying to protect me against and can I do anything about it?
For sample reference, my sample code is:
extern crate cursive;
use cursive::Cursive;
use std::thread;
use std::sync::{Mutex,Arc};
fn main() {
let mut siv = Arc::new(Mutex::new(Cursive::default()));
let copy_siv = siv.clone();
thread::spawn(move || {
let mut new_siv = copy_siv.lock().unwrap();
});
(*(siv.lock().unwrap())).run();
}
The compiler complains at thread::spawn:
Error[E0277]: `(dyn cursive::traits::View + 'static)` cannot be sent between threads safely
--> src/main.rs:16:5
|
16 | thread::spawn(move || {
| ^^^^^^^^^^^^^ `(dyn cursive::traits::View + 'static)` cannot be sent between threads safely
|
= help: the trait `std::marker::Send` is not implemented for `(dyn cursive::traits::View + 'static)`
What is rust trying to protect me against [...]
Something in what you're sending between threads contains a dyn cursive::traits::View trait object. This trait object is not Send. It needs to be Send because by putting it inside an Arc, you can no longer predict which thread will be responsible for destroying it, so it must be safe to transfer ownership between threads.
[...] can I do anything about it?
You haven't provided enough context to say for certain, but probably not.
You could maybe try using a plain borrowed reference (plus a threading library that supports scoped threads), but I can't say if that will work for you.
Why wouldn't Mutex make it sync? Isn't that the point of Mutex?
No. It can't make something thread-safe when it wasn't already thread-safe. Mutex just manages exclusive access to a value, it doesn't make that access from different threads safe. The only thing that can make a type thread-safe is the type in question.
Making a guess: the library was written such that it does not require thread safety, thus Arc cannot assume it's thread-safe, so it refuses to compile.
I don't know what your actual code is. But the following example replicate the exact error you have:
use std::thread;
use std::sync::{Mutex,Arc};
struct Cursive;
impl Default for Cursive {
fn default() -> Self {
Cursive
}
}
trait View{
fn run(&self);
}
impl View for Cursive{
fn run(&self){}
}
fn main() {
let mut siv:Arc<Mutex<dyn View>> = Arc::new(Mutex::new(Cursive::default()));
let copy_siv = siv.clone();
thread::spawn(move || {
let mut new_siv = copy_siv.lock().unwrap();
});
(*(siv.lock().unwrap())).run();
}
You can try it in playground. The error message:
error[E0277]: `dyn View` cannot be sent between threads safely
--> src/main.rs:21:5
|
21 | thread::spawn(move || {
| ^^^^^^^^^^^^^ `dyn View` cannot be sent between threads safely
|
= help: the trait `std::marker::Send` is not implemented for `dyn View`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<dyn View>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<std::sync::Mutex<dyn View>>`
= note: required because it appears within the type `[closure#src/main.rs:21:19: 23:6 copy_siv:std::sync::Arc<std::sync::Mutex<dyn View>>]`
= note: required by `std::thread::spawn`
Analysis and Solution
The error message explained everything to experienced users. For those new to the language, siv is a reference counted, mutex protected trait object. This object only known to be a View, the compiler have no evidence on whether or not it is Send. However, for the code to work,
Arc<Mutex<T>> must be Send, as you are sending such a thing to another thread; Therefore:
Mutex<T> must be Send and Sync, as Arc requires the reference counted object to be Send and Sync. Therefore:
T must be Send, as the same object will be accessed in different threads without any further protection.
So, this code does not work. The solution is
let mut siv:Arc<Mutex<dyn View + Send>> = ...
You can try it yourself!
Mutex<T>: Send + Sync requires T: Send
To see why, first ask a question: what cannot be Send?
One example is that references to things with interior mutablity cannot be Send. Because if they were, people can mutate the thing through interior mutability in different threads and causes data race.
Now suppose you have a Mutex<&Cell<T>>, because the protected thing is only a reference, not the Cell itself, the Cell itself may still be somewhere unprotected. The compiler thus cannot conclude when you call lock().set() there is no risk to cause data race. So the compiler prevent it from Send.
What if I have to ...
So we see that &Cell<T> is not Send, and so even it is protected in Mutex we still cannot use it in another thread. What can we do then?
This problem is actually not new. Almost all UI API have the same problem: the UI components were created in the UI thread, and so you cannot access them in any other threads. Instead, you have to schedule a routine to be run in the UI thread, and let the UI thread to access the component.
Fails to do so in other languages (.NET, Java...) will throw exceptions in the best, causing undefined behavior in the worst. Once again, Rust turns such violates into compile errors without special treatments (&Cell<T> have nothing to do with UI), this is really GOOD!
So, if this is what you wanted to do, you have to do the same thing: access the view object in the UI thread only. How to do so depends on the API you were using.

Resources