www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Garbage collector and real resources

reply AlexBurton <alexibu.spampreventingextrabit mac.com> writes:
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
parent reply Boris Kolar <boris.kolar globera.com> writes:
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)
 
 Alex
It 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
parent reply AlexBurton <alexibu.spampreventingextrabit mac.com> writes:
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
parent reply Bradley Smith <digitalmars-com baysmith.com> writes:
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."
 
 Alex
I 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
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
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