How to join thread in drop function? [duplicate] - rust

I want to create a thread inside of the new method and stop it after the struct is destroyed:
use std::thread;
struct Foo {
handle: thread::JoinHandle<()>,
}
impl Foo {
pub fn new(name: &str) -> Foo {
let name = name.to_string();
Foo {
handle: thread::spawn(move || {
println!("hi {}", name);
}),
}
}
pub fn stop(&mut self) {
self.handle.join();
}
}
fn main() {
let mut foo = Foo::new("test");
foo.stop();
}
This doesn't compile, and I can not understand why:
error[E0507]: cannot move out of borrowed content
--> <anon>:15:9
|
15 | self.handle.join();
| ^^^^ cannot move out of borrowed content
And in newer versions of Rust:
error[E0507]: cannot move out of `self.handle` which is behind a mutable reference
--> src/main.rs:17:9
|
17 | self.handle.join();
| ^^^^^^^^^^^ move occurs because `self.handle` has type `std::thread::JoinHandle<()>`, which does not implement the `Copy` trait
How can I fix this error?
In the future, I will implement Drop for Foo, and will call stop() from drop().

The function signature of JoinHandle::join is:
fn join(self) -> Result<T>
This means that the method takes self (the receiver object) by values (taking the ownership/consuming it). But you only have a borrow to your JoinHandle; a mutable one, but still merely a borrow, not the ownership. Thus you can't call this method, because you can't move the ownership out of your borrow into this join() method.
An easy way to fix that, is by accepting self by value in the stop() method, too:
pub fn stop(self) {
self.handle.join();
}
But you will notice that this isn't possible when implementing Drop, because drop() has the signature fn drop(&mut self)! Bummer! But there is a little trick you can use, described below. Please be aware that joining threads in drop() is probably not a good idea! Read Matthieu M.'s answer for more information on that!
If you still think, for whatever reason, that you really want to join a thread in drop(), you can store the JoinHandle in an Option<T> to save whether or not it's already joined. If you have a Some(T) you can obtain a T (by value!) from it by using the method Option::take(). Then you can write:
fn drop(&mut self) {
// `self.handle` has the type `Option<JoinHandle<()>>` here!
if let Some(handle) = self.handle.take() {
handle.join().expect("failed to join thread");
}
}

Don't.
It may seem counter-intuitive, but joining a thread (or process) in a destructor is generally a bad idea.
Note that asking to join does not cause the thread to stop by itself; it's just about waiting for the thread to stop, and the thread may not. There are multiple reasons why this could happen:
the handle being on the same thread as the one it controls,
the thread to be stopped waiting on a channel the current thread should send a signal on,
...
Yes, that's a deadlock. An implicit deadlock.
A particular nasty situation is if your current thread panics (something unforeseen occurred). Unwinding starts... and blocks! And all the resources that this thread was supposed to clean-up hung in limbo, forever.
A better design is to instead create an explicit join method, which consume self (by value). It also lets you return a Result, in case joining causes an error.
And in order for your users to remember to join explicitly, panic! in the Drop implementation if they forgot to.
That is:
impl Foo {
fn join(mut self) -> std::thread::Result<()> {
match self.handle.take() {
Some(h) => h.join(),
None => Ok(()),
}
}
}
impl Drop for Foo {
fn drop(&mut self) {
if self.handle.is_some() {
panic!("You MUST call either join on `Foo` to clean it up.");
}
}
}
Note: I am aware that panicking in destructors is controversial, however it is much safer to abort a process when it's in an unknown state that go on and hope for the best.
If you really, despite my warning, want to shoot yourself in the foot, join in drop.

The problem in join signature:
fn join(self) -> Result<T>
so to fix your code, you need something like:
pub fn stop(self) {
self.handle.join();
}

If you don't mind unsafe code, then here's something you could do (please look at Matthieus answer why this can be a bad idea).
struct Foo {
handle: ManuallyDrop<thread::JoinHandle<()>>,
}
impl Drop for Foo {
fn drop(&mut self) {
unsafe {
let _ = ManuallyDrop::take(&mut self.handle).join();
}
}
}

Related

Can I mark an immutable borrow as exclusive in Rust?

I have a data structure, that is somewhat like a RwLock. It is not re-entrant, but the actual locking mechanism is a const function. Is there any way that I can mark this function as "exclusive_borrow" without switching it to be be a mutable function. That way multiple calls to 'read' will be caught at compile time instead of panicking.
struct MyRwLock<T> {
t: T,
}
impl MyRwLock {
// Works fine, but doesn't enforce on compile time that there is
// only 1 Guard.
pub fn read(&self) -> ReadGuard<'_, T> { ... }
// Enforces only 1 ReadGuard at compile time, but unnecessarily
// requires MyMutex to be mutable to read.
pub fn mut_read(&mut self) -> ReadGuard<'_, T> { ... }
}
&mut is a bit of a misnomer. It actually means exclusive reference, not mutable. If that's what you want, then it's correct to use &mut.
In fact, there was a proposal to rename &mut back in 2014. It never came through, but you might occasionally hear whispers of the "mutpocalypse" today.

What happens to the ownership of a value returned but not assigned by the calling function?

Consider the following Rust code, slightly modified from examples in The Book.
I'm trying to understand what happens to the value in the second running of function dangle() in the main() function (see comment). I would imagine that because the value isn't assigned to any owner, it gets deallocated, but I've so far failed to find information to confirm that. Otherwise, I would think that calling dangle() repeatedly would constantly allocate more memory without deallocating it. Which is it?
fn main() {
// Ownership of dangle()'s return value is passed to the variable `thingamabob`.
let thingamabob = dangle();
// No ownership specified. Is the return value deallocated here?
dangle();
println!("Ref: {}", thingamabob);
}
fn dangle() -> String {
// Ownership specified.
let s = String::from("hello");
// Ownership is passed to calling function.
s
}
When a value has no owner (is not bound to a variable) it goes out of scope. Values that go out of scope are dropped. Dropping a value frees the resources associated with that value.
Anything less would lead to memory leaks, which would be a poor idea in a programming language.
See also:
Is it possible in Rust to delete an object before the end of scope?
How does Rust know whether to run the destructor during stack unwind?
Does Rust free up the memory of overwritten variables?
In your example, the second call creates an unnamed temporary value whose lifetime ends immediately after that one line of code, so it goes out of scope (and any resources are reclaimed) immediately.
If you bind the value to a name using let, then its lifetime extends until the end of the current lexical scope (closing curly brace).
You can explore some of this yourself by implementing the Drop trait on a simple type to see when its lifetime ends. Here's a small program I made to play with this (playground):
#[derive(Debug)]
struct Thing {
val: i32,
}
impl Thing {
fn new(val: i32) -> Self {
println!("Creating Thing #{}", val);
Thing { val }
}
fn foo(self, val: i32) -> Self {
Thing::new(val)
}
}
impl Drop for Thing {
fn drop(&mut self) {
println!("Dropping {:?}", self);
}
}
pub fn main() {
let _t1 = Thing::new(1);
Thing::new(2); // dropped immediately
{
let t3 = Thing::new(3);
Thing::new(4).foo(5).foo(6); // all are dropped, in order, as the next one is created
println!("Doing something with t3: {:?}", t3);
} // t3 is dropped here
} // _t1 is dropped last

Can a type know when a mutable borrow to itself has ended?

I have a struct and I want to call one of the struct's methods every time a mutable borrow to it has ended. To do so, I would need to know when the mutable borrow to it has been dropped. How can this be done?
Disclaimer: The answer that follows describes a possible solution, but it's not a very good one, as described by this comment from Sebastien Redl:
[T]his is a bad way of trying to maintain invariants. Mostly because dropping the reference can be suppressed with mem::forget. This is fine for RefCell, where if you don't drop the ref, you will simply eventually panic because you didn't release the dynamic borrow, but it is bad if violating the "fraction is in shortest form" invariant leads to weird results or subtle performance issues down the line, and it is catastrophic if you need to maintain the "thread doesn't outlive variables in the current scope" invariant.
Nevertheless, it's possible to use a temporary struct as a "staging area" that updates the referent when it's dropped, and thus maintain the invariant correctly; however, that version basically amounts to making a proper wrapper type and a kind of weird way to use it. The best way to solve this problem is through an opaque wrapper struct that doesn't expose its internals except through methods that definitely maintain the invariant.
Without further ado, the original answer:
Not exactly... but pretty close. We can use RefCell<T> as a model for how this can be done. It's a bit of an abstract question, but I'll use a concrete example to demonstrate. (This won't be a complete example, but something to show the general principles.)
Let's say you want to make a Fraction struct that is always in simplest form (fully reduced, e.g. 3/5 instead of 6/10). You write a struct RawFraction that will contain the bare data. RawFraction instances are not always in simplest form, but they have a method fn reduce(&mut self) that reduces them.
Now you need a smart pointer type that you will always use to mutate the RawFraction, which calls .reduce() on the pointed-to struct when it's dropped. Let's call it RefMut, because that's the naming scheme RefCell uses. You implement Deref<Target = RawFraction>, DerefMut, and Drop on it, something like this:
pub struct RefMut<'a>(&'a mut RawFraction);
impl<'a> Deref for RefMut<'a> {
type Target = RawFraction;
fn deref(&self) -> &RawFraction {
self.0
}
}
impl<'a> DerefMut for RefMut<'a> {
fn deref_mut(&mut self) -> &mut RawFraction {
self.0
}
}
impl<'a> Drop for RefMut<'a> {
fn drop(&mut self) {
self.0.reduce();
}
}
Now, whenever you have a RefMut to a RawFraction and drop it, you know the RawFraction will be in simplest form afterwards. All you need to do at this point is ensure that RefMut is the only way to get &mut access to the RawFraction part of a Fraction.
pub struct Fraction(RawFraction);
impl Fraction {
pub fn new(numerator: i32, denominator: i32) -> Self {
// create a RawFraction, reduce it and wrap it up
}
pub fn borrow_mut(&mut self) -> RefMut {
RefMut(&mut self.0)
}
}
Pay attention to the pub markings (and lack thereof): I'm using those to ensure the soundness of the exposed interface. All three types should be placed in a module by themselves. It would be incorrect to mark the RawFraction field pub inside Fraction, since then it would be possible (for code outside the module) to create an unreduced Fraction without using new or get a &mut RawFraction without going through RefMut.
Supposing all this code is placed in a module named frac, you can use it something like this (assuming Fraction implements Display):
let f = frac::Fraction::new(3, 10);
println!("{}", f); // prints 3/10
f.borrow_mut().numerator += 3;
println!("{}", f); // prints 3/5
The types encode the invariant: Wherever you have Fraction, you can know that it's fully reduced. When you have a RawFraction, &RawFraction, etc., you can't be sure. If you want, you may also make RawFraction's fields non-pub, so that you can't get an unreduced fraction at all except by calling borrow_mut on a Fraction.
Basically the same thing is done in RefCell. There you want to reduce the runtime borrow-count when a borrow ends. Here you want to perform an arbitrary action.
So let's re-use the concept of writing a function that returns a wrapped reference:
struct Data {
content: i32,
}
impl Data {
fn borrow_mut(&mut self) -> DataRef {
println!("borrowing");
DataRef { data: self }
}
fn check_after_borrow(&self) {
if self.content > 50 {
println!("Hey, content should be <= {:?}!", 50);
}
}
}
struct DataRef<'a> {
data: &'a mut Data
}
impl<'a> Drop for DataRef<'a> {
fn drop(&mut self) {
println!("borrow ends");
self.data.check_after_borrow()
}
}
fn main() {
let mut d = Data { content: 42 };
println!("content is {}", d.content);
{
let b = d.borrow_mut();
//let c = &d; // Compiler won't let you have another borrow at the same time
b.data.content = 123;
println!("content set to {}", b.data.content);
} // borrow ends here
println!("content is now {}", d.content);
}
This results in the following output:
content is 42
borrowing
content set to 123
borrow ends
Hey, content should be <= 50!
content is now 123
Be aware that you can still obtain an unchecked mutable borrow with e.g. let c = &mut d;. This will be silently dropped without calling check_after_borrow.

Joining a thread in a method that takes `&mut self` (like drop) results in "cannot move out of borrowed content"

I want to create a thread inside of the new method and stop it after the struct is destroyed:
use std::thread;
struct Foo {
handle: thread::JoinHandle<()>,
}
impl Foo {
pub fn new(name: &str) -> Foo {
let name = name.to_string();
Foo {
handle: thread::spawn(move || {
println!("hi {}", name);
}),
}
}
pub fn stop(&mut self) {
self.handle.join();
}
}
fn main() {
let mut foo = Foo::new("test");
foo.stop();
}
This doesn't compile, and I can not understand why:
error[E0507]: cannot move out of borrowed content
--> <anon>:15:9
|
15 | self.handle.join();
| ^^^^ cannot move out of borrowed content
And in newer versions of Rust:
error[E0507]: cannot move out of `self.handle` which is behind a mutable reference
--> src/main.rs:17:9
|
17 | self.handle.join();
| ^^^^^^^^^^^ move occurs because `self.handle` has type `std::thread::JoinHandle<()>`, which does not implement the `Copy` trait
How can I fix this error?
In the future, I will implement Drop for Foo, and will call stop() from drop().
The function signature of JoinHandle::join is:
fn join(self) -> Result<T>
This means that the method takes self (the receiver object) by values (taking the ownership/consuming it). But you only have a borrow to your JoinHandle; a mutable one, but still merely a borrow, not the ownership. Thus you can't call this method, because you can't move the ownership out of your borrow into this join() method.
An easy way to fix that, is by accepting self by value in the stop() method, too:
pub fn stop(self) {
self.handle.join();
}
But you will notice that this isn't possible when implementing Drop, because drop() has the signature fn drop(&mut self)! Bummer! But there is a little trick you can use, described below. Please be aware that joining threads in drop() is probably not a good idea! Read Matthieu M.'s answer for more information on that!
If you still think, for whatever reason, that you really want to join a thread in drop(), you can store the JoinHandle in an Option<T> to save whether or not it's already joined. If you have a Some(T) you can obtain a T (by value!) from it by using the method Option::take(). Then you can write:
fn drop(&mut self) {
// `self.handle` has the type `Option<JoinHandle<()>>` here!
if let Some(handle) = self.handle.take() {
handle.join().expect("failed to join thread");
}
}
Don't.
It may seem counter-intuitive, but joining a thread (or process) in a destructor is generally a bad idea.
Note that asking to join does not cause the thread to stop by itself; it's just about waiting for the thread to stop, and the thread may not. There are multiple reasons why this could happen:
the handle being on the same thread as the one it controls,
the thread to be stopped waiting on a channel the current thread should send a signal on,
...
Yes, that's a deadlock. An implicit deadlock.
A particular nasty situation is if your current thread panics (something unforeseen occurred). Unwinding starts... and blocks! And all the resources that this thread was supposed to clean-up hung in limbo, forever.
A better design is to instead create an explicit join method, which consume self (by value). It also lets you return a Result, in case joining causes an error.
And in order for your users to remember to join explicitly, panic! in the Drop implementation if they forgot to.
That is:
impl Foo {
fn join(mut self) -> std::thread::Result<()> {
match self.handle.take() {
Some(h) => h.join(),
None => Ok(()),
}
}
}
impl Drop for Foo {
fn drop(&mut self) {
if self.handle.is_some() {
panic!("You MUST call either join on `Foo` to clean it up.");
}
}
}
Note: I am aware that panicking in destructors is controversial, however it is much safer to abort a process when it's in an unknown state that go on and hope for the best.
If you really, despite my warning, want to shoot yourself in the foot, join in drop.
The problem in join signature:
fn join(self) -> Result<T>
so to fix your code, you need something like:
pub fn stop(self) {
self.handle.join();
}
If you don't mind unsafe code, then here's something you could do (please look at Matthieus answer why this can be a bad idea).
struct Foo {
handle: ManuallyDrop<thread::JoinHandle<()>>,
}
impl Drop for Foo {
fn drop(&mut self) {
unsafe {
let _ = ManuallyDrop::take(&mut self.handle).join();
}
}
}

Borrowing reference in structure

I'm trying to put in one structure information about inotify events and hashmap with inotify watch id as key and name of the file as value.
extern crate inotify;
use inotify::INotify;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
struct Notificator {
inotify: INotify,
watch_service: Arc<Mutex<HashMap<inotify::wrapper::Watch, Arc<String>>>>,
}
impl Notificator {
pub fn new() -> Notificator {
Notificator {
inotify: INotify::init().unwrap(),
watch_service: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn resolving_events(&mut self) {
{
let mut events = self.check_for_events();
self.events_execution(events);
}
}
fn check_for_events(&mut self) -> &[inotify::wrapper::Event] {
self.inotify.available_events().unwrap()
}
fn events_execution(&self, events: &[inotify::wrapper::Event]) {
for event in events.iter() {
}
}
}
During compilation I am receiving an error
src/main.rs:204:13: 204:17 error: cannot borrow `*self` as immutable because it is also borrowed as mutable [E0502]
src/main.rs:204 self.events_execution(events);
I thought the best solution would be to separate somehow inotify variable in Notificator structure with watch_service, but I can't dereference self.check_for_events(); because I receive
src/main.rs:203:17: 203:27 error: the trait bound `[inotify::wrapper::Event]: std::marker::Sized` is not satisfied [E0277]
src/main.rs:203 let mut events = *self.check_for_events();
I understand the core of the problem: I'm trying to borrow reference by check_for_events and then using it as parameter in events_execution which also requires self as parameter, but I have no idea how to resolve it.
One thing you could do, although it's not very elegant, is to have your mutable method consume its mutable borrow and return an immutable one that you can then use:
pub fn resolving_events(&mut self) {
let (slf, events) = self.check_for_events();
slf.events_execution(events);
}
fn check_for_events(&mut self) -> (&Self, &[inotify::wrapper::Event]) {
let events = self.inotify.available_events().unwrap();
(&self, events)
}
I've made a small proof-of-concept on the playground (using vecs of u64 as the mutable state, but the principle is similar). It might be cleaner to refactor your code so that some external client can (mutably) borrow the Notifier, produce the events, release the borrow, and borrow it (immutably) to process them...
This is a known issue in the borrow checker (more discussion on internals). You cannot have a function taking a &mut T to an object and returning a &T without losing the ability to access the object again until the &T goes out of scope. You can't work around it due to the way inotify is implemented.
But you can ask the inotify authors to create a get_available_notifications method that doesn't fetch new data. This way you can call available_notifications once and drop the returned value. Then call get_available_notifications (which doesn't take &mut INotify, but just &INotify) and work from there.

Resources