digitalmars.D - Please help with GC exception!
- Gor Gyolchanyan (30/30) May 09 2012 I have a structure:
- Steven Schveighoffer (6/33) May 09 2012 Yes, you cannot use GC.delete on a member of a class. Ever. The reason...
- Gor Gyolchanyan (14/54) May 09 2012 I'm not deleting the member. I'm deleting memory, allocated by the membe...
- Steven Schveighoffer (19/26) May 09 2012 The member is a struct, so it's fully contained within the class's block...
- Gor Gyolchanyan (19/43) May 09 2012 I got your point. Thanks for the reply!
- Steven Schveighoffer (9/15) May 09 2012 The memory may have already been reallocated elsewhere!
- Kevin Cox (5/9) May 09 2012 cases.
I have a structure: private struct Block { this(size_t n) { /* allocate n bytes with GC.malloc */ } this(this) { /* deep-copy the bytes */ } ~this() { /* deallocate them with GC.free */ } } And a class: final class Region { private Block _block; alias _block this; } This setup allows me to have memory regions, reallocation of which will never invalidate pointers, because thanks to Region class no-one holds a direct pointer to the memory. The problem is, that I get a core.exception.InvalidMemoryOperationError when my program ends. shared static ~this() { import core.thread; Thread.sleep(dur!`seconds`(1)); } // The error message still showed up after a delay, so it had to be at the process termination When I delete the Region object with "clear", the error diappears. Disabling the GC or forcing a collection before the 1-second sleep doesn't do anything: I still get the error after the 1-second sleep. The only way to stop the error, besides manually deleting the object is the remove the deallocation from the Block's destructor. Can somebody please help me fix this problem? -- Bye, Gor Gyolchanyan.
May 09 2012
On Wed, 09 May 2012 11:28:30 -0400, Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> wrote:I have a structure: private struct Block { this(size_t n) { /* allocate n bytes with GC.malloc */ } this(this) { /* deep-copy the bytes */ } ~this() { /* deallocate them with GC.free */ } } And a class: final class Region { private Block _block; alias _block this; } This setup allows me to have memory regions, reallocation of which will never invalidate pointers, because thanks to Region class no-one holds a direct pointer to the memory. The problem is, that I get a core.exception.InvalidMemoryOperationError when my program ends. shared static ~this() { import core.thread; Thread.sleep(dur!`seconds`(1)); } // The error message still showed up after a delay, so it had to be at the process termination When I delete the Region object with "clear", the error diappears. Disabling the GC or forcing a collection before the 1-second sleep doesn't do anything: I still get the error after the 1-second sleep. The only way to stop the error, besides manually deleting the object is the remove the deallocation from the Block's destructor. Can somebody please help me fix this problem?Yes, you cannot use GC.delete on a member of a class. Ever. The reason is, the memory you are attempting to delete may already be gone, there is no guaranteed order of destruction in a collection cycle. -Steve
May 09 2012
I'm not deleting the member. I'm deleting memory, allocated by the member. If GC deleted it before the object, the same error would appear when I forced a GC collection cycle. Also, docs clearly say, that I'm free to delete the memory myself. This means, that I shouldn't care if a collection cycle went before my deletion! On Wed, May 9, 2012 at 8:07 PM, Steven Schveighoffer <schveiguy yahoo.com> wrote:On Wed, 09 May 2012 11:28:30 -0400, Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> wrote:malloc */ }I have a structure: private struct Block { =C2=A0 =C2=A0 =C2=A0 =C2=A0this(size_t n) { /* allocate n bytes with GC.=}=C2=A0 =C2=A0 =C2=A0 =C2=A0this(this) { /* deep-copy the bytes */ } =C2=A0 =C2=A0 =C2=A0 =C2=A0~this() { /* deallocate them with GC.free */ =he reason is,} And a class: final class Region { =C2=A0 =C2=A0 =C2=A0 =C2=A0private Block _block; =C2=A0 =C2=A0 =C2=A0 =C2=A0alias _block this; } This setup allows me to have memory regions, reallocation of which will never invalidate pointers, because thanks to Region class no-one holds a direct pointer to the memory. The problem is, that I get a core.exception.InvalidMemoryOperationError when my program ends. shared static ~this() { import core.thread; Thread.sleep(dur!`seconds`(1)); } // The error message still showed up after a delay, so it had to be at the process termination When I delete the Region object with "clear", the error diappears. Disabling the GC or forcing a collection before the 1-second sleep doesn't do anything: I still get the error after the 1-second sleep. The only way to stop the error, besides manually deleting the object is the remove the deallocation from the Block's destructor. Can somebody please help me fix this problem?Yes, you cannot use GC.delete on a member of a class. =C2=A0Ever. =C2=A0T=the memory you are attempting to delete may already be gone, there is no guaranteed order of destruction in a collection cycle. -Steve--=20 Bye, Gor Gyolchanyan.
May 09 2012
On Wed, 09 May 2012 12:12:55 -0400, Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> wrote:I'm not deleting the member. I'm deleting memory, allocated by the member.The member is a struct, so it's fully contained within the class's block. Therefore, the memory block is a member, even if indirectly so.If GC deleted it before the object, the same error would appear when I forced a GC collection cycle. Also, docs clearly say, that I'm free to delete the memory myself. This means, that I shouldn't care if a collection cycle went before my deletion!Here is how it works. This is not your example, it's an even simpler one. Memory block A has a pointer to memory block B. Both are GC allocated. A is an object with a dtor that calls GC.delete(B). Since A has the only pointer to B, B is not removed by the GC as long as A is pointed at. Now, A is no longer pointed at, and a GC collection runs. The GC marks all memory, does not mark A, and therefore, does not mark B, so now A and B are scheduled for deletion. After the mark period, the GC cycles through all deletable (unmarked) memory blocks, and finds *B first*. Since B has no dtor, it's simply deallocated. However, A has a destructor. So first, the runtime runs A.__dtor(), which *again* deletes B. This is where the failure occurs. For this reason, you cannot have a destructor which deletes GC-allocated memory. This is even in the spec. -Steve
May 09 2012
I got your point. Thanks for the reply! Wouldn't it make more sense for GC to ignore second deallocation? If this was the case, data, which is know to become garbage would be deallocated right away. On the other hand, I might as well use std.c.stdlib.realloc for these cases= . On Wed, May 9, 2012 at 8:43 PM, Steven Schveighoffer <schveiguy yahoo.com> wrote:On Wed, 09 May 2012 12:12:55 -0400, Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> wrote:r.I'm not deleting the member. I'm deleting memory, allocated by the membe=The member is a struct, so it's fully contained within the class's block. =C2=A0Therefore, the memory block is a member, even if indirectly so.r one.If GC deleted it before the object, the same error would appear when I forced a GC collection cycle. Also, docs clearly say, that I'm free to delete the memory myself. This means, that I shouldn't care if a collection cycle went before my deletion!Here is how it works. =C2=A0This is not your example, it's an even simple=Memory block A has a pointer to memory block B. =C2=A0Both are GC allocat=ed. =C2=A0Ais an object with a dtor that calls GC.delete(B). =C2=A0Since A has the o=nlypointer to B, B is not removed by the GC as long as A is pointed at. Now, A is no longer pointed at, and a GC collection runs. The GC marks all memory, does not mark A, and therefore, does not mark B,=sonow A and B are scheduled for deletion. After the mark period, the GC cycles through all deletable (unmarked) mem=oryblocks, and finds *B first*. =C2=A0Since B has no dtor, it's simply deall=ocated.However, A has a destructor. =C2=A0So first, the runtime runs A.__dtor(),=which*again* deletes B. =C2=A0This is where the failure occurs. For this reason, you cannot have a destructor which deletes GC-allocated memory. =C2=A0This is even in the spec. -Steve--=20 Bye, Gor Gyolchanyan.
May 09 2012
On Wed, 09 May 2012 12:52:43 -0400, Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> wrote:I got your point. Thanks for the reply! Wouldn't it make more sense for GC to ignore second deallocation?The memory may have already been reallocated elsewhere!If this was the case, data, which is know to become garbage would be deallocated right away. On the other hand, I might as well use std.c.stdlib.realloc for these cases.Do not call C's realloc on GC allocated memory. The best option is to avoid the GC whatsoever for memory that you want to manage that tightly. Use c's malloc and free, which can be called from the dtor, no problem. If the block has pointers to GC memory, remember to addRoot it (and removeRoot it in destructor). -Steve
May 09 2012
On May 9, 2012 12:53 PM, "Gor Gyolchanyan" <gor.f.gyolchanyan gmail.com> wrote:Wouldn't it make more sense for GC to ignore second deallocation? If this was the case, data, which is know to become garbage would be deallocated right away. On the other hand, I might as well use std.c.stdlib.realloc for thesecases. No, because then the GC could never give out that same location in memory again.
May 09 2012