www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The C heap and D.

reply Dave <Dave_member pathlink.com> writes:
Can the std.c.stdlib.malloc() and free() family of functions 'safely' be used as
they are in C for basic types and structs w/o any special interaction with the
GC (e.g.: w/o using gc.addRange())?

For example, with char* strings, or dynamic arrays of structs.

Any special considerations for malloc/free on an array of structs with GC'd
members?

The docs. mention using addRange() for class allocators (using the C heap) if
the classes allocate any objects that use the GC, but the docs. don't seem to
really go beyond that.

TIA for the sanity check.
Sep 27 2004
parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cj8drs$1cj7$1 digitaldaemon.com>, Dave says...
Can the std.c.stdlib.malloc() and free() family of functions 'safely' be used as
they are in C for basic types and structs w/o any special interaction with the
GC (e.g.: w/o using gc.addRange())?

Yes, provided that your mallocked memory contains no pointers to anything obtained via "new".
For example, with char* strings, or dynamic arrays of structs.

If by "char* strings" you mean an array of chars, that will be fine. An array of pointers to chars is not. An array of structs will be okay iff the structs contain no pointers to "new"ed stuff.
Any special considerations for malloc/free on an array of structs with GC'd
members?

Well, then you'd have to call addRange(). Arcane Jill
Sep 27 2004
parent reply Dave <Dave_member pathlink.com> writes:
Arcane Jill wrote:

 In article <cj8drs$1cj7$1 digitaldaemon.com>, Dave says...
Can the std.c.stdlib.malloc() and free() family of functions 'safely' be
used as they are in C for basic types and structs w/o any special
interaction with the GC (e.g.: w/o using gc.addRange())?

Yes, provided that your mallocked memory contains no pointers to anything obtained via "new".
For example, with char* strings, or dynamic arrays of structs.

If by "char* strings" you mean an array of chars, that will be fine. An array of pointers to chars is not. An array of structs will be okay iff the structs contain no pointers to "new"ed stuff.
Any special considerations for malloc/free on an array of structs with
GC'd members?

Well, then you'd have to call addRange(). Arcane Jill

Thanks for the quick reply! How about somewhat of the 'inverse' of the examples above.. e.g: The case where a GC'd class object malloc's member(s) which are subsequently free'd in the destructor? Unless 'auto' is enforced by the class definition, I know the destructor may be called at an indeterminate time and therefore not free resources as quickly as may be desired, but in some cases I think this will be acceptable behaviour. So, other than not freeing resources immediately, are there any problems with this not covered by the standard C heap caveats? If 'no', than I take it that four general rules of thumb can be applied here: 1) The C heap /can/ be used in D for non-aggregate native types without special consideration of the GC. It is simply using a seperate heap. 2) struct pointers allocated with the C heap can have GC'd members if the struct pointers are properly 'registered' with the GC. 3) If struct pointers allocated with the C heap are /not/ registered with the GC, then all members must use the C heap as well. 4) All classes allocated with the C heap should be registered with the GC because they can be derived from. If so, I'll add a suggested change to the docs. to http://www.prowiki.org/wiki4d/wiki.cgi?DocComments/Memory I think this needs to be clarified in the docs. because a lot of people coming over from C will probably find plenty of uses for the C heap, and the ability to mix GC with C heap relatively easily is a very cool feature of D, IMHO. Thanks again.
Sep 28 2004
parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cjc0c0$2eni$1 digitaldaemon.com>, Dave says...

Thanks for the quick reply!

How about somewhat of the 'inverse' of the examples above.. e.g: The case
where a GC'd class object malloc's member(s) which are subsequently free'd
in the destructor?

Unless 'auto' is enforced by the class definition, I know the destructor may
be called at an indeterminate time and therefore not free resources as
quickly as may be desired, but in some cases I think this will be
acceptable behaviour. So, other than not freeing resources immediately, are
there any problems with this not covered by the standard C heap caveats?

I think you got it spot on. There is one caveat, for which I don't have a workaround. Rumor has it that if an application has global/static data, and that data happens by coincidence to "look like" the address of a GC'ed object (for example, an int whose value happens to be identical to the address of a "new"ed object) then it is, apparently, feasable that the GC will /never/ regard the object as unreachable, and /never/ call its destructor, even on program exit. would like Walter to address at some point.
If 'no', than I take it that four general rules of thumb can be applied
here:

1) The C heap /can/ be used in D for non-aggregate native types without
special consideration of the GC. It is simply using a seperate heap.

I think that's right.
2) struct pointers allocated with the C heap can have GC'd members if the
struct pointers are properly 'registered' with the GC.

Yup.
3) If struct pointers allocated with the C heap are /not/ registered with
the GC, then all members must use the C heap as well.

Sounds good to me.
4) All classes allocated with the C heap should be registered with the GC
because they can be derived from.

Aren't you confusing an instance with a declaration there? How can you derive from an instance? This one I don't get. I've probably misunderstood.
If so, I'll add a suggested change to the docs. to
http://www.prowiki.org/wiki4d/wiki.cgi?DocComments/Memory

I think this needs to be clarified in the docs. because a lot of people
coming over from C will probably find plenty of uses for the C heap, and
the ability to mix GC with C heap relatively easily is a very cool feature
of D, IMHO.

So long as you remember that anything mallocked must also be freed, I guess that's so. Arcane Jill
Sep 28 2004
next sibling parent reply Burton Radons <burton-radons smocky.com> writes:
Arcane Jill wrote:
 In article <cjc0c0$2eni$1 digitaldaemon.com>, Dave says...
 
 
Thanks for the quick reply!

How about somewhat of the 'inverse' of the examples above.. e.g: The case
where a GC'd class object malloc's member(s) which are subsequently free'd
in the destructor?

Unless 'auto' is enforced by the class definition, I know the destructor may
be called at an indeterminate time and therefore not free resources as
quickly as may be desired, but in some cases I think this will be
acceptable behaviour. So, other than not freeing resources immediately, are
there any problems with this not covered by the standard C heap caveats?

I think you got it spot on. There is one caveat, for which I don't have a workaround. Rumor has it that if an application has global/static data, and that data happens by coincidence to "look like" the address of a GC'ed object (for example, an int whose value happens to be identical to the address of a "new"ed object) then it is, apparently, feasable that the GC will /never/ regard the object as unreachable, and /never/ call its destructor, even on program exit. would like Walter to address at some point.

Why would an easily-testable quality be rumour? The GC is type-unaware, so this is true. It doesn't require an identical match, either; it only has to alias to the span of memory allocated for the object: import std.gc; int x; class Foo { this () { printf ("%p feels strong!\n", this); } ~this () { printf ("%p's melting!\n", this); } } int main () { x = 3 + cast (int) cast (void *) new Foo (); new Foo (); std.gc.fullCollect (); return 0; } Prints: 008A1FE0 feels strong! 008A1FD0 feels strong! 008A1FD0's melting! An object temporarily suspended by an accidental alias will probably get terminated the next collection; I would worry more that scanning takes many times longer than it should due to the lack of type awareness, rather than a fear of an object being suspended by the GC. Odd that the GC is never called, that's new behaviour.
Sep 29 2004
parent Burton Radons <burton-radons smocky.com> writes:
Burton Radons wrote:

 Odd that the GC is never called, that's new behaviour.

Ack, odd that the destructor is never called, that is.
Sep 29 2004
prev sibling next sibling parent reply Dave <Dave_member pathlink.com> writes:
Arcane Jill wrote:

 In article <cjc0c0$2eni$1 digitaldaemon.com>, Dave says...
 

 
 There is one caveat, for which I don't have a workaround. Rumor has it
 that if an application has global/static data, and that data happens by
 coincidence to "look like" the address of a GC'ed object (for example, an
 int whose value happens to be identical to the address of a "new"ed
 object) then it is, apparently, feasable that the GC will /never/ regard
 the object as unreachable, and /never/ call its destructor, even on
 program exit. would like Walter to address at some point.
 

This seems to work Ok; the dtor is called both w/ & w/o 'delete a'. #import std.gc; # #int i; # #class A { # int i; # static int j; # ~this() # { # printf("~dtor\n"); # } #} # #void main() #{ # newA(); # printf("%x, %x\n",i,A.j); #} # #void newA() #{ # A a; # i = cast(int)&a; # a = new A(); # a.j = cast(int)&a; # printf("%p, %x, %x\n",&a,i,a.j); # //delete a; #}
 
If 'no', than I take it that four general rules of thumb can be applied
here:

1) The C heap /can/ be used in D for non-aggregate native types without
special consideration of the GC. It is simply using a seperate heap.

I think that's right.
2) struct pointers allocated with the C heap can have GC'd members if the
struct pointers are properly 'registered' with the GC.

Yup.
3) If struct pointers allocated with the C heap are /not/ registered with
the GC, then all members must use the C heap as well.

Sounds good to me.
4) All classes allocated with the C heap should be registered with the GC
because they can be derived from.

Aren't you confusing an instance with a declaration there? How can you derive from an instance? This one I don't get. I've probably misunderstood.

Something along those lines is already outlined here http://digitalmars.com/d/memory.html#newdelete so I'm thinking 'rule-of-thumb' 4) would apply because the original developer can't know if the class will be derived from.
 
If so, I'll add a suggested change to the docs. to
http://www.prowiki.org/wiki4d/wiki.cgi?DocComments/Memory

I think this needs to be clarified in the docs. because a lot of people
coming over from C will probably find plenty of uses for the C heap, and
the ability to mix GC with C heap relatively easily is a very cool feature
of D, IMHO.

So long as you remember that anything mallocked must also be freed, I guess that's so.

I'll add that to the suggested doc. change as well. Something along the lines of "...as with C/++, memory malloc'd must be free'd and pointers should be zeroed...", etc.
 
 Arcane Jill

Sep 29 2004
parent reply Burton Radons <burton-radons smocky.com> writes:
Dave wrote:
 #void newA()
 #{
 #    A a;
 #    i = cast(int)&a;

That stores the address of the container of the reference to the instance of the class A, not the address of the reference to the instance of the class A.
Sep 29 2004
next sibling parent Dave <Dave_member pathlink.com> writes:
Burton Radons wrote:

 Dave wrote:
 #void newA()
 #{
 #    A a;
 #    i = cast(int)&a;

That stores the address of the container of the reference to the instance of the class A, not the address of the reference to the instance of the class A.

Oops - you're right. Give the following code a shot.. This /does/ apparently show what AJ describes. W/o 'delete a;' the destructor is not called. If delete is called or the i and A.j values are changed before return from main(), the dtor is called. #import std.gc; # #int i; # #class A { # int i; # static int j; # ~this() # { # printf("~dtor (%p)\n",this); # } #} # #void main() #{ # newA(); # printf("%x, %x\n",i,A.j); # //i = 0; A.j = 0; #} # #void newA() #{ # A a; # a = new A(); # i = cast(int)(cast(void*)a); # a.j = cast(int)(cast(void*)a); # printf("%p, %x, %x\n",a,i,a.j); # //delete a; #}
Sep 29 2004
prev sibling parent Regan Heath <regan netwin.co.nz> writes:
On Wed, 29 Sep 2004 06:23:03 -0700, Burton Radons 
<burton-radons smocky.com> wrote:
 Dave wrote:
 #void newA()
 #{
 #    A a;
 #    i = cast(int)&a;

That stores the address of the container of the reference to the instance of the class A, not the address of the reference to the instance of the class A.

This is a sentence even "Dr. Seuss" would be proud of. :) Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 29 2004
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:cjdk3a$ejq$1 digitaldaemon.com...
 There is one caveat, for which I don't have a workaround. Rumor has it

 an application has global/static data, and that data happens by

 "look like" the address of a GC'ed object (for example, an int whose value
 happens to be identical to the address of a "new"ed object) then it is,
 apparently, feasable that the GC will /never/ regard the object as

 and /never/ call its destructor, even on program exit. would like Walter

 address at some point.

I'll clear up the rumor, it is a fact, and you describe it correctly. In the future, D's gc will probably be modified to use type information to skip 'int' and other non-pointer types in the static data when looking for roots.
Sep 30 2004
next sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Walter" <newshound digitalmars.com> wrote in message
news:cjic7v$2pid$2 digitaldaemon.com...
 "Arcane Jill" <Arcane_member pathlink.com> wrote in message
 news:cjdk3a$ejq$1 digitaldaemon.com...
 There is one caveat, for which I don't have a workaround. Rumor has it

 an application has global/static data, and that data happens by

 "look like" the address of a GC'ed object (for example, an int whose


 happens to be identical to the address of a "new"ed object) then it is,
 apparently, feasable that the GC will /never/ regard the object as

 and /never/ call its destructor, even on program exit. would like Walter

 address at some point.

I'll clear up the rumor, it is a fact, and you describe it correctly. In

 future, D's gc will probably be modified to use type information to skip
 'int' and other non-pointer types in the static data when looking for

I want to emphasize that code like: int x; ... void foo() { void* p = new int[10]; x = cast(int)p; } is very bad form for several reasons, not the least of which it will break when the gc does start using type information. To make things like this work reliably, use a union: union V { int x; void* p; } V x; ... x.p = p; ...
Sep 30 2004
parent Arcane Jill <Arcane_member pathlink.com> writes:
In article <cjicqq$2ptq$1 digitaldaemon.com>, Walter says...

I want to emphasize that code like:
[bad code snipped]
is very bad form

Of course. I was more concerned by code like: # ubyte g[20]; # # void f() # { # // fill g with some data, possibly random # } resulting in four consecutive bytes of g /accidentally/ mimicking a GC address. Jill
Sep 30 2004
prev sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cjic7v$2pid$2 digitaldaemon.com>, Walter says...

I'll clear up the rumor, it is a fact, and you describe it correctly. In the
future, D's gc will probably be modified to use type information to skip
'int' and other non-pointer types in the static data when looking for roots.

Forgive me, but I don't see why - AT PROGRAM EXIT (and at no other time) - you can't just remove /all/ static data when looking for roots. Or even [phase 1] collect all static data; [phase 2] remove all static data from the list of roots [phase 3] collect everything else. I can't see what that would break. I don't think it would break anything. Am I missing something? If not, it seems a very simple modification to make. Jill
Sep 30 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:cjiv11$1g8$1 digitaldaemon.com...
 In article <cjic7v$2pid$2 digitaldaemon.com>, Walter says...

I'll clear up the rumor, it is a fact, and you describe it correctly. In


future, D's gc will probably be modified to use type information to skip
'int' and other non-pointer types in the static data when looking for


 Forgive me, but I don't see why - AT PROGRAM EXIT (and at no other time) -

 can't just remove /all/ static data when looking for roots. Or even [phase

 collect all static data; [phase 2] remove all static data from the list of

 [phase 3] collect everything else. I can't see what that would break. I

 think it would break anything. Am I missing something? If not, it seems a

 simple modification to make.

Since all the program's memory gets handed back to the operating system at program exit regardless, there's not much point to even bothering running a gc pass at program exit.
Oct 04 2004
next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cjslr3$1oav$1 digitaldaemon.com>, Walter says...
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:cjiv11$1g8$1 digitaldaemon.com...
 In article <cjic7v$2pid$2 digitaldaemon.com>, Walter says...

I'll clear up the rumor, it is a fact, and you describe it correctly. In


future, D's gc will probably be modified to use type information to skip
'int' and other non-pointer types in the static data when looking for


 Forgive me, but I don't see why - AT PROGRAM EXIT (and at no other time) -

 can't just remove /all/ static data when looking for roots. Or even [phase

 collect all static data; [phase 2] remove all static data from the list of

 [phase 3] collect everything else. I can't see what that would break. I

 think it would break anything. Am I missing something? If not, it seems a

 simple modification to make.

Since all the program's memory gets handed back to the operating system at program exit regardless, there's not much point to even bothering running a gc pass at program exit.

Absoulutely not true. We're talking about destructors being skipped there - and that's /important/. What if a destructor needs to write cached data to a half-written file before closing it? (And please don't say "use an auto class" - there are circumstances where that's infeasible). Honestly, Walter, a guarantee that all destructors /will/ run at some point would be a very, very, very, very, very good thing. Arcane Jill
Oct 05 2004
parent Stewart Gordon <smjg_1998 yahoo.com> writes:
Arcane Jill wrote:
<snip>
 Absoulutely not true. We're talking about destructors being skipped there - and
 that's /important/. What if a destructor needs to write cached data to a
 half-written file before closing it? (And please don't say "use an auto class"
-
 there are circumstances where that's infeasible).
 
 Honestly, Walter, a guarantee that all destructors /will/ run at some point
 would be a very, very, very, very, very good thing.

Am I allowed to say "call gc_term() on program exit"? Stewart.
Oct 05 2004
prev sibling next sibling parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
 Since all the program's memory gets handed back to the operating system at
 program exit regardless, there's not much point to even bothering running

 gc pass at program exit.

Do you know what Java and C# do? I'm not exactly sure but I don't think Java does a "full" GC pass at exit - at least nothing is guaranteed. I don't know about C#. Personally I agree gc_term should be a no-op by default. It might be interesting to experiment with other mechanisms for managing non-memory resources besides using destructors/finalizers. For example why run a destructor at exit that closes a file when the file will be closed by the OS on exit anyway? In Java one can register a "shutdown hook": http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Runtime.html#addShutdownHook(java.lang.Thread) Maybe D can do something like that instead. -Ben
Oct 05 2004
parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Ben Hinkle wrote:
<snip>
 Do you know what Java and C# do? I'm not exactly sure but I don't think Java
 does a "full" GC pass at exit - at least nothing is guaranteed. I don't know
 about C#. Personally I agree gc_term should be a no-op by default.

Then what on the face of the planet would the point of having gc_term?
 It might be interesting to experiment with other mechanisms for managing
 non-memory resources besides using destructors/finalizers. For example why
 run a destructor at exit that closes a file when the file will be closed by
 the OS on exit anyway?

Because the OS has absolutely no idea how to flush a cache held by the application or a third-party library. Stewart.
Oct 05 2004
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message
news:cjum4c$g35$1 digitaldaemon.com...
 Ben Hinkle wrote:
 <snip>
 Do you know what Java and C# do? I'm not exactly sure but I don't think


 does a "full" GC pass at exit - at least nothing is guaranteed. I don't


 about C#. Personally I agree gc_term should be a no-op by default.

Then what on the face of the planet would the point of having gc_term?

Why the incredulous response? Did I offend you at some point? If so I apologize. Anyway, I did say "by default" meaning some compiler flag (eg, version=CollectGarbageOnExit) or startup switch or something could cause it to do a full-collect at exit.
 It might be interesting to experiment with other mechanisms for managing
 non-memory resources besides using destructors/finalizers. For example


 run a destructor at exit that closes a file when the file will be closed


 the OS on exit anyway?

Because the OS has absolutely no idea how to flush a cache held by the application or a third-party library.

Destructors in D are very very limited. In particular if the cache uses GC-managed memory it can't be referenced in a destructor. Basically I think D should learn from Java/C# instead of taking a step backwards. Even calling D's destructors "destructors" is probably a mistake since C++ users will write destructors assuming the concept is the same as in C++ and will most likely write destructors that fail randomly.
Oct 05 2004
parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Ben Hinkle wrote:
 "Stewart Gordon" <smjg_1998 yahoo.com> wrote in message
 news:cjum4c$g35$1 digitaldaemon.com...

 Then what on the face of the planet would the point of having gc_term?

Why the incredulous response? Did I offend you at some point? If so I apologize. Anyway, I did say "by default" meaning some compiler flag (eg, version=CollectGarbageOnExit) or startup switch or something could cause it to do a full-collect at exit.

I still don't get it. Why would someone put in a call to gc_term if it isn't what's wanted? If someone's main function ends with a gc_term call, then someone wants it to collect the garbage when it exits. There's no point in having to tell it twice by specifying a version as well. <snip>
 Because the OS has absolutely no idea how to flush a cache held by the
 application or a third-party library.

Destructors in D are very very limited. In particular if the cache uses GC-managed memory it can't be referenced in a destructor.

Do you mean that if you have something like class CachcedFile { void[] cache; ... ~this() { flush(); } } then cache will already be an invalid reference by the time the destructor is called? I'm not sure how that would be. Stewart.
Oct 06 2004
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message
news:ck0dfe$20u2$1 digitaldaemon.com...
 Ben Hinkle wrote:
 "Stewart Gordon" <smjg_1998 yahoo.com> wrote in message
 news:cjum4c$g35$1 digitaldaemon.com...

 Then what on the face of the planet would the point of having gc_term?

Why the incredulous response? Did I offend you at some point? If so I apologize. Anyway, I did say "by default" meaning some compiler flag (eg, version=CollectGarbageOnExit) or startup switch or something could cause


 to do a full-collect at exit.

I still don't get it. Why would someone put in a call to gc_term if it isn't what's wanted? If someone's main function ends with a gc_term call, then someone wants it to collect the garbage when it exits. There's no point in having to tell it twice by specifying a version as well.

That's a good point. The reason I said gc_term should do nothing was because Walter mentioned gc_term. I should have said something like "it should be optional to call gc_term at program exit". I was being too imprecise by saying "gc_term should do nothing by default". There is a slight difference that I was ignoring.
 <snip>
 Because the OS has absolutely no idea how to flush a cache held by the
 application or a third-party library.

Destructors in D are very very limited. In particular if the cache uses GC-managed memory it can't be referenced in a destructor.

Do you mean that if you have something like class CachcedFile { void[] cache; ... ~this() { flush(); } } then cache will already be an invalid reference by the time the destructor is called? I'm not sure how that would be.

Assuming the cache was allocated from the GC the flush() function shouldn't reference it. The reason is that the collector collects memory in random order so the destructor for a CachedFile might run after the cache has been collected. The only things that should be referenced in a destructor are the object itself and any external (non-GC managed) objects. Since most external objects get cleaned up by the OS at exit anyway this tends to mean destructors aren't very useful. However, if the user explicitly calls "delete" then the destructor is called directly so it is perfectly safe in that case for flush() to reference cache. Perhaps the way to solve these problems is for the destructor to get a parameter that says if the call was explicit or implicit: ~this(bool explicit) { if (explicit) flush(); }
 Stewart.

Oct 06 2004
parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Ben Hinkle wrote:

<snip>
 Assuming the cache was allocated from the GC the flush() function shouldn't
 reference it. The reason is that the collector collects memory in random
 order so the destructor for a CachedFile might run after the cache has been
 collected.

Collection and destruction don't have to happen at the same time. Indeed, the GC ought to run all the destructors it has to run, and then do the collecting. Presumably, allocating more memory in a destructor is something you'd avoid anyway, whether or not it's supposed to persist after the destructor has run. <snip>
 However, if the user explicitly calls "delete" then the destructor is called
 directly so it is perfectly safe in that case for flush() to reference
 cache. Perhaps the way to solve these problems is for the destructor to get
 a parameter that says if the call was explicit or implicit:
  ~this(bool explicit) {
     if (explicit) flush();
   }

Thereby bringing in the horror from Fortran I thought we were trying to avoid. Stewart.
Oct 06 2004
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message
news:ck0usl$2ft4$1 digitaldaemon.com...
 Ben Hinkle wrote:

 <snip>
 Assuming the cache was allocated from the GC the flush() function


 reference it. The reason is that the collector collects memory in random
 order so the destructor for a CachedFile might run after the cache has


 collected.

Collection and destruction don't have to happen at the same time. Indeed, the GC ought to run all the destructors it has to run, and then do the collecting. Presumably, allocating more memory in a destructor is something you'd avoid anyway, whether or not it's supposed to persist after the destructor has run.

For a mark-sweep collector that would be possible but for a generational collector it would have to make a full pass in order to call all the destructors before collecting anything (and that wouldn't be very generational). So to keep the GC implementation flexible the spec pretty much needs to say any garbage can be collected at any time (or never), in any order and on any thread.
 <snip>
 However, if the user explicitly calls "delete" then the destructor is


 directly so it is perfectly safe in that case for flush() to reference
 cache. Perhaps the way to solve these problems is for the destructor to


 a parameter that says if the call was explicit or implicit:
  ~this(bool explicit) {
     if (explicit) flush();
   }

Thereby bringing in the horror from Fortran I thought we were trying to avoid.

.I'm not familiar with the horror you are referring to - can you elaborate?
Oct 06 2004
parent Stewart Gordon <smjg_1998 yahoo.com> writes:
Ben Hinkle wrote:

<snip>
 For a mark-sweep collector that would be possible but for a generational
 collector it would have to make a full pass in order to call all the
 destructors before collecting anything (and that wouldn't be very
 generational). So to keep the GC implementation flexible the spec pretty
 much needs to say any garbage can be collected at any time (or never), in
 any order and on any thread.

Can you think of any scenario in which a two-pass mechanism (one to destruct, one to collect) wouldn't be possible? <snip>
a parameter that says if the call was explicit or implicit:
 ~this(bool explicit) {
    if (explicit) flush();
  }

Thereby bringing in the horror from Fortran I thought we were trying to avoid.

.I'm not familiar with the horror you are referring to - can you elaborate?

Open a file. Write lots of data to it. Then do any of the following: - simply forget to close it - interrupt the program (I'm not sure to what extent D manages to deal with this) - exit via an error condition The file is left incomplete, even more than it would be by the interruption/error alone. Stewart.
Oct 07 2004
prev sibling parent Kevin Bealer <Kevin_member pathlink.com> writes:
In article <cjslr3$1oav$1 digitaldaemon.com>, Walter says...
Since all the program's memory gets handed back to the operating system at
program exit regardless, there's not much point to even bothering running a
gc pass at program exit.

I think running the destructors at exit is a good thing, because: 1. Destructors are very rare in D, so it should be cheap - proportional to number of sockets + files + semaphores, plus a few. 2. Memory does not need to be analyzed or freed, you only want to iterate over blocks (in any order), calling ~this() if found. 3. User can always prevent this action, so code can be safe by default, optionally fast later (if desired): :int main() :{ : Convoluted x = new Convoluted(...); : gc.disable(); // destructors foiled :} Kevin
Oct 05 2004