The following gives me an Access Violation on windows 32-bit, dmd.2.052, no flags. When the destructor is run by the garbage collector, the message box seems to be corrupted in the process.
import std.stdio;
import core.thread;
import core.memory;
import std.concurrency;
class C
{
string m_str;
Tid m_receiverTid;
this(string s, Tid rt) { this.m_str = s; this.m_receiverTid = rt; }
~this() { writeln("Destructor : ",this.m_str);
m_receiverTid.send(this.m_str);
}
}
void receiver() {
try {
while(true) {
receive((string s){writeln("Received: ",s);});
}
} catch (Throwable th) {
writeln("Caught throwable: ",th.toString());
}
}
void main() {
Tid receiverTid = spawn(&receiver);
receiverTid.send("Basic test");
Thread.sleep( 5_000_000 );
C c1 = new C("c1 Manually deleted",receiverTid);
delete c1;
Thread.sleep( 5_000_000 );
{
C c2 = new C("c2 Garbage collected",receiverTid);
}
writeln("Running garbage collector..."); // This line needed to flush out the c2 root pointer.
GC.collect();
Thread.sleep( 5_000_000 );
writeln("Exiting main thread...");
}
The above produces:
Received: Basic test
Destructor : c1 Manually deleted
Received: c1 Manually deleted
Running garbage collector...
Destructor : c2 Garbage collected
Received: c2 Garbage collected
Caught throwable: object.Error: Access Violation
Exiting main thread...
Are there any workarounds for this?
Is there a way for destructor code to know if it is being invoked by the GC or not?
Is message passing from a destructor intrinsically unsafe, e.g. if non-GC threads are frozen by the GC whilst they have a mutex lock on a shared message box then the GC might deadlock if it sends to a locked message box? Or does destructor code only take place in a sweep cycle after all threads are thawed?
Is it safe for a destructor to reference thread local storage, e.g. could the GC sweep cycle be in a different thread?
Destructors are methods inside a class that's sole purpose is to destroy the instance of that class when they are no longer needed. The destructor is called implicitly by the Garbage Collector of the . NET framework.
The destructor is a special member function which is invoked when an object is destroyed. It is the last method run by a class. The garbage collector is part of the framework, automatically manages memory, and non-deterministically collects unreferenced objects to avoid memory leaks.
The difference between the garbage collector and destructor is that a garbage collector is a software that performs automatic memory management while a destructor is a special method called by the garbage collector during the destruction of the object.
__del__ is a destructor method which is called as soon as all references of the object are deleted i.e when an object is garbage collected. Example: Here is the simple example of destructor. By using del keyword we deleted the all references of object 'obj', therefore destructor invoked automatically.
I see two issues here that may be related. First, referencing subobjects from a destructor called by the GC is not allowed.
Furthermore, the order in which the garbage collector calls destructors for unreference objects is not specified. This means that when the garbage collector calls a destructor for an object of a class that has members that are references to garbage collected objects, those references may no longer be valid. This means that destructors cannot reference sub objects.
Second, as you mention, destructor calls do only take place in the sweep cycle after all threads are resumed. They may be called from a different thread than the one that owned the object when it was alive. There are proposals on the table to fix this, but so far none have been implemented.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With