You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Box-enclosed destructors can't safely access other boxes. #3039
Ran into this issue when trying to write a destructor for the doubly-linked-list. The following code segfaults after (i believe) one of the boxes has been annihilated and the other's destructor attempts to run.
The solution, I think, is for the box annihilator to run in two phases - one phase to run destructors, and then another to actually annihilate the boxes after that. (EDIT: this is a point of contention.)
class ticky_tacky {
let data: uint;
let mut neighbour: @little_box;
new(data: uint) { self.data = data; self.neighbour = @none; }
drop {
alt *self.neighbour {
some(other_box) {
#error["Neighbour's value is %u", other_box.data];
}
none { }
}
}
}
enum little_box { none, some(ticky_tacky) }
fn main() {
let box1: @little_box = @some(ticky_tacky(42));
let box2: @little_box = @some(ticky_tacky(31337));
alt check *box1 {
some(x) { x.neighbour = box2; }
}
alt check *box2 {
some(x) { x.neighbour = box1; }
}
fail;
}
No, this should not compile and it's not related to those bugs. Values with dtors should not have @ boxes in them, statically. They're supposed to be acyclic, precisely so they can run top-down. This is why resources were originally their own kind. Kind system bug.
Sorry, that sounds yell-y, I don't mean to be unduly harsh; just want to be completely clear that "running dtors in multiple passes" is how finalizers work, not dtors, and we've made a very explicit effort to avoid that up until now. If it's not working, that's a bug, not a "let's switch to finalizers" opportunity.
No problem. I'm trying to come up with a way that your proposal isn't quite complete, involving allocating new @-boxes inside of destructors, but I think it's solid.
I came up with crashing code that respects your proposed kind restriction, but it just ended up being another demonstration of why TLS deserves to be unsafe. (note that this code follows the "tls is supposedly safe if you don't use polymorphic key functions" rule.)
fn key(+_x: @ticky_tacky) { }
class ticky_tacky {
let data: uint;
new(data: uint) { self.data = data; }
drop unsafe {
do task::local_data_modify(key) |box| {
alt box {
some(x) { #error["My own data: %u", x.data]; }
none { fail ~"????" }
}
box
}
}
}
fn main() {
unsafe { task::local_data_set(key, @ticky_tacky(42)); }
fail;
}
However, if we forbid destructors in things that contain @-boxes, then the doubly-linked-list cannot possibly have its own auto-clean-up function. If a dlist goes out of scope with elements still inside, they'll stick around until the cycle collector runs (or until we add true GC to the language...).
There's already a dlist::clear() function for people to use to avoid this, but it'd be nicer if it could clear itself automatically.
OK, I think your proposal is right. I was able to break it with three canonical "funny businesses" - global state (TLS, above), captured closure environments, and existentials, and all three of them can be ruled out with the "no box" kind.
I'm not totally convinced it's no longer possible to write this program, but
I tried and got stuck because I couldn't match on the @-boxes with a ref mut
pattern. So I'm going to assume this is just not allowed anymore. If you can
port the example to trunk and get the same behavior, by all means reopen!
Oddly, if I switch the fail_unless line in the above code for error!("Neighbour's value is %u", data);, it does not segfault. I cannot figure out why. fail_unless is not itself tripping.
I looked into the mystery a bit more, and it turned into more of a mystery, enough that I'm not confident that my above code is a reproduction of the original bug here. But I note that in the other bug, the place where it segfaulted changed (here, it segfaults in glue_drop_2475).
Activity
Add issue number #3039 to dlist destructor
bblum commentedon Jul 26, 2012
Related: #2047 #910
graydon commentedon Jul 26, 2012
No, this should not compile and it's not related to those bugs. Values with dtors should not have @ boxes in them, statically. They're supposed to be acyclic, precisely so they can run top-down. This is why resources were originally their own kind. Kind system bug.
graydon commentedon Jul 26, 2012
Sorry, that sounds yell-y, I don't mean to be unduly harsh; just want to be completely clear that "running dtors in multiple passes" is how finalizers work, not dtors, and we've made a very explicit effort to avoid that up until now. If it's not working, that's a bug, not a "let's switch to finalizers" opportunity.
bblum commentedon Jul 26, 2012
No problem. I'm trying to come up with a way that your proposal isn't quite complete, involving allocating new @-boxes inside of destructors, but I think it's solid.
I came up with crashing code that respects your proposed kind restriction, but it just ended up being another demonstration of why TLS deserves to be unsafe. (note that this code follows the "tls is supposedly safe if you don't use polymorphic key functions" rule.)
bblum commentedon Jul 27, 2012
However, if we forbid destructors in things that contain @-boxes, then the doubly-linked-list cannot possibly have its own auto-clean-up function. If a dlist goes out of scope with elements still inside, they'll stick around until the cycle collector runs (or until we add true GC to the language...).
There's already a
dlist::clear()function for people to use to avoid this, but it'd be nicer if it could clear itself automatically.bblum commentedon Jul 27, 2012
OK, I think your proposal is right. I was able to break it with three canonical "funny businesses" - global state (TLS, above), captured closure environments, and existentials, and all three of them can be ruled out with the "no box" kind.
Crashing code with a @-closure: http://pastebin.mozilla.org/1716353
And crashing code with an existential: http://pastebin.mozilla.org/1716358
One question still: Will the "no-box" kind be equivalent to "send", or will it be able to permit region pointers too?
bblum commentedon Jul 27, 2012
these crashes still happen when the 'fail' statement is removed, which convinces me that the box annihilator is orthogonal to the problem.
(comments only) Mention unsafe TLS destructor use (#3039)
Undo dlist's destructor-involving type structure in light of #3039
catamorphism commentedon Mar 21, 2013
I'm not totally convinced it's no longer possible to write this program, but
I tried and got stuck because I couldn't match on the @-boxes with a
ref mutpattern. So I'm going to assume this is just not allowed anymore. If you can
port the example to trunk and get the same behavior, by all means reopen!
bblum commentedon Mar 23, 2013
bblum commentedon Mar 23, 2013
Oddly, if I switch the
fail_unlessline in the above code forerror!("Neighbour's value is %u", data);, it does not segfault. I cannot figure out why.fail_unlessis not itself tripping.bblum commentedon Mar 23, 2013
I looked into the mystery a bit more, and it turned into more of a mystery, enough that I'm not confident that my above code is a reproduction of the original bug here. But I note that in the other bug, the place where it segfaulted changed (here, it segfaults in
glue_drop_2475).thestinger commentedon Mar 29, 2013
Is this fully fixed by d4fee24?
Auto merge of rust-lang#3039 - RalfJung:catch_panic, r=RalfJung
Bump Kani version to 0.47.0 (rust-lang#3039)
'Fill match arms' should work with existing match arms
Merge rust-lang#3623
Merge pull request rust-lang#3039 from rust-lang/tshepang/misc