-
-
Notifications
You must be signed in to change notification settings - Fork 380
Clarify scope of application of invariants #851
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clarify scope of application of invariants #851
Conversation
Specifically, explain why it is not OK to refer to other GC managed objects from invariant.
See also http://forum.dlang.org/post/ossuvfmqthllgdpgzntm@forum.dlang.org for an example of confusion current docs cause. |
I think this is a crappy reason which will massively restrict invariant usefulness. The definition of when an object is valid is, in practice, almost always in relation to other heap-allocated objects. For example, this is supposedly invalid: class Account
{
Currency balance;
Transaction[] transactions;
invariant()
{
assert(balance == transactions.map!(t => t.debit).sum);
}
} You say that the core reason is that the GC might destroy objects in arbitrary order. But if those objects don't themselves have destructors, then their state is actually valid, as the GC does not clobber freed objects' memory until it has finished running all destructors. I've heard the argument that the above is just how the current implementation works, and should not be solidified into the language, as it might constrain future GC implementations. I'm not sure I agree, since with the current implementation, things are still memory-safe, no matter what you do in the destructor. Sure, you are allowed to access an object whose destructor has run and is now logically invalid, but that doesn't jeopardize memory safety. I think we should go into the opposite direction and guarantee memory safety inside a destructor (with the remaining caveat that finalization order is arbitrary). And as for the invariant getting called after the destructor... well, that's a bug. I checked TDPL, and even though it doesn't say much about invariants accessing other objects, it is explicit in that the invariant does not run after the destructor. @andralex What do you think? |
My intent was to simply document existing situation and avoid awkward surprises for newcomers. If official spec and/or implementation will change (intentionally or by fixing the supposed bug), it should be changed too. |
I am personally OK with approach of not calling the invariants in the middle of collection cycle. But I also have not ever written a single invariant that uses other GC objects either. |
@@ -181,6 +181,13 @@ class Date | |||
$(LI postconditions) | |||
) | |||
|
|||
$(P Because at the start of class/struct destructor object instance may | |||
point to other object instances already collected by the GC trying to |
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 needs a comma after "GC" so it doesn't parse as "... instances collected by [the GC trying to check...]".
It's really good to document the situation!
I'd want it to be plainly obvious what counts as "internal consistency". The spec on destructors says:
That means an [Separately, the situation is a nasty surprise. Should the collector skip calling the |
Specifically, explain why it is not OK to refer to
other GC managed objects from invariant.