-
Notifications
You must be signed in to change notification settings - Fork 7
Use vector for dtors in thread and remove defer() #32
Conversation
ghost
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice cleanup!
|
Before merging it, I'd like to add tests first. While I'm quite confident it's correct, but my feeling is often wrong... :) |
|
Some changes:
@stjepang May I ask if you could review it again? Sorry for sneaking in an approved PR... |
oliver-giersch
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some improvement suggestions, nothing critical
src/thread.rs
Outdated
| fn drop_all(&mut self) -> thread::Result<()> { | ||
| let mut ret = Ok(()); | ||
| while let Some(dtor) = DtorChain::pop(&mut self.dtors.borrow_mut()) { | ||
| let dtors = mem::replace(&mut *self.dtors.borrow_mut(), Vec::new()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest to use for dtor in self.dtors.borrow_mut().drain(..) { instead (more expressive/idiomatic).
Alternatively self.dtors.borrow_mut().clear() could be called after the loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now this PR uses drain(..) :) Thanks for suggestion!
src/thread.rs
Outdated
| pub struct Scope<'a> { | ||
| /// The list of the deferred functions and thread join jobs. | ||
| dtors: RefCell<Option<DtorChain<'a, thread::Result<()>>>>, | ||
| dtors: RefCell<Vec<Box<FnBox<thread::Result<()>> + 'a>>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively RefCell<Vec<Box<dyn FnBox<thread::Result<()>> + 'a>>> could be used here (I believe bare trait objects are going to be deprecated at some point),
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to switch to dyn Trait when we're starting to support Rust 2018. Probably we have other lint warnings for Rust 2018, and I want to remove them at once. What do you think?
|
For consistency, the vector should probably be reverse iterated during |
|
What should the following do? catch_unwind(AssertUnwindSafe(|| {
crossbeam::scope(|scope| {
scope.defer(|| panic!());
panic!();
});
})I'd say "catch the second panic and execute the deferred function", but this will currently abort the process. The reason is that the deferred function is executed while we're already unwinding (inside To solve this problem, I suggest removing the // Executes the scoped function.
let result = catch_unwind(AssertUnwindSafe(|| f(&scope)));
scope.drop_all();
let ret = result?;Can we add the example above as a test to make sure double panics don't abort the process?
I agree, but... What is going to happen once we allow borrowing |
|
@stjepang I agree with you: there should be no order between thread joins and deferred functions, and After thinking of this issue a little bit more, I finally agree with you in removing Maybe we can provide this deferred function container and logger as a companion to the |
|
@stjepang @oliver-giersch I updated this PR as follows:
I think it's ready to be merged. Would you please give another round of reviews? Thanks! |
src/thread.rs
Outdated
| let result = panic::catch_unwind(panic::AssertUnwindSafe(|| f(&scope))); | ||
|
|
||
| // Joins all the threads. | ||
| scope.drop_all(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point, it'd probably make sense to rename drop_all to join_all (nothing is dropped, really - we just join threads).
src/thread.rs
Outdated
| fn drop(&mut self) { | ||
| // Actually, there should be no deferred functions left to be run. | ||
| self.drop_all().unwrap(); | ||
| // Note that `self.dtors` can be non-empty when the code inside a `scope()` panics and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment needs to be updated. self.dtors doesn't exist anymore.
|
|
||
| // Joins all the threads. | ||
| scope.drop_all(); | ||
| let panic = scope.panics.borrow_mut().pop(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before this line, we could do:
let result = match result {
Ok(r) => r,
Err(e) => panic::resume_unwind(e),
};And return the result as Ok(result) at the end of function.
This way we only catch panics inside spawned threads, not inside the scope itself.
Do you agree with this, or do you think we should catch panics inside the scope, too? I'm personally not 100% sure yet...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. I'm also not 100% sure. So let's look at how rayon does: https://docs.rs/rayon/1.0.2/rayon/fn.scope.html#panics "If a panic occurs, either in the closure given to scope() or in any of the spawned jobs, that panic will be propagated and the call to scope() will panic. If multiple panics occurs, it is non-deterministic which of their panic values will propagate. Regardless, once a task is spawned using scope.spawn(), it will execute, even if the spawning task should later panic. scope() returns once all spawned jobs have completed, and any panics are propagated at that point."
Basically rayon doesn't distinguish panics inside scope() and panics of the spawned threads. I'd like to follow the same thing: either (1) propagating a panic, as rayon does, or (2) returning a panic as Err, but not mixing two schemes. We've decided to return thread panics as Err, but we've not released it and we're free to go back to the old behavior (propagating it). An advantage of it is it's behavior/signature is matching with rayon.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, following whatever Rayon does makes sense.
I'll also ask one more person for opinion, prz on IRC, who's been following recent developments in crossbeam-utils.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So there are a number of differences between scopes in crossbeam and rayon:
- In crossbeam, spawned threads can return a result. In rayon, they can't.
- In crossbeam, spawned threads can be manually joined. In rayon, they can't.
- In crossbeam, panics from spawned threads can be handles using
.join(). In rayon, they can't. - In crossbeam, panics from unjoined threads can be handled as the
Resultreturned fromscope. In rayon,scopesimply propagates all panics and returns aT, notResult.
Basically, it looks like in rayon all panics are propagated, while in crossbeam panics tend to be caught and handled manually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree -- let's return Result instead of propagating panic. I'm merging it. Thanks!
ghost
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I'm happy with this PR. Feel free to merge. 👍
I replaced a custom list with
Vecand corrected comments.Fixes the point 1 of #29
Fixes the point 3 of #29
Closes #33 (actually using vector for dtors was intertwined with removing defer(); sorry for making a duplicated PR.)
cc @oliver-giersch @stjepang