www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Can I remove an element from a global associative array from within a

reply realhet <real_het hotmail.com> writes:
Hi,

I tried to make some resource statistict for my OpenGL Buffer 
objects:


//here are the things that hold the statistics.
private __gshared{ size_t[int] textureSizeMap, bufferSizeMap; }

struct GLCounters{
   int programs, shaders, textures, buffers;
   size_t textureSize, bufferSize;
}
__gshared GLCounters glCounters;

...

//And this is the buffer creation.
int genBuffer(size_t size){
   int res; glGenBuffers (1, &res); glChk;
   glCounters.buffers ++;
   glCounters.bufferSize  += size;
   bufferSizeMap [res] = size;
   writefln("buffer allocated %d %d", res, size);
   return res;
}

//Finally, this is the deallocation. If it is called from the GC 
(when it destroys a class), it has a big chance to throw an 
Invalid Memory Operation exception.

void deleteBuffer (int handle){
   if(!handle) return;
   glDeleteBuffers (1, &handle); glChk;
   glCounters.buffers --;
   glCounters.bufferSize  -= bufferSizeMap [handle];
   writefln("buffer deallocated %d %d", handle, bufferSizeMap 
[handle]);
   bufferSizeMap.remove(handle); <- this is the problematic part.
}

--------------------------------------------
Today I read the documentation about structs, unions and classes, 
but I haven't find any restrictions for the ~this() destructors.

Is there some extra rules regarding the GC and what I must not do 
in the destructors?

I think the destructor always called in the same thread where the 
instance was created. This can't be the case.
But what I can guess is: The GC makes a collection and calls my 
destructor and inside I do something and the GC calls a 
collection again recursively. But it seems a bit crazy, so I 
think it's not the case :D

Please help me solve this!

*I'm using LDC 1.6.0 Win64
Aug 02 2019
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Friday, August 2, 2019 5:13:10 PM MDT realhet via Digitalmars-d-learn 
wrote:
 Hi,

 I tried to make some resource statistict for my OpenGL Buffer
 objects:


 //here are the things that hold the statistics.
 private __gshared{ size_t[int] textureSizeMap, bufferSizeMap; }

 struct GLCounters{
    int programs, shaders, textures, buffers;
    size_t textureSize, bufferSize;
 }
 __gshared GLCounters glCounters;

 ...

 //And this is the buffer creation.
 int genBuffer(size_t size){
    int res; glGenBuffers (1, &res); glChk;
    glCounters.buffers ++;
    glCounters.bufferSize  += size;
    bufferSizeMap [res] = size;
    writefln("buffer allocated %d %d", res, size);
    return res;
 }

 //Finally, this is the deallocation. If it is called from the GC
 (when it destroys a class), it has a big chance to throw an
 Invalid Memory Operation exception.

 void deleteBuffer (int handle){
    if(!handle) return;
    glDeleteBuffers (1, &handle); glChk;
    glCounters.buffers --;
    glCounters.bufferSize  -= bufferSizeMap [handle];
    writefln("buffer deallocated %d %d", handle, bufferSizeMap
 [handle]);
    bufferSizeMap.remove(handle); <- this is the problematic part.
 }

 --------------------------------------------
 Today I read the documentation about structs, unions and classes,
 but I haven't find any restrictions for the ~this() destructors.

 Is there some extra rules regarding the GC and what I must not do
 in the destructors?

 I think the destructor always called in the same thread where the
 instance was created. This can't be the case.
 But what I can guess is: The GC makes a collection and calls my
 destructor and inside I do something and the GC calls a
 collection again recursively. But it seems a bit crazy, so I
 think it's not the case :D

 Please help me solve this!

 *I'm using LDC 1.6.0 Win64
As I understand it, you can't do much of anything that involves the GC inside a destructor that's being run as a finalizer by the GC (as is almost always the case with classes). Structs or classes on the stack (which for classes requires something like scope) don't have that problem, because they're not being run by the GC. But if a struct or class is on the GC heap, then attempting to allocate or deallocate GC resources doesn't work, because the destructor/finalizer runs when the GC is doing a collection. Even trying to use other objects that are on the GC heap from a finalizer is not a allowed, because there is no guarantee about the order that the objects are collected (otherwise, cyclical references would be a big problem), meaning that references in the finalizer could refer to objects that have already been destroyed and their memory freed. https://dlang.org/spec/class.html#destructors Basically, destructors/finalizers for anything on the GC heap are just for cleaning up non-GC-allocated resources. - Jonathan M Davis
Aug 02 2019
parent realhet <real_het hotmail.com> writes:
On Saturday, 3 August 2019 at 05:33:05 UTC, Jonathan M Davis 
wrote:
 On Friday, August 2, 2019 5:13:10 PM MDT realhet via 
 Digitalmars-d-learn wrote:
 Hi,
 ...
Thank you! Now I have 2 solutions in mind: 1. If I only want to track the count and totalBytes for a specific kind of reference, I will be able update those from the destructor. 2. If I wanna track all instances of resources, I will use 2 classes instead of one class and an associative array: - When I create a resource, I create class1 for the statistics, and store them in an assocArray. Then I create a class2 and inside it there is a reference to class1. Class2 will be given to the user. If the user don't use it anymore, the GC will destroy class2, and in it's destructor it will mark a field in class1. And it will be periodically checked, so the actual deletion of the GLBuffer and other statistics will be done by me. With millions of resources it will be slow, but for a few thousand it's ok. (I can also put them in a hierarchy, but the most important is not to alloc/dealloc from ~this()) I hope it will be stable :D Thank You again!
Aug 03 2019
prev sibling parent JN <666total wp.pl> writes:
On Friday, 2 August 2019 at 23:13:10 UTC, realhet wrote:
 Today I read the documentation about structs, unions and 
 classes, but I haven't find any restrictions for the ~this() 
 destructors.

 Is there some extra rules regarding the GC and what I must not 
 do in the destructors?
Class destructors are similar to Java's finalizers in behavior. D doesn't give you a guarantee when the class destructor is called and it doesn't give you a guarantee if it will be called at all, so it doesn't really work for RAII-like resource management. If you want RAII-like behavior, either use structs (struct destructors are deterministic), or wrap your class inside a scoped/refCounted wrapper from std.typecons.
Aug 03 2019