digitalmars.D - destructor order
- Albin Breen (17/17) Jan 26 2011 Hi! I've been trying out D recently but I'm a little confused about the ...
- Steven Schveighoffer (16/34) Jan 26 2011 dtors are not guaranteed to run in any order.
- Albin Breen (9/18) Jan 26 2011 Thanks! This means that the GC cannot be trusted to call destructors. I ...
- Steven Schveighoffer (6/32) Jan 26 2011 That is a misdesign in clear. It should destroy an object and not
- Andrej Mitrovic (1/1) Jan 26 2011 I think I glanced over a recent svn commit that fixed this, though I'm n...
- Steven Schveighoffer (6/8) Jan 26 2011 No, it hasn't been fixed.
- Andrei Alexandrescu (4/12) Jan 26 2011 Steve, if you could point out what I need to do I'll be glad to do it
- Steven Schveighoffer (69/85) Jan 26 2011 *sweats nervously* I don't know, I'd like to read about how git works
- Steven Schveighoffer (7/34) Feb 24 2011 Finally got around to doing this. I have to agree with others, git is
- Andrei Alexandrescu (3/88) Feb 26 2011 Thanks for taking this to completion! I had it on my todo list forever.
- Jonathan M Davis (18/49) Jan 26 2011 Personally, I don't think that using class destructors is currently a go...
- Sean Kelly (6/27) Jan 26 2011 (in case there are static dependencies). Have I missed something?
Hi! I've been trying out D recently but I'm a little confused about the order in which destructors are called: class A { this() { writeln("ctor"); } ~this() { writeln("dtor"); } static this() { writeln("static ctor"); } static ~this() { writeln("static dtor"); } } void main() { auto a = new A; } This will output: static ctor ctor static dtor dtor I would have thought that the static destructor should be the last one (in case there are static dependencies). Have I missed something? /Albin
Jan 26 2011
On Wed, 26 Jan 2011 07:55:22 -0500, Albin Breen <abreen ea.com> wrote:Hi! I've been trying out D recently but I'm a little confused about the order in which destructors are called: class A { this() { writeln("ctor"); } ~this() { writeln("dtor"); } static this() { writeln("static ctor"); } static ~this() { writeln("static dtor"); } } void main() { auto a = new A; } This will output: static ctor ctor static dtor dtor I would have thought that the static destructor should be the last one (in case there are static dependencies). Have I missed something?dtors are not guaranteed to run in any order. See here: http://www.digitalmars.com/d/2.0/class.html#destructors "The garbage collector is not guaranteed to run the destructor for all unreferenced objects. 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." FYI, the runtime calls static ctors when a thread starts (and once at program start for the main thread) and calls static dtors when a thread exits. If you want a static ctor/dtor to initialize/destroy shared data (or __gshared), use a shared static ctor/dtor. -Steve
Jan 26 2011
Steven Schveighoffer Wrote:See here: http://www.digitalmars.com/d/2.0/class.html#destructors "The garbage collector is not guaranteed to run the destructor for all unreferenced objects. 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."Thanks! This means that the GC cannot be trusted to call destructors. I interpret that as "class destructors must be called manually". Furthermore, The D Programming Language book states that: "...there is no delete operator. (D used to have a delete operator, but it was depre- cated.)" so you can't use that either. In other words you are left with: clear(a); to manually call the destructor, which will also call the constructor again (this time with no parameters), and possibly (but not certainly) the destructor once more. To be able to use clear() you will have to enforce RAII using structs (not garbage collected) or finally{} or scope constructs all the way from main in parallel with all your garbage collected code. All in all, if class destructors cannot be guaranteed to execute by other means than the manual approach then should they be considered a liability? In Phobos std.socket a destructor is used to close() the socket. /Albin
Jan 26 2011
On Wed, 26 Jan 2011 10:53:29 -0500, Albin Breen <abreen ea.com> wrote:Steven Schveighoffer Wrote:That is a misdesign in clear. It should destroy an object and not initialize it again. Furthermore, the destructor should only be called once. Andrei agreed to make that change, but it hasn't gone in yet. -SteveSee here: http://www.digitalmars.com/d/2.0/class.html#destructors "The garbage collector is not guaranteed to run the destructor for all unreferenced objects. 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."Thanks! This means that the GC cannot be trusted to call destructors. I interpret that as "class destructors must be called manually". Furthermore, The D Programming Language book states that: "...there is no delete operator. (D used to have a delete operator, but it was depre- cated.)" so you can't use that either. In other words you are left with: clear(a); to manually call the destructor, which will also call the constructor again (this time with no parameters), and possibly (but not certainly) the destructor once more. To be able to use clear() you will have to enforce RAII using structs (not garbage collected) or finally{} or scope constructs all the way from main in parallel with all your garbage collected code.
Jan 26 2011
I think I glanced over a recent svn commit that fixed this, though I'm not sure.
Jan 26 2011
On Wed, 26 Jan 2011 11:09:45 -0500, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:I think I glanced over a recent svn commit that fixed this, though I'm not sure.No, it hasn't been fixed. The fix is *really* simple, just have clear call rt_finalize (as Sean pointed out on the mailing list) -Steve
Jan 26 2011
On 1/26/11 10:17 AM, Steven Schveighoffer wrote:On Wed, 26 Jan 2011 11:09:45 -0500, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:Steve, if you could point out what I need to do I'll be glad to do it right now. Better yet, feel free to try your hand at a git commit. It's fun! AndreiI think I glanced over a recent svn commit that fixed this, though I'm not sure.No, it hasn't been fixed. The fix is *really* simple, just have clear call rt_finalize (as Sean pointed out on the mailing list) -Steve
Jan 26 2011
On Wed, 26 Jan 2011 12:26:22 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 1/26/11 10:17 AM, Steven Schveighoffer wrote:*sweats nervously* I don't know, I'd like to read about how git works before doing a commit. I don't really understand it at all, I had the same problem with subversion when I started using it. The fix is really easy, just change clear to this: void clear(T)(T obj) if (is(T == class)) { rt_finalize(cast(void*)obj); } Here is the body of the current clear: if (!obj) return; auto ci = obj.classinfo; auto defaultCtor = cast(void function(Object)) ci.defaultConstructor; version(none) // enforce isn't available in druntime _enforce(defaultCtor || (ci.flags & 8) == 0); immutable size = ci.init.length; auto ci2 = ci; do { auto dtor = cast(void function(Object))ci2.destructor; if (dtor) dtor(obj); ci2 = ci2.base; } while (ci2) auto buf = (cast(void*) obj)[0 .. size]; buf[] = ci.init; if (defaultCtor) defaultCtor(obj); And the body of rt_finalize if (p) // not necessary if called from gc { ClassInfo** pc = cast(ClassInfo**)p; if (*pc) { ClassInfo c = **pc; byte[] w = c.init; try { if (det || collectHandler is null || collectHandler(cast(Object)p)) { do { if (c.destructor) { fp_t fp = cast(fp_t)c.destructor; (*fp)(cast(Object)p); // call destructor } c = c.base; } while (c); } if ((cast(void**)p)[1]) // if monitor is not null _d_monitordelete(cast(Object)p, det); (cast(byte*) p)[0 .. w.length] = w[]; } catch (Throwable e) { onFinalizeError(**pc, e); } finally { *pc = null; // zero vptr } } } Note the eerie similarities :) -SteveOn Wed, 26 Jan 2011 11:09:45 -0500, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:Steve, if you could point out what I need to do I'll be glad to do it right now. Better yet, feel free to try your hand at a git commit. It's fun!I think I glanced over a recent svn commit that fixed this, though I'm not sure.No, it hasn't been fixed. The fix is *really* simple, just have clear call rt_finalize (as Sean pointed out on the mailing list) -Steve
Jan 26 2011
On Wed, 26 Jan 2011 13:18:58 -0500, Steven Schveighoffer <schveiguy yahoo.com> wrote:On Wed, 26 Jan 2011 12:26:22 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Finally got around to doing this. I have to agree with others, git is freaking cool (and github too). Thanks to Lars for suggesting the Pro Git book, it helped me a lot. https://github.com/D-Programming-Language/druntime/pull/7 -SteveOn 1/26/11 10:17 AM, Steven Schveighoffer wrote:*sweats nervously* I don't know, I'd like to read about how git works before doing a commit. I don't really understand it at all, I had the same problem with subversion when I started using it. The fix is really easy, just change clear to this: void clear(T)(T obj) if (is(T == class)) { rt_finalize(cast(void*)obj); }On Wed, 26 Jan 2011 11:09:45 -0500, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:Steve, if you could point out what I need to do I'll be glad to do it right now. Better yet, feel free to try your hand at a git commit. It's fun!I think I glanced over a recent svn commit that fixed this, though I'm not sure.No, it hasn't been fixed. The fix is *really* simple, just have clear call rt_finalize (as Sean pointed out on the mailing list) -Steve
Feb 24 2011
On 1/26/11 12:18 PM, Steven Schveighoffer wrote:On Wed, 26 Jan 2011 12:26:22 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Thanks for taking this to completion! I had it on my todo list forever. AndreiOn 1/26/11 10:17 AM, Steven Schveighoffer wrote:*sweats nervously* I don't know, I'd like to read about how git works before doing a commit. I don't really understand it at all, I had the same problem with subversion when I started using it. The fix is really easy, just change clear to this: void clear(T)(T obj) if (is(T == class)) { rt_finalize(cast(void*)obj); } Here is the body of the current clear: if (!obj) return; auto ci = obj.classinfo; auto defaultCtor = cast(void function(Object)) ci.defaultConstructor; version(none) // enforce isn't available in druntime _enforce(defaultCtor || (ci.flags & 8) == 0); immutable size = ci.init.length; auto ci2 = ci; do { auto dtor = cast(void function(Object))ci2.destructor; if (dtor) dtor(obj); ci2 = ci2.base; } while (ci2) auto buf = (cast(void*) obj)[0 .. size]; buf[] = ci.init; if (defaultCtor) defaultCtor(obj); And the body of rt_finalize if (p) // not necessary if called from gc { ClassInfo** pc = cast(ClassInfo**)p; if (*pc) { ClassInfo c = **pc; byte[] w = c.init; try { if (det || collectHandler is null || collectHandler(cast(Object)p)) { do { if (c.destructor) { fp_t fp = cast(fp_t)c.destructor; (*fp)(cast(Object)p); // call destructor } c = c.base; } while (c); } if ((cast(void**)p)[1]) // if monitor is not null _d_monitordelete(cast(Object)p, det); (cast(byte*) p)[0 .. w.length] = w[]; } catch (Throwable e) { onFinalizeError(**pc, e); } finally { *pc = null; // zero vptr } } } Note the eerie similarities :) -SteveOn Wed, 26 Jan 2011 11:09:45 -0500, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:Steve, if you could point out what I need to do I'll be glad to do it right now. Better yet, feel free to try your hand at a git commit. It's fun!I think I glanced over a recent svn commit that fixed this, though I'm not sure.No, it hasn't been fixed. The fix is *really* simple, just have clear call rt_finalize (as Sean pointed out on the mailing list) -Steve
Feb 26 2011
On Wednesday, January 26, 2011 07:53:29 Albin Breen wrote:Steven Schveighoffer Wrote:Personally, I don't think that using class destructors is currently a good idea. Certainly, if you _need_ the destructor to run, then you probably need to rethink your design, since there's no guarantee that it's going to run. You can call clear(), but relying on your code doing that in the general case seems like a bad idea. Already, class destructors can't deal with other GC-allocated objects, so they're restricted to cleaning up other types of resources. If anything, I question that classes should even _have_ destructors. For structs, it makes great sense, but for classes... not so much. At best, they seem useable for stuff like file descriptors where you'd like them to be cleaned up when the object is destroyed if you forgot to do so by calling the appropriate close function or whatnot. But generally, it would be a bug in your code if you didn't release the file descriptor yourself (via the close function or whatever) So, I'd argue that in the general case, you shouldn't be using class destructors. If your object really needs a destructor, then perhaps it should be a struct (on the stack, since structs on the heap _never_ get their destructors called IIRC). - Jonathan M DavisSee here: http://www.digitalmars.com/d/2.0/class.html#destructors "The garbage collector is not guaranteed to run the destructor for all unreferenced objects. 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."Thanks! This means that the GC cannot be trusted to call destructors. I interpret that as "class destructors must be called manually". Furthermore, The D Programming Language book states that: "...there is no delete operator. (D used to have a delete operator, but it was depre- cated.)" so you can't use that either. In other words you are left with: clear(a); to manually call the destructor, which will also call the constructor again (this time with no parameters), and possibly (but not certainly) the destructor once more. To be able to use clear() you will have to enforce RAII using structs (not garbage collected) or finally{} or scope constructs all the way from main in parallel with all your garbage collected code. All in all, if class destructors cannot be guaranteed to execute by other means than the manual approach then should they be considered a liability? In Phobos std.socket a destructor is used to close() the socket.
Jan 26 2011
On Jan 26, 2011, at 4:55 AM, Albin Breen wrote:Hi! I've been trying out D recently but I'm a little confused about =the order in which destructors are called:=20 class A { this() { writeln("ctor"); } ~this() { writeln("dtor"); } =20 static this() { writeln("static ctor"); } static ~this() { writeln("static dtor"); } } =20 void main() { auto a =3D new A; } =20 This will output: static ctor ctor static dtor dtor =20 I would have thought that the static destructor should be the last one =(in case there are static dependencies). Have I missed something? The static dtors are run when main exits and then the GC terminates, = which may trigger a final collection. In this case, the last "dtor" is = when this collection occurs.=
Jan 26 2011