On Wed, 10 Apr 2013 10:48:30 +0100, Henning Pohl <[email protected]>
wrote:
On Tuesday, 9 April 2013 at 20:46:12 UTC, Steven Schveighoffer wrote:
No. You are not allowed to access any GC-managed resources inside a
destructor.
Okay, I finally found it: http://dlang.org/class.html#destructors. But
it's not listed here: http://dlang.org/garbage.html.
And it won't be null, because that would be extremely costly. Consider
if 300 objects had a pointer to a block of memory. Now those 300
objects all go away. By your expectation, if the targeted block of
memory was collected, the GC would have to spend time going through all
those 300 pointers, setting them to null. When they are about to all
be destroyed. 99.99% of the time, that would be wasted, as those 300
objects may not even have destructors that care.
I thought about the case when the user sets the reference to null before
the destructor was even called.
A destructor is ONLY for destroying non-GC managed resources, nothing
else. Like an OS file handle for instance.
Imagine this case:
...
Any instance of B always needs to be destructed _before_ the A it's
connected to. How do you express this in D?
// External C library.
extern(C) {
alias void* a_handle;
alias void* b_handle;
a_handle create_a();
void destroy_a(a_handle);
b_handle create_b(a_handle);
void destroy_b(b_handle);
}
class A {
this() {
// An a_handle owns multiple b_handles.
handle = create_a();
}
~this() {
Dispose(false); // ** new **
}
private a_handle handle;
// ** new **
private bool _disposed;
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
if (disposing)
{
// Destroys all bs connected with this a.
destroy_a(handle);
}
}
}
}
class B {
this(A a) {
this.a = a;
// Creates a b_handle connected to the given a_handle.
handle = create_b(a.handle);
}
~this() {
Dispose(false); // **new **
}
private A a;
private b_handle handle;
// ** new **
private bool _disposed;
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
if (disposing)
{
destroy_b(handle);
a.Dispose(true); // or maybe not depending on how many b handles
there are?
}
}
}
}
Yes, your code will leak library handles/resources if you forget to
manually call Dispose() on (at least) the B above (assuming it calls
Dispose on its A). It's a pain and not perfect, but I don't think it can
be - in D - without compiler/GC support for a disposal pattern like this
(as C# has).
The disposal pattern in C# is not 'free' in terms of costs to the running
of the application though, it causes objects to survive a GC collection,
any they in turn keep other objects alive so there is a very real issue of
objects living longer and resource use being higher. Add to that, that
objects can be resurrected after the first collection, before the GC
finalizer calls Dispose and you have all sorts of tricky edge cases and
timing windows.
So, it's not without costs. But, in D, you can do the above and ensure
you call Dispose manually and at worst it's the same as manual memory
management in C/C++ for a smaller sub-set of your code making it more
manageable, but perhaps easier to forget and get wrong.
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/