digitalmars.D - Garbage collector and real resources
- AlexBurton (33/33) Jan 24 2007 Hi,
- Boris Kolar (31/81) Jan 24 2007 It works as expected for me. This program:
- AlexBurton (7/7) Jan 24 2007 Thanks for your reply Boris
- Bradley Smith (35/48) Jan 24 2007 I got it to work by explicitly deleting the File object in a FileWriter
- Frits van Bommel (9/42) Jan 25 2007 Note that code may have undefined behavior if FileWriter is ever deleted...
Hi, First I must confess that I come from C++. I am hoping to move most of my code in the future to D, and am finding it a great language. One problem which I hope someone can help me with is where classes reference real resources. Here is some example code which I think illustrates my problem import std.stream; class FileWriter { std.stream.File f; this() { f = new std.stream.File("data.txt",FileMode.Out); f.write(23); } }; int main() { { scope auto f = new FileWriter(); } { scope auto f = new FileWriter(); } return 0; } In this case the FileWriter class uses a real and finite resource (unlike memory which a GC can handle quite well) The second instance of FileWriter fails because the first is still around even though the scope keyword is used. A solution is to write a destructor for FileWriter that uses the delete keyword (and still use scope). Or to leak the close method of the FileStream - and write a try catch block for each level of function call that can exist higher in the stack. But what happens if the FileWriter is an aggregate part of another larger class, the explicit destructors and scope decrlarations are leaking the implementation detail that this class has a real resource. Also I note that the scope keyword can't be put in class scope ... This is a general problem that ties in conceptual constness - it would be nice if there were a way to express that an object is an aggregate part of another. Please tell me if I am missing something (including possibly some fundamental GC philosophy) Alex
Jan 24 2007
AlexBurton Wrote:Hi, First I must confess that I come from C++. I am hoping to move most of my code in the future to D, and am finding it a great language. One problem which I hope someone can help me with is where classes reference real resources. Here is some example code which I think illustrates my problem import std.stream; class FileWriter { std.stream.File f; this() { f = new std.stream.File("data.txt",FileMode.Out); f.write(23); } }; int main() { { scope auto f = new FileWriter(); } { scope auto f = new FileWriter(); } return 0; } In this case the FileWriter class uses a real and finite resource (unlike memory which a GC can handle quite well) The second instance of FileWriter fails because the first is still around even though the scope keyword is used. A solution is to write a destructor for FileWriter that uses the delete keyword (and still use scope). Or to leak the close method of the FileStream - and write a try catch block for each level of function call that can exist higher in the stack. But what happens if the FileWriter is an aggregate part of another larger class, the explicit destructors and scope decrlarations are leaking the implementation detail that this class has a real resource. Also I note that the scope keyword can't be put in class scope ... This is a general problem that ties in conceptual constness - it would be nice if there were a way to express that an object is an aggregate part of another. Please tell me if I am missing something (including possibly some fundamental GC philosophy) AlexIt works as expected for me. This program: void main() { scope class Scope { this(char[] foo) { printf("Scope.this(\"%s\")\n", foo.ptr); _foo = foo; } ~this() { printf("Scope.~this(\"%s\")\n", _foo.ptr); } private char[] _foo; } printf("Init.\n"); { scope Scope a = new Scope("a"); } printf("Check 1\n"); { scope Scope a = new Scope("b"); } printf("Done.\n"); } ... outputs: Init. Scope.this("a") Scope.~this("a") Check 1 Scope.this("b") Scope.~this("b") Done.
Jan 24 2007
Thanks for your reply Boris It appears that I have miscommunicated my problem. I was not intending to say that the scope keyword was not working as documented. This line of my original post: "The second instance of FileWriter fails because the first is still around even though the scope keyword is used." Should have read : "The second instance of FileWriter fails because the FileStream that was part of the first has not been destroyed." Alex
Jan 24 2007
AlexBurton wrote:Thanks for your reply Boris It appears that I have miscommunicated my problem. I was not intending to say that the scope keyword was not working as documented. This line of my original post: "The second instance of FileWriter fails because the first is still around even though the scope keyword is used." Should have read : "The second instance of FileWriter fails because the FileStream that was part of the first has not been destroyed." AlexI got it to work by explicitly deleting the File object in a FileWriter destructor. Below is the code. The documentation on destructors (http://www.digitalmars.com/d/class.html#destructors) states "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 makes me wonder how to reasonably release resources held by an object. I'll be asking this question on digitalmars.D.learn. Thanks for the interesting post. Bradley import std.stream; class FileWriter { std.stream.File f; this() { f = new std.stream.File("data.txt",FileMode.Out); f.write(23); } ~this() { delete f; } } int main() { { scope auto f = new FileWriter(); } { scope auto f = new FileWriter(); } return 0; }
Jan 24 2007
Bradley Smith wrote: [snip]"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 makes me wonder how to reasonably release resources held by an object. I'll be asking this question on digitalmars.D.learn.import std.stream; class FileWriter { std.stream.File f; this() { f = new std.stream.File("data.txt",FileMode.Out); f.write(23); } ~this() { delete f; } } int main() { { scope auto f = new FileWriter(); } { scope auto f = new FileWriter(); } return 0; }Note that code may have undefined behavior if FileWriter is ever deleted by the GC. In that case, the std.stream.File may have been collected before the FileWriter (that's what the unspecified order of destructors means), and you're performing a double deletion. So if you delete f in the destructor, it may be best to make FileWriter a scope class (add 'scope' before 'class') so that it's always explicitly deleted.
Jan 25 2007