Diligent Engine
Loading...
Searching...
No Matches
Diligent::WeakObjectCache< InterfaceType > Class Template Reference

#include <WeakObjectCache.hpp>

Public Member Functions

size_t Size () const noexcept
void Reserve (size_t ExpectedTotalEntries)
template<typename CreateObjectFuncType>
std::pair< RefCntAutoPtr< InterfaceType >, bool > GetOrCreate (const Char *CacheKey, CreateObjectFuncType &&CreateObjectFunc)
bool EraseIfExpired (const Char *CacheKey)
size_t EraseExpired ()

Detailed Description

template<typename InterfaceType>
class Diligent::WeakObjectCache< InterfaceType >

Thread-safe cache for reference-counted objects stored as weak references.

Ensures that at most one object creation is in flight for a given key. Object factories are invoked outside the shard lock, so factories may recursively request other keys from the same cache.

The cache owns only weak references. A cached object may expire when all external strong references are released; the key remains in the map until the object is replaced by a later GetOrCreate() call or explicitly removed by EraseIfExpired().

Example:

WeakObjectCache<IMyObject> Cache;
auto [pObject, Created] =
Cache.GetOrCreate(
"object-key",
[&]() {
return RefCntAutoPtr<IMyObject>{MakeNewRCObj<MyObject>()(...)};
});
if (pObject == nullptr)
{
// The key was invalid, the factory returned null, or another
// in-flight factory for the same key failed.
}
else if (Created)
{
// This call created and published a new object.
}
Template class that implements reference counting.
Definition RefCntAutoPtr.hpp:77
std::pair< RefCntAutoPtr< InterfaceType >, bool > GetOrCreate(const Char *CacheKey, CreateObjectFuncType &&CreateObjectFunc)
Definition WeakObjectCache.hpp:393

Same-thread recursion for the same key is detected and fails:

Cache.GetOrCreate("X", [&] {
return Cache.GetOrCreate("X", ...).first; // returns null
});

Cross-thread dependency cycles are not detected and may deadlock:

// Thread A creates key X and, from its factory, requests key Y.
// Thread B creates key Y and, from its factory, requests key X.
// Both threads can wait for each other's in-flight creation.

Member Function Documentation

◆ EraseExpired()

template<typename InterfaceType>
size_t Diligent::WeakObjectCache< InterfaceType >::EraseExpired ( )
inline

Removes all keys that have no live object or in-flight operation.

Returns the number of entries actually erased.

◆ EraseIfExpired()

template<typename InterfaceType>
bool Diligent::WeakObjectCache< InterfaceType >::EraseIfExpired ( const Char * CacheKey)
inline

Removes the key if it exists and no live object or in-flight operation is associated with it.

Returns true only when the entry is actually erased.

Returns false if the key is missing, the object is still live, another thread currently holds the entry for creation or waiting, or the key is null or empty. Null or empty keys also log an error.

◆ GetOrCreate()

template<typename InterfaceType>
template<typename CreateObjectFuncType>
std::pair< RefCntAutoPtr< InterfaceType >, bool > Diligent::WeakObjectCache< InterfaceType >::GetOrCreate ( const Char * CacheKey,
CreateObjectFuncType && CreateObjectFunc )
inline

Returns a live object for the key, creating one if needed.

If a live object already exists, the method returns it with Created set to false.

If the key is missing or the weak object has expired, one caller becomes the creator and invokes CreateObjectFunc outside the shard lock. Other callers for the same key wait for that creation attempt to finish.

If creation succeeds, the created object is stored as a weak reference and returned to the creator with Created set to true. Waiting callers retry the lookup. They usually return the newly created object with Created set to false, but because the cache stores only a weak reference, the object may already have expired if no external strong reference remains.

If CreateObjectFunc returns null, the creator logs an error and returns null with Created set to false. Waiting callers wake and may return null with Created set to false. The failed placeholder entry is removed once no current caller still references it, and a later call may retry creation.

If CreateObjectFunc throws, the exception is propagated to the creator. Waiting callers wake and may return null with Created set to false. A later call may retry creation. As with null factory results, the failed placeholder entry is removed once current callers release it.

A waiter retries after the creation attempt it observed succeeds. If it wakes after later attempts have already completed, it retries the normal lookup path instead of using a result from the wrong generation. If the exact attempt it observed fails, the waiter returns null with Created set to false.

If the key is null or empty, the method logs an error and returns null with Created set to false without invoking CreateObjectFunc.

If a factory recursively requests the same key on the same thread, the recursive call logs an error and returns null with Created set to false.

The factory may request other keys from the same cache, but callers must avoid cross-thread dependency cycles between keys.

◆ Reserve()

template<typename InterfaceType>
void Diligent::WeakObjectCache< InterfaceType >::Reserve ( size_t ExpectedTotalEntries)
inline

Reserves space for approximately ExpectedTotalEntries map entries.

The reservation is distributed evenly between cache shards. This can be used before bulk loading to reduce unordered_map rehashes while shard locks are contended.

◆ Size()

template<typename InterfaceType>
size_t Diligent::WeakObjectCache< InterfaceType >::Size ( ) const
inlinenoexcept

Returns the number of map entries in the cache.

This method is non-blocking and reads a single atomic counter. The returned value includes live entries, expired entries that have not been erased yet, and in-flight creation placeholders. Under concurrent modification, the value is a snapshot and may become stale immediately.