digitalmars.D.learn - Resource Management... Howto?
- Samuel Winchenbach (36/36) Jul 06 2007 Hi there.
- Jarrett Billingsley (79/90) Jul 06 2007 You have the basic idea right. Say one of your resource classes (for
- downs (14/14) Jul 06 2007 I've been thinking about a better way to do this.
- Samuel Winchenbach (6/21) Jul 07 2007 Wow, I think that might be a little advanced for me. I am on
- downs (35/59) Jul 07 2007 Sure .. I whipped something up that seems to work.
- Lutger (3/6) Jul 07 2007 std.gc has some functions to hide pointers from the collector, perhaps
- Samuel Winchenbach (61/61) Jul 07 2007 Thanks Jarrett!
- Jarrett Billingsley (14/34) Jul 07 2007 The only issue with this is that _technically_ what you're doing in the
- Samuel Winchenbach (55/96) Jul 07 2007 Ok... How do you learn these things? I have been referencing the
- Jarrett Billingsley (5/58) Jul 07 2007 From using D and posting on the NGs for three years ;)
- Samuel Winchenbach (23/23) Jul 09 2007 Ok, I have come to a number of conclusions...
- Jarrett Billingsley (19/33) Jul 09 2007 One thing you can do is forget about the "nondeterministic destruction" ...
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
"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
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
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
Samuel Winchenbach Wrote:downs wrote: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.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
downs wrote:hidden hide(Object ptr) { return cast(hidden)((cast(size_t)cast(void*)ptr)^0); } /// xor with 0 equals negationXor 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
Deewiant wrote:downs wrote:Stupid me. 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 :) --downshidden hide(Object ptr) { return cast(hidden)((cast(size_t)cast(void*)ptr)^0); } /// xor with 0 equals negationXor 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.
Jul 07 2007
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
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
"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
Jarrett Billingsley wrote:"Samuel Winchenbach" <swinchen eece.maine.edu> wrote in message news:f6o2k2$2gu6$1 digitalmars.com...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() { } } }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
"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
Ok, I have come to a number of conclusions... problematic (not being able to store them in an array for example) So I need a method that doesn't rely on them. 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
"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