Hi,
I started to make a resident texture class, it just holds a TexHandle (an int).
I it's destructor, I try to notify the outer world in a thread safe way that that the handle is no longer in use.
I know that in the ~this() I have restricted options.
I tried to do this with a simple queue object:
class SafeQueue(T, bool multiSrc, bool multiDst)
{
struct Node { T data; Node* next; }
Node* head, tail;
this()
{ head = tail = new Node; }
void put(T data)
{
auto node = new Node(data);
void doit()
{
tail.next = node;
tail = node;
}
static if(multiSrc) synchronized doit; else doit;
}
T* fetch()
{
T* res;
void doit()
{
if(auto newHead = head.next)
{
res = &newHead.data;
head = newHead;
}
}
static if(multiDst) synchronized doit; else doit;
return res;
}
auto fetchAll()
{
//Opt: it could be optimized into a single syncronize block.
T[] res; /+Not the fastest but it's synchronous.+/
while(1) if(auto a = fetch) res ~= *a; else break;
return res;
}
}
alias SSQueue (T) = SafeQueue!(T, 0, 0),
MSQueue (T) = SafeQueue!(T, 1, 0),
SMQueue (T) = SafeQueue!(T, 0, 1),
MMQueue (T) = SafeQueue!(T, 1, 1);
class Texture
{
const TexHandle handle;
version(/+$DIDE_REGION Tracking released texHandles+/all)
{
protected __gshared MSQueue!TexHandle destroyedResidentTexHandles;
shared static this()
{ destroyedResidentTexHandles = new typeof(destroyedResidentTexHandles); }
}
this(S)(in TexFlags flags, in TexFormat format, in S size, in void[] data=null)
{
TexSizeFormat fmt;
fmt.flags = flags,
fmt.format = format,
fmt.size = size;
fmt.resident = true;
handle = TB.createHandleAndSetData(fmt, data);
}
this(S)(in TexFormat format, in S size, in void[] data=null, in TexFlags flags=TexFlags.init)
{ this(flags, format, size, data); }
~this()
{
if(handle)
{ destroyedResidentTexHandles.put(handle); }
}
}
This is working in a normal use case: I create a Texture class insance, I store it's pointer and later when I press a key, I release the class pointer.
But when I do not store the class pointer: new Texture(...);
It just enters into a dead stall right after the first GC call.
Maybe it is because inside MSQueue.put() there is a synchronized{} clause and when the GC runs every thread is stopped?
Maybe it because there are additional memory allocation inside MSQueue.put()?
What's the best practice to do this automatic resource deallocation problem?
Manual deallocation is not a solution for this, because the meaning of this is to avoid manual deallocation, and the human errors that comes with that, in the first place. ;)