Using referenceCounters

Rene Zwanenburg via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Wed Jun 1 13:36:15 PDT 2016


On Wednesday, 1 June 2016 at 18:14:33 UTC, Begah wrote:
> I started using reference counters for my assets in my 
> application :
>  - Images
>  - Models
>  - ....

For my resource manager I started out with something similar to 
what you're describing, but I eventually changed the design which 
turned out to be an immense improvement so I'll just describe 
that ^^

- Differentiate between the type implementing a resource, and 
handles to a resource.
- Split up the resource manager, which stores the 'real' resource 
for a given handle, and the cache where you look up resources by 
name.

A stripped down version of the resource manager looks something 
like this:

============

template ResourceManager(ResourceType)
{
   struct HandleImpl
   {
     ~this()
     {
       // Destroy the resource, make sure you mark the 
corresponding slot in the array as free
     }

     size_t slot;
   }

   alias Handle = RefCounted!HandleImpl;

   private ResourceType[] resources;

   // When creating a resource add it to the manager and pass 
around the opaque handle instead.
   Handle add(ResourceType resource)
   {
     // Find an empty slot in the resources array, put the 
resource there, and return a handle pointing to that slot.
   }

   // This should be used sparingly. Most code shouldn't care 
about resource implementation. The parts that do can get at it 
via this function, but shouldn't hold on to the reference longer 
than necessary.
   ResourceType getActualResource(Handle handle) { ... }

   // This allows you to hot-swap resources while keeping the 
resource implementation logically const. Hot-swapping is 
extremely valuable, I've always found myself wanting to support 
it sooner or later. I've tried to do it in different ways, but it 
always ended up in a mess. Using handles on the other hand turned 
out to be clean and easy.
   void update(Handle handle, ResourceType newResource) { ... }
}

============

Note that it doesn't allow you to search for resource by name or 
anything, this is just a simple mapping between handles and the 
resource implementation. It doesn't store instances of the 
RefCounted handles itself, so there are no issues with resources 
not being cleaned up.

Now when you have this in place you can build a caching mechanism 
on top of it:

============

template ResourceCache(ResourceType, alias loadByName)
{
   alias Manager = ResourceManager!ResourceType;

   Manager.Handle[string] cache;

   Manager.Handle get(string name)
   {
     // Check if the resource is already in the cache, if not load 
it using the loadByName function and store it.
   }

   void purge()
   {
     // the keys property allocates a new array with the AA keys, 
which is important since we're modifying the AA.
     foreach(key; cache.keys)
     {
       // This is the part you were actually interested in. It's 
important to take a pointer to the handle here, and not reference 
it directly. I had some issues with that in the past, I'm not 
sure if that's still the case.
       auto ptr = key in cache;
       if(ptr.refCountedStore.refCount == 1) // If this is the 
last reference
       {
         destroy(*ptr); // I'm not sure if this is still 
necessary, there was some issue with AAs in the past. It may be 
fixed today. Destroying the handle doesn't hurt either way so I 
left it in.
         cache.remove(key);
       }
     }
   }
}

============

So cached resources don't get cleaned automatically if you don't 
call purge, but that's usually the right thing to do anyways. For 
example when you're changing scenes (I assume this is for some 
kind of game?) you can simply destroy the current scene, load 
another, then purge the cache, without having to reload resources 
used by both scene 1 and 2.

Another upside is that this doesn't require every resource to be 
named / backed by file. Resources can be dynamically generated at 
runtime without special casing anywhere.


More information about the Digitalmars-d-learn mailing list