Passing an object to a thread in rust - multithreading

I'm used to writing code in python and C++ and try to get along with rust. I want to pass an object to a thread and call a method of this object.
In addition, the object is passed to the thread by dependency injection because I aim to reuse this module.
When the function expects the Object FIFO, everything's fine. But when using the trait, it fails.
I get the following error when passing the clone of the object to the thread:
borrowed data escapes outside of function
requirement occurs because of the type Mutex<&dyn testTrait>, which makes the generic argument &dyn testTrait invariant
the struct Mutex<T> is invariant over the parameter T
use std::thread;
use std::sync::{Arc, Mutex};
pub trait testTrait: Send + Sync {
fn test(&self, i: i32) -> i32;
}
pub struct FIFO {}
unsafe impl Send for FIFO {}
unsafe impl Sync for FIFO {}
impl testTrait for FIFO {
fn test(&self, i: i32) -> i32 {
return i;
}
}
impl FIFO {}
fn main() {
let fifo = FIFO {};
caller(&fifo);
}
pub fn caller(t: &dyn testTrait) {
let a = Arc::new(Mutex::new(t));
let clone = a.clone();
thread::spawn(move || {
if let Ok(mut x) = clone.lock() {
x.test(5);
}
});
}

Using a reference in this situation is probably the wrong choice, because a reference connects the lifetime of the thread with the calling function.
This problem is not specific to Rust, Rust just complains about it because Rust has a zero-undefined-behavior tolerance.
In C++, for example, it is undefined behavior:
#include <iostream>
#include <thread>
#include <chrono>
struct TestTrait {
virtual int test() = 0;
};
struct Foo : TestTrait {
int value;
int test() override { return value; }
};
int main() {
{
Foo x;
x.value = 10;
std::thread thrd([&x]() {
std::cout << x.test() << std::endl;
});
thrd.detach();
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return 0;
}
Segmentation fault
So as #PitaJ already points out, there are two solutions:
Make sure the thread doesn't outlive the main function by using std::thread::scope
Make the data reference counted via Arc
I assume you want to go the Arc route because you already started with that.
You can't place the reference in the Arc, though, you have to put the object itself into the Arc. The whole point of an Arc is to have multiple ownership, and for that, the Arc has to actually own the object, not borrow it.
Here is a possible solution with Arc:
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
pub trait TestTrait: Send + Sync {
fn test(&mut self, i: i32) -> i32;
}
pub struct FIFO {}
impl TestTrait for FIFO {
fn test(&mut self, i: i32) -> i32 {
return i;
}
}
impl FIFO {}
fn main() {
let fifo = Arc::new(Mutex::new(FIFO {}));
caller(Arc::clone(&fifo));
std::thread::sleep(Duration::from_millis(100));
}
pub fn caller(t: Arc<Mutex<impl TestTrait + 'static>>) {
thread::spawn(move || {
if let Ok(mut x) = t.lock() {
println!("{}", x.test(5));
}
});
}
5
Note that:
You need to use impl instead of dyn as this situation is too complex for trait objects
No need to use unsafe anywhere; in fact, you should never define Send and Sync manually, as they are derived automatically

Related

Borrow checker and shared I/O

I'm hitting a problem in my code where multiple structs need to send data to a shared output sink and the borrow checker doesn't like it.
struct SharedWriter {
count: u32,
}
impl SharedWriter {
pub fn write(&mut self) {
self.count += 1;
}
}
struct Container<'a> {
writer: &'a mut SharedWriter,
}
impl Container<'_> {
pub fn write(&mut self) {
self.writer.write();
}
}
pub fn test() {
let mut writer = SharedWriter { count: 0 };
let mut c0 = Container {
writer: &mut writer,
};
let mut c1 = Container {
// compiler chokes here with:
// cannot borrow `writer` as mutable more than once at a time
writer: &mut writer,
};
c0.write();
c1.write();
}
I understand the problem and why it's happening; you can't borrow something as mutable more than once at a time.
What I don't understand is a good general solution. This pattern happens a lot. You've got a common output sink, like a file or a socket or a database, and you want to feed multiple streams of data to it. It has to be mutable if it maintains any kind of state. It has to be just a single entity if it holds any resources.
You could pass a reference to the sink in every single write() method (write(&mut writer, some_data)), but this clutters the code and will get called (in my particular app) millions of times per second. I'm speculating that there is some extra overhead in passing this parameter over and over.
Is there some syntax that will get past this problem?
Interior mutability.
In your case the easiest way is probably to use RefCell. It will have some runtime overhead, but it is safe.
use std::cell::RefCell;
struct SharedWriter {
count: RefCell<u32>,
}
impl SharedWriter {
pub fn new(count: u32) -> Self {
Self { count: RefCell::new(count) }
}
pub fn write(&self) {
*self.count.borrow_mut() += 1;
}
}
If the data is Copy (like u32, in case this is your real data), you may want to use Cell. It is applicable to less types but zero-cost:
use std::cell::Cell;
struct SharedWriter {
count: Cell<u32>,
}
impl SharedWriter {
pub fn new(count: u32) -> Self {
Self { count: Cell::new(count) }
}
pub fn write(&self) {
self.count.set(self.count.get() + 1);
}
}
There are more interior mutability primitives (for example, UnsafeCell for zero-cost but unsafe access, or mutexes and atomics for thread safe mutation).
One option would be to use a channel. Here is an example of how that might look. This also has the added benefit of allowing you to scale across multiple threads with your io. It takes a handler which it runs in a loop on a new thread. It blocks until a value sent through the sender is received then calls func with a given value and a mutable reference to the handler. The thread exits when all the senders have been dropped. However one downside of this approach is the channel only works in one direction.
use std::sync::mpsc::{channel, Sender};
use std::thread::{self, JoinHandle};
pub fn create_shared_io<T, H, F>(mut handler: H, mut func: F) -> (JoinHandle<H>, Sender<T>)
where
T: 'static + Send,
H: 'static + Send,
F: 'static + FnMut(&mut H, T) + Send,
{
let (send, recv) = channel();
let join_handle = thread::spawn(move || loop {
let value = match recv.recv() {
Ok(v) => v,
Err(_) => break handler,
};
func(&mut handler, value);
});
(join_handle, send)
}
And then it can be used similarly to your example. Since no data was passed in your example, it sends () as a placeholder.
pub fn main() {
let writer = SharedWriter { count: 0 };
println!("Starting!");
let (join_handle, sender) = create_shared_io(writer, |writer, _| {
writer.count += 1;
println!("Current count: {}", writer.count);
});
let mut c0 = Container {
writer: sender.clone(),
};
let mut c1 = Container {
writer: sender,
};
c0.write();
c1.write();
// Ensure the senders are dropped before we join the io thread to avoid possible deadlock
// where the compiler attempts to drop these values after the join.
std::mem::drop((c0, c1));
// Writer is returned when the thread is joined
let writer = join_handle.join().unwrap();
println!("Finished!");
}
struct SharedWriter {
count: u32,
}
struct Container {
writer: Sender<()>,
}
impl Container {
pub fn write(&mut self) {
self.writer.send(()).unwrap();
}
}

How do I convert a concrete, statically dispatched `T: Trait` to a dynamically dispatched `dyn Trait`?

In this code, I have the skeleton of an observable system. The documentation on implementing Observable and other decoupling patterns are usually missing a final step on allowing access to the listener while also having it be &mut during the notification call, but that's what RefCell is intended to handle. So I have this code all worked out, but I'm having a last piece of trouble getting my concrete T into the &dyn Trait.
use std::{cell::RefCell, rc::Rc};
pub trait Observer {
fn notify(&mut self);
}
#[derive(Default)]
pub struct Subject<'s> {
listeners: Vec<Rc<RefCell<Box<dyn Observer + 's>>>>,
}
pub fn wrap<T>(t: T) -> Rc<RefCell<Box<T>>> {
Rc::new(RefCell::new(Box::new(t)))
}
impl<'s> Subject<'s> {
pub fn add_observer(&mut self, observer: Rc<RefCell<Box<dyn Observer + 's>>>) {
self.listeners.push(observer)
}
pub fn notify(&mut self) {
for listener in &mut self.listeners {
listener.borrow_mut().notify();
}
}
}
#[cfg(test)]
mod test {
use super::{wrap, Observer, Subject};
#[derive(Default)]
pub struct Counter {
count: usize,
}
impl Observer for Counter {
fn notify(&mut self) {
self.count += 1;
}
}
#[test]
fn it_observes() {
let mut subject = Subject::default();
let counter = wrap(Counter::default());
subject.add_observer(counter); // mismatched types
for i in 1..5 {
subject.notify();
subject.notify();
assert_eq!(counter.borrow().count, i * 2);
}
}
}
The full error is
error[E0308]: mismatched types
--> src/observer.rs:48:30
|
48 | subject.add_observer(counter);
| ^^^^^^^ expected trait object `dyn Observer`, found struct `Counter`
|
= note: expected struct `Rc<RefCell<Box<(dyn Observer + 'static)>>>`
found struct `Rc<RefCell<Box<Counter>>>
I've seen (what looks like) this pattern used in a number of contexts, but I can't tell what either I'm missing or doing differently.
How do I get the T: Trait out of its static dispatch and into dynamic dispatch?
You can take to boxing outside of your wrap function, and box the counter itself with the proper box type:
pub fn wrap<T>(t: T) -> Rc<RefCell<T>> {
Rc::new(RefCell::new(t))
}
...
let counter: Box<dyn Observer> = Box::new(Counter::default());
let counter = wrap(counter);
Playground
Btw, once you have the dynamic dispatch, you have an dyn Observer so you lose access to the Counter itself. You will have to take ownership of it and downcast the pointer to the concrete type again.

How do I get a static path for tokio::fs::File::open?

The tokio::fs::File::open(path: T + 'static) requires a 'static lifetime on its path parameter.
This makes sense because it is handled in runtime threads during the program's execution. I think it would make more sense if you could pass your own lifetimes, because the runtime does not need to run the whole time and so you could throw away some stuff. Do I understand something wrong?
I'd like to stay for 'static at the moment and so my problem is this...
I have a trait TraitN and some struct StructX { path: String, } with a fn new(path: &String) -> Box<TraitN>. The new creates and sets self.path = path.to_string();.
In some impl fn doit(&self) { ... } for StructX, I'd like to call tokio::fs::File::open(&self.path).
How can I pass &self.path with a 'static lifetime?
This is a complete example:
extern crate futures;
extern crate tokio;
#[macro_use]
extern crate error_chain;
use futures::future;
use futures::future::{loop_fn, ok, Future, Loop};
use futures::Stream;
use std::io::BufReader;
use tokio::{fs, io};
mod error {
error_chain!{}
}
use error::*;
type FutureResult<T> = future::FutureResult<T, Error>;
trait HandlerTrait {
fn new(path: &str) -> Box<HandlerTrait>
where
Self: Sized;
fn get_all(&self) -> FutureResult<Vec<String>>;
}
#[derive(Debug)]
pub struct Handler {
path: String,
}
impl HandlerTrait for Handler {
fn new(path: &str) -> Box<HandlerTrait> {
Box::new(Handler {
path: path.to_string(),
})
}
fn get_all(&self) -> FutureResult<Vec<String>> {
let file = fs::File::open(self.path.clone())
.and_then(|file: fs::File| ok(file))
.wait()
.unwrap();
let lines = io::lines(BufReader::new(file));
ok(lines
.filter(|line| line.len() > 80)
.map(|all| all[0..80].to_string())
.collect()
.wait()
.unwrap())
}
}
fn get_handler(path: &str) -> Option<Box<HandlerTrait>> {
Some(Handler::new(path))
}
fn get_path() -> FutureResult<String> {
ok("./somepath/file".to_string())
}
fn start_runtime() -> Result<()> {
let path: &str = get_path().wait().unwrap().as_str();
tokio::run(doit(path.clone()));
Ok(())
}
fn doit(path: &'static str) -> impl Future<Item = (), Error = ()> + 'static {
let n = 0;
loop_fn(n, move |_nr| {
let lh = get_handler(path).unwrap();
lh.get_all()
.or_else(|_| Err(()))
.and_then(|_all| ok(Loop::Break(())))
})
}
#[test]
fn test() {
start_runtime().unwrap();
assert!(true);
}
error[E0597]: borrowed value does not live long enough
--> src/lib.rs:63:22
|
63 | let path: &str = get_path().wait().unwrap().as_str();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
= note: borrowed value must be valid for the static lifetime...
playground
TL;DR Use a String instead of a &str. This might change when async / await syntax is stabilized.
Here's the MCVE I made of your original question:
extern crate tokio; // 0.1.11
trait TraitN {}
struct StructX {
path: String,
}
impl TraitN for StructX {}
fn new(path: &str) -> Box<TraitN> {
Box::new(StructX {
path: path.to_string(),
})
}
impl StructX {
fn doit(&self) {
tokio::fs::File::open(self.path.clone());
}
}
To solve this, clone the String and give ownership of it to the function:
impl StructX {
fn doit(&self) {
tokio::fs::File::open(self.path.clone());
}
}
With your example code, there are numerous problems:
fn start_runtime() -> Result<()> {
let path: &str = get_path().wait().unwrap().as_str();
tokio::run(doit(path.clone()));
Ok(())
}
You cannot take a reference to the result of unwrap because nothing will own that value. You cannot have a reference to this kind of temporary.
Cloning a &'a str returns a &'a str, not a String.
It doesn't make sense to call wait on the value because that blocks the thread. Run everything in the reactor loop.
This function should look like
fn start_runtime() -> Result<()> {
tokio::run({
get_path()
.map_err(|e| panic!("{}", e))
.and_then(|path| doit(path))
});
Ok(())
}
Then all of your code should switch to impl Into<String> instead of &str of &'static str. doit also needs to be able to create duplicate Strings:
fn doit(path: impl Into<String> + Clone) -> impl Future<Item = (), Error = ()> + 'static {
let n = 0;
let path = path.into();
loop_fn(n, move |_nr| {
let lh = get_handler(path.clone()).unwrap();
lh.get_all()
.or_else(|_| Err(()))
.and_then(|_all| ok(Loop::Break(())))
})
}
this [...] is config which doesn't change [...] read from a configfile during app init.
In that case, create a singleton which will give you an effectively-static value:
extern crate lazy_static; // 1.1.0
use lazy_static::lazy_static;
lazy_static! {
static ref PATH: String = {
// Should be read from a file.
String::from("/the/path/to/the/thing")
};
}
Then change all of the values to &'static str:
#[derive(Debug)]
pub struct Handler {
path: &'static str,
}
impl HandlerTrait for Handler {
fn new(path: &'static str) -> Box<HandlerTrait> {
Box::new(Handler {
path
})
}
}
And take a reference to the singleton:
fn start_runtime() -> Result<()> {
tokio::run(doit(&PATH));
Ok(())
}
You can couple this with phimuemue's answer to get a &'static MyConfigStruct, which could then have a fn foo(&'static self) that is available.
There must be something wrong with a language if this becomes so difficult and needs mem-io multiple times.
You are partially correct. It's difficult to have maximally performant async code with today's Rust (1.30) because Rust wants to ensure memory safety above all else. This doesn't mean that the code is unperformant, just that there's a bit of room to do better.
Honestly, making clones here is unlikely to be a performance bottleneck, but it is annoying. That's where async and await syntax comes in. This will allow futures to more easily make use of references in an idiomatic Rust manner.
because the runtime does not need to run the whole time [...] Do I understand something wrong?
However, async and await still might not help you, as by default Tokio will run your future on a different thread. That's one of the primary reasons it requires a 'static bound. This prevents a Tokio thread from having a reference to a stack local that goes out of scope, introducing memory unsafety. This isn't a unique problem to Tokio, however.
See also:
How can I pass a reference to a stack variable to a thread?
The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want
Why is the bound `T: 'a` required in order to store a reference `&'a T`?
Why does the Rust compiler request I constrain a generic type parameter's lifetime (error E0309)?
Other bits
It appears that every single call to wait in this code is a misuse of futures. You may wish to re-read the Tokio docs to better understand understand how you are supposed to chain futures. If there's a wait call, it's usually at the end of everything, and even that is rare when using Tokio.
See also:
How do I synchronously return a value calculated in an asynchronous Future in stable Rust?
You can restrict the lifetime of &self:
impl StructX {
fn doit(&'static self) {
// here, we know that self and its members are 'static
}
}
If you do this, you may actually be better off to have StructX store the 'static borrow of the path in the first place (instead of a string).
I can answer this now by myself:
fn make_static_str<T>(s: T) -> &'static str where T: Into<String>
A solution with Arc<Mutex<String>> - The test fails because there is no file to read on playground.

How do I globally store a trait object to make it accessible to a C API callback?

Suppose a C API which calls a callback before returning. Unfortunately, there is no way to send data to the callback except by global variables. There is only 1 thread, by the way.
To make this example compile, I've added a dummy implementation for it in Rust, the real thing is extern "C"
unsafe fn c_api(c_api_callback:extern fn()){
c_api_callback();
}
I want to encapsulate some state for this API
pub trait State {
fn called(&mut self); //c_api_callback should call this on self
}
In a generic way. Multiple independent implementations of State can exist
struct MyState {
value:i32
}
impl State for MyState{
fn called(&mut self){
println!("I hope this prints 123:{}", self.value);
}
}
pub fn main(){
let mut mystate = MyState { value: 123 };
do_call(&mut mystate);
}
The basic question: How do I implement what follows?
//rustc says: error: explicit lifetime bound required [E0228]
static static_state:* mut State=0 as *mut State;
//This doesn't work
//static static_state:*'static mut State=0 as *mut State;
//error: bare raw pointers are no longer allowed, you should likely use `*mut T`, but otherwise `*T` is now known as `*const T`
extern fn my_callback_impl(){
static_state.called();
}
pub fn do_call(state:&mut State){
static_state=state;
unsafe{
c_api(my_callback_impl);
}
static_state=0 as *mut State;
}
I tried all kinds of horrible workarounds, up to wrapping the trait in a struct and using transmute on it to cast it to *u8, and I have a nice collection of weird error messages and compiler crashes as a result.
As this is the second time I get confused by static in rust, I would also appreciate it if someone has some pointers to blogs or good example code clarifying what's going on here.
The 'static lifetime actually isn't too complicated - it simply denotes that something is guaranteed to live for the entire life of the program. In this case, a global value, by definition, needs to be available for that long.
A problem often occurs because people want to initialize that global value during runtime of the program, which means that it isn't available for the entire program.
Now, the meat of the problem. Solution presented with very little guarantee on how safe it is.
First, I think you are running into a bug that prevents you from directly storing the trait object. To work around that, we wrap the trait object in a little dummy struct (Holder) that gives the trait object somewhere to live.
Then, we stick the reference to the holder into the global, mutable, scary, location. Call the callback, and wham, presto, there it is!
use std::mem;
struct Holder<'a>(&'a mut (State + 'a)); //'
// You'd truly better never use this in multiple threads!
static mut static_state: *mut Holder<'static> = 0 as *mut _; //'
pub trait State {
fn called(&mut self);
}
struct MyState {
value: i32
}
impl State for MyState{
fn called(&mut self) {
println!("I hope this prints 123:{}", self.value);
}
}
unsafe fn c_api(c_api_callback: extern fn()) {
c_api_callback();
}
extern fn my_callback_impl() {
// really should check that it's not 0 here...
let h = unsafe { &mut *static_state };
h.0.called();
}
pub fn do_call(state: &mut State){
let h = Holder(state);
unsafe {
// Straight-up lie to the compiler: "yeah, this is static"
static_state = mem::transmute(&h);
c_api(my_callback_impl);
static_state = 0 as *mut _;
}
}
pub fn main(){
let mut mystate = MyState { value: 123 };
do_call(&mut mystate);
}

How to specify that method argument must have longer lifetime than self's lifetime?

I'd like to write a safe Rust wrapper for a C library. I need to express the C's library raw pointer ownership rules in Rust's terms.
The library has its private structure such as: struct handle {void *_data} and exposes setter as set_data(struct handle*, void *data).
I'd like to make Rust version of that method with signature that says "data must live at least as long as the handle".
I've tried:
set_data(&'a self, &'a data:…)
but borrow checker seems to apply that to lifetime within that function, not overall lifetime of the object.
I've also tried to add lifetime to impl, but that's still no good. Full test case:
#![allow(unused_variables)]
struct Handle<'a>;
impl<'a> Handle<'a> {
pub fn set_data(&'a mut self, data: &'a DropCanary) {
// save data raw ptr
}
pub fn use_data(&'a self) {
// use data raw ptr
println!("alive?");
}
}
fn main() {
let mut handle = Handle;
let long_enough_lifetime = DropCanary{label:"long"};
{
let short_lifetime = DropCanary{label:"short"};
handle.set_data(&short_lifetime); // This shouldn't be allowed!
handle.set_data(&long_enough_lifetime); // This is OK
}
handle.use_data();
}
/// --- just for testing ---
struct DropCanary {
label: &'static str,
}
impl Drop for DropCanary {
fn drop(&mut self) {
println!("dropped: {}", self.label);
}
}
The problem is that the following code compiles and outputs:
dropped: short
alive?
dropped: long
So it causes use-after-free, because Rust doesn't know that short_lifetime must outlive handle.
This should work for you (playpen).
The reason that your example compiles is simply because you do not use the lifetime inside the struct inside your example. Since you do not use it there is no constraint on it and it could just as well have been omitted. If you do not store any data with a lifetime inside your struct there is marker types which you can substitute for the Option I used here.
#![allow(unused_variables)]
struct Handle<'a>(Option<&'a DropCanary>);
impl<'a> Handle<'a> {
pub fn set_data(&mut self, data: &'a DropCanary) {
self.0 = Some(data);
// save data raw ptr
}
pub fn use_data(&self) {
// use data raw ptr
println!("alive?");
}
}
fn main() {
let mut handle = Handle(None);
let long_enough_lifetime = DropCanary{label:"long"};
{
let short_lifetime = DropCanary{label:"short"};
//handle.set_data(&short_lifetime); // This shouldn't be allowed!
handle.set_data(&long_enough_lifetime); // This is OK
}
handle.use_data();
}
/// --- just for testing ---
struct DropCanary {
label: &'static str,
}
impl Drop for DropCanary {
fn drop(&mut self) {
println!("dropped: {}", self.label);
}
}

Resources