www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Releasing resources

reply rlpt pathlink.com writes:
In a language binding to a C library that I'm writing, the Context class need to
allocate some resources that should be deallocated when finish using it. Since
the destructors aren't guaranteed to be called on classes... How can I be sure
the class is releasing all it's resources? 

Here's an example of what I'm looking to archieve:

| class Example
| {
|     Context ctx = new Context();
| 
|     void save()
|     {
|         ctx.save(cast(void*)this, this.sizeof);
|     }
| }
| 
| void main()
| {
|     Example e = new Example();
|     e.save();
|     // e.ctx's destructor should be called here.
| }

Thanks
Jan 30 2006
next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
<rlpt pathlink.com> wrote in message news:drl64t$2ulv$1 digitaldaemon.com...
 In a language binding to a C library that I'm writing, the Context class 
 need to
 allocate some resources that should be deallocated when finish using it. 
 Since
 the destructors aren't guaranteed to be called on classes... How can I be 
 sure
 the class is releasing all it's resources?

I usually keep a list of all instances in existence, and delete them all in a static dtor: class Example { Context ctx; // You can't new a class member out here this() { ctx = new Context(); examples[this] = this; } ~this() { delete ctx; examples.remove(this); } protected static Example[Example] examples; static ~this() { foreach(Example e; examples) delete e; } void save() { ctx.save(cast(void*)this, this.sizeof); } } Of course, you may want to move this into the Context class instead (keep a list of Contexts and delete them all at the end, as it's really Context which holds the resources and not Example). It really is irritating that dtors are not guaranteed to be called, isn't it? Poor Walter doesn't seem to understand that there are resources other than vanilla memory that need to be deallocated. Seems kind of pointless to even have dtors then, if they can't be expected to be the opposite of a ctor.
Jan 30 2006
parent reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
Jarrett Billingsley wrote:
 <rlpt pathlink.com> wrote in message news:drl64t$2ulv$1 digitaldaemon.com...
 
In a language binding to a C library that I'm writing, the Context class 
need to
allocate some resources that should be deallocated when finish using it. 
Since
the destructors aren't guaranteed to be called on classes... How can I be 
sure
the class is releasing all it's resources?

I usually keep a list of all instances in existence, and delete them all in a static dtor: class Example { Context ctx; // You can't new a class member out here this() { ctx = new Context(); examples[this] = this; } ~this() { delete ctx; examples.remove(this); } protected static Example[Example] examples; static ~this() { foreach(Example e; examples) delete e; } void save() { ctx.save(cast(void*)this, this.sizeof); } } Of course, you may want to move this into the Context class instead (keep a list of Contexts and delete them all at the end, as it's really Context which holds the resources and not Example). It really is irritating that dtors are not guaranteed to be called, isn't it? Poor Walter doesn't seem to understand that there are resources other than vanilla memory that need to be deallocated. Seems kind of pointless to even have dtors then, if they can't be expected to be the opposite of a ctor.

Last time I checked, D had RAII (ironically, it's now suggested in another thread to rename RAII to RR, meaning "resource release", because that's exactly what it is). it's like this: //new scope { auto Context c = new Context( /* whatever */ ); // .. code .. } // scope finished, c is deleted and the destructor is called This should do it, unless I'm missing something!!
Jan 30 2006
parent reply Mike Parker <aldacron71 yahoo.com> writes:
Hasan Aljudy wrote:
 
 it's like this:
 //new scope
 {
    auto Context c = new Context( /* whatever */ );
    // .. code ..
 }  // scope finished, c is deleted and the destructor is called
 
 This should do it, unless I'm missing something!!

That doesn't work for objects that need to be valid through the life of the program or across multiple method calls, unless of course you declare them in main(). Then that severely limits your design. Consider a global Database context. I'm content to use init & shutdown methods for objects which allocate system resources and need to release them. No biggie.
Jan 31 2006
parent James Dunne <james.jdunne gmail.com> writes:
Mike Parker wrote:
 Hasan Aljudy wrote:
 
 it's like this:
 //new scope
 {
    auto Context c = new Context( /* whatever */ );
    // .. code ..
 }  // scope finished, c is deleted and the destructor is called

 This should do it, unless I'm missing something!!

That doesn't work for objects that need to be valid through the life of the program or across multiple method calls, unless of course you declare them in main(). Then that severely limits your design. Consider a global Database context. I'm content to use init & shutdown methods for objects which allocate system resources and need to release them. No biggie.

I ran across this exact same problem. Turns out I went adventurous and modified phobos to include a "guaranteed" destruction call, even if a segfault occurred. This was for Linux only, since I knew how to do it there, but the concept is the same for Windows. -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/MU/S d-pu s:+ a-->? C++++$ UL+++ P--- L+++ !E W-- N++ o? K? w--- O M-- V? PS PE Y+ PGP- t+ 5 X+ !R tv-->!tv b- DI++(+) D++ G e++>e h>--->++ r+++ y+++ ------END GEEK CODE BLOCK------ James Dunne
Jan 31 2006
prev sibling next sibling parent clayasaurus <clayasaurus gmail.com> writes:
Does the following work?

auto Example e = new Example();
e.save();

// dtor is called when e is out of scope

rlpt pathlink.com wrote:
 In a language binding to a C library that I'm writing, the Context class need
to
 allocate some resources that should be deallocated when finish using it. Since
 the destructors aren't guaranteed to be called on classes... How can I be sure
 the class is releasing all it's resources? 
 
 Here's an example of what I'm looking to archieve:
 
 | class Example
 | {
 |     Context ctx = new Context();
 | 
 |     void save()
 |     {
 |         ctx.save(cast(void*)this, this.sizeof);
 |     }
 | }
 | 
 | void main()
 | {
 |     Example e = new Example();
 |     e.save();
 |     // e.ctx's destructor should be called here.
 | }
 
 Thanks
 
 

Jan 30 2006
prev sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
rlpt pathlink.com wrote:
 In a language binding to a C library that I'm writing, the Context class need
to
 allocate some resources that should be deallocated when finish using it. Since
 the destructors aren't guaranteed to be called on classes... How can I be sure
 the class is releasing all it's resources? 

Call gc_term() immediately before your program exits. Stewart. -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/M d- s:- C++ a->--- UB P+ L E W++ N+++ o K- w++ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++>++++ h-- r-- !y ------END GEEK CODE BLOCK------ My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Jan 31 2006
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message 
news:drnk3s$30po$1 digitaldaemon.com...
 rlpt pathlink.com wrote:
 Call gc_term() immediately before your program exits.

gc_term() gets called by dmain() anyway, and it doesn't call dtors. So if you have resources held in objects which are anything but memory allocated by new (like OS handles or something) and which need to be deallocated in a special way, gc_term() won't get that done.
Jan 31 2006
parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Jarrett Billingsley wrote:
 "Stewart Gordon" <smjg_1998 yahoo.com> wrote in message 
 news:drnk3s$30po$1 digitaldaemon.com...
 rlpt pathlink.com wrote:
 Call gc_term() immediately before your program exits.

gc_term() gets called by dmain() anyway,

It didn't use to. digitalmars.D/3569
 and it doesn't call dtors.

When did this change? It isn't in the change log at all. And http://www.digitalmars.com/d/windows.html still states that it does. And I can't imagine why Walter would have changed the semantics of gc_term so that it doesn't do what some of us are rightly relying on it to do, let alone not bothered telling us. Stewart. -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/M d- s:- C++ a->--- UB P+ L E W++ N+++ o K- w++ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++>++++ h-- r-- !y ------END GEEK CODE BLOCK------ My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Jan 31 2006
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message 
news:dro1g2$o3n$1 digitaldaemon.com...
 It didn't use to.

You post there talks about WinMain, which replaces dmain() if you're writing a Windows app. But check it out - dmd/src/phobos/internal/dmain2.d. Right under the "result = main(args)" line, the module dtors and gc_term() get called. I'm kind of surprised that it's in the try block and not after the catch, though. Maybe that's a bug?
 When did this change?  It isn't in the change log at all.  And

 http://www.digitalmars.com/d/windows.html

 still states that it does.  And I can't imagine why Walter would have 
 changed the semantics of gc_term so that it doesn't do what some of us are 
 rightly relying on it to do, let alone not bothered telling us.

That's news to me. gc_term() does, in fact, seem to work for this little test prog (I had to do the WinMain version since I don't feel like changing dmain and recompiling phobos.. what a pain!): import std.stdio; import std.c.windows.windows; class A { this() { writefln("A ctor"); } ~this() { writefln("A dtor"); } } int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, INT nCmdShow) { A a = new A; return 0; } // ------------------------------------------------------------------------------------------------ extern(C) void gc_init(); extern(C) void gc_term(); extern(C) void _minit(); extern(C) void _moduleCtor(); extern(C) void _moduleDtor(); extern(C) void _moduleUnitTests(); extern(Windows) int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, INT nCmdShow) { int result; gc_init(); _minit(); try { _moduleCtor(); _moduleUnitTests(); result = myWinMain(hInstance, hPrevInstance, lpszCmdLine, nCmdShow); } catch(Object o) { MessageBoxA(null,o.toString(),"Error",MB_OK | MB_ICONEXCLAMATION); result = 0; } _moduleDtor(); writefln("gc_term()"); gc_term(); writefln("gc_term() end"); return result; } This prints A ctor gc_term() A dtor gc_term() end However, in one of my programs, I must delete all instances of a class manually or else their dtors don't get called.. I'll look into why that's happening.
Jan 31 2006
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
----- Original Message ----- 
From: "Jarrett Billingsley" <kb3ctd2 yahoo.com>
Newsgroups: digitalmars.D.learn
Sent: Tuesday, January 31, 2006 12:33 PM
Subject: Re: Releasing resources
 However, in one of my programs, I must delete all instances of a class 
 manually or else their dtors don't get called.. I'll look into why that's 
 happening.

Oh-ho! class A { this() { writefln("A ctor"); as ~= this; } ~this() { writefln("A dtor"); } static A[] as; } class B { this() { writefln("B ctor"); } ~this() { writefln("B dtor"); } } int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, INT nCmdShow) { A a = new A(); B b = new B(); return 0; } (using the same WinMain as my last post) Prints: A ctor B ctor gc_term() B dtor gc_term() end Notice how references to all instances of A are kept in a static array, while references to B are not. This is what I'm doing in my program (I'm using an AA, though), and it seems to keep the GC from collecting those objects. Unless I'm committing some stupid mistake, is this a shortcoming of the GC?
Jan 31 2006
next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:dro7uu$1ajf$1 digitaldaemon.com...
 Notice how references to all instances of A are kept in a static array, 
 while references to B are not.  This is what I'm doing in my program (I'm 
 using an AA, though), and it seems to keep the GC from collecting those 
 objects.

More testing, and it just seems that any references stored in static arrays are not collected. I'm not sure, but the docs may hint at this at the bottom, where it talks about the GC not being able to reclaim memory if a root to a large data structure is kept, and then says that "this advice only applies to static references."
Jan 31 2006
next sibling parent Derek Parnell <derek psych.ward> writes:
On Tue, 31 Jan 2006 12:56:41 -0500, Jarrett Billingsley wrote:

 "Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
 news:dro7uu$1ajf$1 digitaldaemon.com...
 Notice how references to all instances of A are kept in a static array, 
 while references to B are not.  This is what I'm doing in my program (I'm 
 using an AA, though), and it seems to keep the GC from collecting those 
 objects.

More testing, and it just seems that any references stored in static arrays are not collected. I'm not sure, but the docs may hint at this at the bottom, where it talks about the GC not being able to reclaim memory if a root to a large data structure is kept, and then says that "this advice only applies to static references."

Oh dear ... I use that technique in Build. I better manually delete the static array references when it ends I suppose. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocracy!" 1/02/2006 9:58:40 AM
Jan 31 2006
prev sibling parent reply Carlos Santander <csantander619 gmail.com> writes:
Jarrett Billingsley escribió:
 "Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
 news:dro7uu$1ajf$1 digitaldaemon.com...
 Notice how references to all instances of A are kept in a static array, 
 while references to B are not.  This is what I'm doing in my program (I'm 
 using an AA, though), and it seems to keep the GC from collecting those 
 objects.

More testing, and it just seems that any references stored in static arrays are not collected. I'm not sure, but the docs may hint at this at the bottom, where it talks about the GC not being able to reclaim memory if a root to a large data structure is kept, and then says that "this advice only applies to static references."

This also fails: import std.stdio; class A { ~this() { writefln("~A"); } } A tmp; void main() { tmp=new A; } -- Carlos Santander Bernal
Jan 31 2006
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Carlos Santander" <csantander619 gmail.com> wrote in message 
news:drov9q$284g$3 digitaldaemon.com...
 This also fails:

 import std.stdio;

 class A
 {
     ~this()
     {
         writefln("~A");
     }
 }

 A tmp;

 void main()
 {
     tmp=new A;
 }

Then I guess this does conform to the spec, in that static references cannot be collected by the GC. My question, then, is why can't it? Or at least, why can't it in gc_term(), when it _knows_ that all objects must be deleted, whether or not their references are in the static data segment?
Jan 31 2006
prev sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Jarrett Billingsley wrote:
<snip>
 Prints:
 
 A ctor
 B ctor
 gc_term()
 B dtor
 gc_term() end
 
 Notice how references to all instances of A are kept in a static array, 
 while references to B are not.  This is what I'm doing in my program (I'm 
 using an AA, though), and it seems to keep the GC from collecting those 
 objects.
 
 Unless I'm committing some stupid mistake, is this a shortcoming of the GC? 

Looks like a bug to me. It ought to just look through the heap and destruct everything that's left without caring about references whatsoever. Stewart. -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/M d- s:- C++ a->--- UB P+ L E W++ N+++ o K- w++ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++>++++ h-- r-- !y ------END GEEK CODE BLOCK------ My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Feb 01 2006
parent Sean Kelly <sean f4.ca> writes:
Stewart Gordon wrote:
 Jarrett Billingsley wrote:
 <snip>
 Prints:

 A ctor
 B ctor
 gc_term()
 B dtor
 gc_term() end

 Notice how references to all instances of A are kept in a static 
 array, while references to B are not.  This is what I'm doing in my 
 program (I'm using an AA, though), and it seems to keep the GC from 
 collecting those objects.

 Unless I'm committing some stupid mistake, is this a shortcoming of 
 the GC? 

Looks like a bug to me. It ought to just look through the heap and destruct everything that's left without caring about references whatsoever.

It should probably look through the stack as well. The difference in this case being that should merely call all dtors it can find--freeing memory shouldn't matter since the application is shutting down anyway. Sean
Feb 01 2006