www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Resource Management... Howto?

reply Samuel Winchenbach <swinchen eece.maine.edu> writes:
Hi there.

Lets assume I have a number of resources (audio, textures, fonts, etc) 
that I am trying to keep track of.   I want a number of things to happen:

1.) When something tries to load a resource that isn't already loaded 
the resource will be loaded.  The resource will be stored in an 
associative data structure so resources will be easy to find. 
"Something" will be handed back to the requesting code... maybe an int 
handle, or an object...  I don't know yet.

2.) If an resource is already loaded then it will be found in the 
associative data structure and "something" will be handed back to the 
calling code.

3.) The resource will automatically be freed (calling any appropriate GL 
call to free GPU resources) when code is no longer referencing the 
resource...

I have been thinking about this a lot, and I might have an idea how to 
do this..

I could have something like:
auto h = texture.loadTexture(...);
... use the texture ...
texture.releaseTexture(h);

and texture keeps a reference count and when it drops to zero it is 
removed from the data structure and appropriate actions are taken to 
release any system resources.


What I would like is to not have a texture.releaseTexture().  I would 
like that to be automatic.  Perhaps "h" could be an object with a 
destructor that automatically calls "releaseTexture()"?   Not sure of 
the structure of this....

and what would be best is to have one resource manager that keeps track 
of everything loaded...

auto h = resource.loadResource();

but this gets tricky because how do you know what type of resource you 
are loading, and how do you set the parameters of the resource.


If anyone has any comments, questions, suggestions, sample code, etc. it 
would be greatly appreciated.

Thanks!
Sam
Jul 06 2007
next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Samuel Winchenbach" <swinchen eece.maine.edu> wrote in message 
news:f6m75n$22hh$1 digitalmars.com...
 I could have something like:
 auto h = texture.loadTexture(...);
 ... use the texture ...
 texture.releaseTexture(h);

 and texture keeps a reference count and when it drops to zero it is 
 removed from the data structure and appropriate actions are taken to 
 release any system resources.

 What I would like is to not have a texture.releaseTexture().  I would like 
 that to be automatic.  Perhaps "h" could be an object with a destructor 
 that automatically calls "releaseTexture()"?   Not sure of the structure 
 of this....

You have the basic idea right. Say one of your resource classes (for textures) looks like this: class Texture { private static Texture[char[]] Textures; public static Texture opIndex(char[] name) { if(auto tex = name in Textures) { (*tex).mRefCount++; return *tex; } Texture t = new Texture(name); Textures[name] = t; return t; } public static void release(Texture t) { t.mRefCount--; if(t.mRefCount == 0) { t.releaseResources(); Textures.remove(t); delete t; // optional } } private int mRefCount = 1; private this(char[] name) { // find and load the texture } private void releaseResources() { // you know what to do here } } This way, all you have to do is write "Texture["clouds"]" and it'll look it up in the AA, and if it's not there, automatically load it. Then when you're done, you call "Texture.release(t)" so that it can remove the texture from the Textures AA. Note that every time you access the texture list with the static opIndex, it increments the ref count if the texture is already in the list, so you'll have to call .release on any ref that you get out of it. Now comes the auto-destruction. What you can do is make a little scope class which controls the destruction: scope class TextureRef { private Texture mTexture; public this(Texture t) { mTexture = t; } ~this() { Texture.release(t); } public Texture tex() { return mTexture; } } Then you'd use it like this: void foo() { scope t = new TextureRef(Texture["clouds"]); // do crap with t.tex } When the function leaves, t will have its destructor called, and so the reference count will be decremented. Note that using scope classes can be very efficient, since when you have the pattern "scope x = new X", it will be allocated on the stack, and no heap allocation is made. This means the TextureRef class is very lightweight. Of course, with this layout, there's nothing binding you to using the TextureRef class; you can do it manually too, if you have i.e. a texture that needs to outlive a single function call. auto t = Texture["clouds"]; ... Texture.release(t);
Jul 06 2007
next sibling parent reply downs <default_357-line yahoo.de> writes:
I've been thinking about a better way to do this.
How about we (ab)use the GC? That is, each returned resource with the 
same index contains a pointer to some throwaway object. This object's 
address is stored together with the AA itself, but encoded somehow 
(negative? xor with a constant?) so that the GC doesn't find this relation.
Now, whenever all resources of that type have been deleted, and the GC 
runs, the object will not have anything obvious pointing towards it 
anymore; thus, the GC will delete it. Before that, it will call the 
object's destructor, which, in turn, will delete the object's entry from 
the AA :)
  --downs

PS: I have no idea if that could work; also, you'd be relying on the GC 
which the spec says _not_ to rely upon. Evil :)
PPS: Not guaranteed to work. Use at own risk.
Jul 06 2007
next sibling parent reply Samuel Winchenbach <swinchen eece.maine.edu> writes:
downs wrote:
 I've been thinking about a better way to do this.
 How about we (ab)use the GC? That is, each returned resource with the 
 same index contains a pointer to some throwaway object. This object's 
 address is stored together with the AA itself, but encoded somehow 
 (negative? xor with a constant?) so that the GC doesn't find this relation.
 Now, whenever all resources of that type have been deleted, and the GC 
 runs, the object will not have anything obvious pointing towards it 
 anymore; thus, the GC will delete it. Before that, it will call the 
 object's destructor, which, in turn, will delete the object's entry from 
 the AA :)
  --downs
 
 PS: I have no idea if that could work; also, you'd be relying on the GC 
 which the spec says _not_ to rely upon. Evil :)
 PPS: Not guaranteed to work. Use at own risk.

Wow, I think that might be a little advanced for me. I am on approximately week 2 of D. Coming from ANSI C (with a touch of C++) this is quite a shock for me. If you have any example code of this in action I would love to see it though :) Sam
Jul 07 2007
parent reply downs <default_357-line yahoo.de> writes:
Samuel Winchenbach Wrote:

 downs wrote:
 I've been thinking about a better way to do this.
 How about we (ab)use the GC? That is, each returned resource with the 
 same index contains a pointer to some throwaway object. This object's 
 address is stored together with the AA itself, but encoded somehow 
 (negative? xor with a constant?) so that the GC doesn't find this relation.
 Now, whenever all resources of that type have been deleted, and the GC 
 runs, the object will not have anything obvious pointing towards it 
 anymore; thus, the GC will delete it. Before that, it will call the 
 object's destructor, which, in turn, will delete the object's entry from 
 the AA :)
  --downs
 
 PS: I have no idea if that could work; also, you'd be relying on the GC 
 which the spec says _not_ to rely upon. Evil :)
 PPS: Not guaranteed to work. Use at own risk.

Wow, I think that might be a little advanced for me. I am on approximately week 2 of D. Coming from ANSI C (with a touch of C++) this is quite a shock for me. If you have any example code of this in action I would love to see it though :) Sam

Sure .. I whipped something up that seems to work. import std.stdio, std.gc; typedef size_t hidden; hidden hide(Object ptr) { return cast(hidden)((cast(size_t)cast(void*)ptr)^0); } /// xor with 0 equals negation Object show(hidden h) { return cast(Object)(h^0); } class refcounter { hidden ptr; this() { ptr=hide(new class(this) { refcounter rc; this(refcounter r) { rc=r; } ~this() { writefln("Destructor called, telling refcounter to clean up"); rc.cleanUp; } }); } Object issueReference() { return new class(show(ptr)) { Object reftest; this(Object obj) { reftest=obj; writefln("Reference counted object constructed"); } ~this() { writefln("Reference counted object destroyed"); } }; } void cleanUp() { writefln("CleanUp called. The real deal would now remove the resource from its buffer."); } } void main() { auto r=new refcounter; auto a=r.issueReference(); auto b=r.issueReference; writefln("When calling fullCollect here, nothing will happen."); std.gc.fullCollect; writefln("Now delete the objects."); delete a; delete b; writefln("Again, fullCollect. But this time: "); std.gc.fullCollect; writefln("Exiting"); } And yeah, it's a little advanced. That's because proper automated reference counting is impossible in D because of missing copy semantics - it's impossible to track the duplication of objects.
Jul 07 2007
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
downs wrote:
 hidden hide(Object ptr) { return cast(hidden)((cast(size_t)cast(void*)ptr)^0);
} /// xor with 0 equals negation

Xor with 0? That's a no-op: version (Tango) import tango.stdc.stdio; void main() { printf("%d\n", 0 ^ 0); printf("%d\n", 1 ^ 0); printf("%d\n", (-1) ^ 0); printf("%d\n", 2 ^ 0); printf("%d\n", (-2) ^ 0); printf("%d\n", 1000 ^ 0); printf("%d\n", (-1000) ^ 0); } If you want bitwise negation, use the ~ unary operator. -- Remove ".doesnotlike.spam" from the mail address.
Jul 07 2007
parent downs <default_357-line yahoo.de> writes:
Deewiant wrote:
 downs wrote:
 
hidden hide(Object ptr) { return cast(hidden)((cast(size_t)cast(void*)ptr)^0);
} /// xor with 0 equals negation

Xor with 0? That's a no-op: version (Tango) import tango.stdc.stdio; void main() { printf("%d\n", 0 ^ 0); printf("%d\n", 1 ^ 0); printf("%d\n", (-1) ^ 0); printf("%d\n", 2 ^ 0); printf("%d\n", (-2) ^ 0); printf("%d\n", 1000 ^ 0); printf("%d\n", (-1000) ^ 0); } If you want bitwise negation, use the ~ unary operator.

Sorry. The reason it still worked was probably because the GC doesn't consider size_t something that can point somewhere, so it didn't consider it when searching for references. What I meant to do was probably along the lines of ^-1. Changed to ~. Thanks :) --downs
Jul 07 2007
prev sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
downs wrote:
 This object's 
 address is stored together with the AA itself, but encoded somehow 
 (negative? xor with a constant?) so that the GC doesn't find this relation.

std.gc has some functions to hide pointers from the collector, perhaps one can use these?
Jul 07 2007
prev sibling parent reply Samuel Winchenbach <swinchen eece.maine.edu> writes:
Thanks Jarrett!

I sort of went with a hybrid approach (for now)

module resource;

class TextureHandle
{
     private Texture mTexture;
     private char[]  mName;
     this(texture aTexture, char[] aName)
     {
         mTexture = aTexture;
         mName = aName;

     }
     ~this()
     {
         mTexture.mRef--;
         if (mTexture.mRef == 0)
         {
             mTexture.freeGPUtexture();
             textureList.remove(aName);
             delete mTexture;
         }
     }

}

TextureHandle loadTexture(char[] aName)
{
     if (auto t = (aName in textureList))
     {
         (*t).mRef++;
         return new TextureHandle(*t, aName);

     }

     auto t = new Texture();
     textureList[aName] = t;
     t.mRef++;
     return new TextureHandle(t, aName);
}

private
{
     Texture[char[]] textureList;

     class Texture
     {
         private uint mRef = 0;
         this()
         {
         }

         ~this()
         {
         }

         void freeGPUtexture()
         {
         }
     }

}


I am trying to think ahead and see if this is what I really want. 
Eventually I will want to implement a file that contains all the various 
resources.   I am not sure if I will run into a scope problem with that 
or not.  I guess I just need to play around with it and see.  :) 
Somehow it also seems I like should be able to make this more generic... 
  instead of having a texture handle, have a generic resource handle. 
Overall it makes a LOT more sense to me in D than when I tried to do the 
same thing in C++ :)

Sam
Jul 07 2007
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Samuel Winchenbach" <swinchen eece.maine.edu> wrote in message 
news:f6o2k2$2gu6$1 digitalmars.com...

 class TextureHandle
 {
     private Texture mTexture;
     private char[]  mName;
     this(texture aTexture, char[] aName)
     {
         mTexture = aTexture;
         mName = aName;

     }
     ~this()
     {
         mTexture.mRef--;
         if (mTexture.mRef == 0)
         {
             mTexture.freeGPUtexture();
             textureList.remove(aName);
             delete mTexture;
         }
     }

 }

The only issue with this is that _technically_ what you're doing in the destructor is illegal. Destructors of non-scope classes aren't guaranteed to be called (i.e. on program exit, they might not be, but the GC will definitely call them). Furthermore, the order of destruction is undefined, meaning that on program exit, mTexture might be destroyed before this, causing a nasty access violation when you try to do "mTexture.ref--". However, you can avoid this ugly nondeterministic-ness by always using scope references to TextureHandle (you can have scope references to non-scope classes, too), or by keeping all instances of TextureHandle in a static/global list which you clean up in a "static ~this()" -- when static dtors are called, the GC has not yet cleaned up, so it's still legal to access reference members in class dtors.
Jul 07 2007
parent reply Samuel Winchenbach <swinchen eece.maine.edu> writes:
Jarrett Billingsley wrote:
 "Samuel Winchenbach" <swinchen eece.maine.edu> wrote in message 
 news:f6o2k2$2gu6$1 digitalmars.com...
 
 class TextureHandle
 {
     private Texture mTexture;
     private char[]  mName;
     this(texture aTexture, char[] aName)
     {
         mTexture = aTexture;
         mName = aName;

     }
     ~this()
     {
         mTexture.mRef--;
         if (mTexture.mRef == 0)
         {
             mTexture.freeGPUtexture();
             textureList.remove(aName);
             delete mTexture;
         }
     }

 }

The only issue with this is that _technically_ what you're doing in the destructor is illegal. Destructors of non-scope classes aren't guaranteed to be called (i.e. on program exit, they might not be, but the GC will definitely call them). Furthermore, the order of destruction is undefined, meaning that on program exit, mTexture might be destroyed before this, causing a nasty access violation when you try to do "mTexture.ref--". However, you can avoid this ugly nondeterministic-ness by always using scope references to TextureHandle (you can have scope references to non-scope classes, too), or by keeping all instances of TextureHandle in a static/global list which you clean up in a "static ~this()" -- when static dtors are called, the GC has not yet cleaned up, so it's still legal to access reference members in class dtors.

Ok... How do you learn these things? I have been referencing the digitalmars docs and well.. it sort of seems incomplete. Anyways here is my next attempt. I have to change it around and get rid of the "loadTexture" function because that can not return scope classes, which makes sense I suppose. btw, thanks for the help! This is certainly and educational experience. module texture; import std.stdio; scope class TextureHandle { private Texture mTexture; private char[] mName; this(char[] aName) { mName = aName; if (auto t = (aName in textureList)) { mTexture = *t; } else { mTexture = new Texture(); textureList[aName] = mTexture; } mTexture.mRef++; } ~this() { mTexture.mRef--; if (mTexture.mRef == 0) { mTexture.freeGPUtexture(); textureList.remove(mName); delete mTexture; } } } private { Texture[char[]] textureList; class Texture { private uint mRef = 0; this() { } ~this() { } void freeGPUtexture() { } } }
Jul 07 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Samuel Winchenbach" <swinchen eece.maine.edu> wrote in message 
news:f6pdft$20g2$1 digitalmars.com...
 Ok...  How do you learn these things?  I have been referencing the 
 digitalmars docs and well.. it sort of seems incomplete.

From using D and posting on the NGs for three years ;) (God, it's been that long?!)
 Anyways here is my next attempt.  I have to change it around and get rid 
 of the "loadTexture" function because that can not return scope classes, 
 which makes sense I suppose.

 btw, thanks for the help!  This is certainly and educational experience.



 module texture;

 import std.stdio;

 scope class TextureHandle
 {
     private Texture mTexture;
     private char[]  mName;

     this(char[] aName)
     { mName = aName;

         if (auto t = (aName in textureList))
         {
             mTexture = *t;
         }
         else
         {
             mTexture = new Texture();
             textureList[aName] = mTexture;
         }

         mTexture.mRef++;

     }

     ~this()
     {
         mTexture.mRef--;
         if (mTexture.mRef == 0)
         {
             mTexture.freeGPUtexture();
             textureList.remove(mName);
             delete mTexture;
         }
     }
 }

 private
 {
     Texture[char[]] textureList;

     class Texture
     {
         private uint mRef = 0;
         this()
         {
         }

         ~this()
         {
         }
         void freeGPUtexture()
         {
         }
     }
 }

Looks good to me.
Jul 07 2007
prev sibling parent reply Samuel Winchenbach <swinchen eece.maine.edu> writes:
Ok, I have come to a number of conclusions...

#1) Scope classes are cool, but in my situation their limitations are 
problematic (not being able to store them in an array for example)  So I 
need a method that doesn't rely on them.

#2) D can not really do not reference counting because lack of copy 
operator.   So maybe I should just ditch reference counting all together 
and try to make it slightly simpler.

auto h = resource.create();
resource.destroy(h);

something like that.  avoid reference counting and just rely on the 
developer using the resource manager to free the resource.

Opinions on this?


Also fonts have been bugging me.  Fonts can have the same file name, but 
different settings (italic, bold, underlined, pt. size, etc...)  so 
instead of having: FontList[char[]] I think I might have 
FontList[FontDescriptor[]].   This seems reasonable right?

So I think my resource manager will have both an AA and a descriptor for 
each type of resource.   Still working on how to make it somewhat 
generic.   I would really like there to only be one type of handle, one 
type of createResource and one type of freeResource.  I am not sure this 
is feasible though.   At this point I am just blabbing...

Thanks for the input,
Sam
Jul 09 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Samuel Winchenbach" <swinchen eece.maine.edu> wrote in message 
news:f6tlr6$1je2$1 digitalmars.com...
 auto h = resource.create();
 resource.destroy(h);

 something like that.  avoid reference counting and just rely on the 
 developer using the resource manager to free the resource.

 Opinions on this?

One thing you can do is forget about the "nondeterministic destruction" rule and free your resources in the dtor of your resource classes anyway (this would also remove the need for a "reference" class -- just use the Texture class directly). Then run the GC periodically. If you're tight with mem allocation in other areas, this could be pretty efficient. The downside is that you still have the possibility of long GC cycles (though you have the possibility of long allocations without a GC too..), and the resources wouldn't be freed _immediately_ as with refcounting. Though, if you called the GC once every 10 seconds or so, I doubt that would be too much of a problem, and if you called the GC right before you loaded a bunch of new resources, for example, it definitely wouldn't be a problem.
 Also fonts have been bugging me.  Fonts can have the same file name, but 
 different settings (italic, bold, underlined, pt. size, etc...)  so 
 instead of having: FontList[char[]] I think I might have 
 FontList[FontDescriptor[]].   This seems reasonable right?

It'd probably be FontList[FontDescriptor], but yeah that sounds fine. As long as two instances of FontDescriptor compared equal with the same members, and as long as they hashed to the same value as well.
 So I think my resource manager will have both an AA and a descriptor for 
 each type of resource.   Still working on how to make it somewhat generic. 
 I would really like there to only be one type of handle, one type of 
 createResource and one type of freeResource.  I am not sure this is 
 feasible though.   At this point I am just blabbing...

Is there any reason why you'd need one kind of resource handle? I mean, you can't use a texture in place of some other resource; wouldn't this be breaking type safety?
Jul 09 2007