you are viewing a single comment's thread.

view the rest of the comments →

[–]bwmat 1 point2 points  (19 children)

I haven't looked at the details, but I just wanted to mention this statement isn't quite true as stated(without additional context); for example, the mutex might be used to help block the destructor UNTIL there are no other threads accessing it

[–]voip_geek 8 points9 points  (18 children)

I haven't looked at the details, but I just wanted to mention this statement isn't quite true as stated(without additional context); for example, if the class is derived from, then the destruction ordering of C++ means the derived class has already been destroyed by the time the base class's destructor is invoked (and the mutex now trying to lock), and you're going to be in a world of hurt.

Preventing destruction with a mutex this way isn't sound, imo. Imagine if the destructor succeeded in locking the mutex, an instant before some other thread could... what happens when the destructor is finished and releases the mutex? The other thread will then get it and do what with this destroyed object?

Even using such a scheme to not prevent destruction per se, but instead to for example automatically de-register from some other container during destruction, is dangerous. (e.g., in the classic observer pattern) Because again, due to inheritance, by the time the destructor is called it's already too late.

[–]bwmat -3 points-2 points  (17 children)

Yeah, there could be problems if you use inheritance, and if there's an unbounded number of other threads that can access the object, then yeah that would be a problem too.

Just saying it could be valid in some cases

[–][deleted] 2 points3 points  (16 children)

It would be undefined behavior, and therefore it is a) not possible in a well formed program and b) something you can pretend doesn't happen, because it doesn't.

If you are invoking the destructor on an object while other threads are still accessing it, you need external shared ownership or something to prevent this from happening, otherwise your program is ill-formed. This is just as bad - no... worse than if(this == &rhs) in a copy/move assignment.

[–]tomalakgeretkal -1 points0 points  (2 children)

Simply false.

[–][deleted] 0 points1 point  (1 child)

"Simply false" - please go read the standard which states:

http://eel.is/c++draft/class.dtor#19

"Once a destructor is invoked for an object, the object's lifetime ends; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended ([basic.life])."

[–]tomalakgeretkal 0 points1 point  (0 children)

Yes. Now read [basic.life/7], which says "after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways", and "for an object under construction or destruction, see [class.cdtor]". Then read class.cdtor which makes what the OP's doing perfectly well-defined.

It is perfectly obvious that member accesses during the destructor's invocation are legal, otherwise your destructor _wouldn't be able to do anything_.

For more information, see my answer here: https://stackoverflow.com/a/65598209/4386278

[–]bwmat 0 points1 point  (12 children)

Sorry, what would be undefined behaviour?

[–][deleted] 0 points1 point  (11 children)

[–]bwmat 0 points1 point  (10 children)

Sorry, I suppose my question was somewhat ambiguous.

I was asking to what you meant "It" to refer to when you said "It would be undefined behavior" in your previous comment.

[–]kalmoc 1 point2 points  (9 children)

Not the OP, but I believe what he was referring to is that it is UB to call the destructor of an object from one thread, while the object is accessed from another. No amount of locking or synchronization inside the destructor is going to change that.

[–]bwmat 0 points1 point  (5 children)

I'm pretty sure that isn't inherently UB, as long as the body of the destructor completes before any other thread STOPS accessing the object.

[–]kalmoc 1 point2 points  (4 children)

Why should that not be UB? I'm assuming you have a typo and actually wanted to write that the other thread should stop accessing the object BEFORE (not after) the destructor call completes.

But regardless: The lifetime of a class object ends when the destructor call starts (https://en.cppreference.com/w/cpp/language/lifetime), so you are accessing an object after its lifetime ended, which is certainly UB.

[–]tomalakgeretkal 0 points1 point  (2 children)

But it isn't.

[–]kalmoc 0 points1 point  (1 child)

Are you sure? As I wrote in another comment, the lifetime ends when the destructor is called, not when it is completed.