carb::extras::HandleDatabase
Defined in carb/extras/HandleDatabase.h
-
template<class Mapped, class Handle, class Allocator = std::allocator<Mapped>>
class HandleDatabase Provides an OS-style mapping of a Handle to a Resource.
Essentially HandleDatabase is a fast thread-safe reference-counted associative container.
A given Handle value is typically never reused, however, it is possible for a handle value to be reused if billions of handles are requested.
- Thread Safety
Unless otherwise mentioned, HandleDatabase functions may be called simultaneously from multiple threads.
The HandleDatabase can store at most
(2^31)-1
items, or 2,147,483,647 items simultaneously, provided the memory exists to support it.Implementation Details:
HandleDatabase achieves its speed and thread-safety by having a fixed-size base array (
m_database
) where each element is an array of size 2^(base array index). As such, these arrays double the size of the previous index array. These base arrays are never freed, simplifying thread safety and allowing HandleDatabase to be mostly lockless. This array-of-arrays forms a non-contiguous indexable array where the base array and offset can be computed from a 32-bit index value (seeindexToBucketAndOffset
).The Handle is a 64-bit value composed of two 32-bit values. The least-significant 32 bits is the index value into the array-of-arrays described above. The most-significant 32 bits is a lifecycle counter. Every time a new Mapped object is constructed, the lifecycle counter is incremented. This value forms a contract between a handle and the Mapped object at the index indicated by the Handle. If the lifecycle counter doesn’t match, the Handle is invalid. As this lifecycle counter is incremented, there is the possibility of rollover after 2^31 handles are generated at a given index. The most significant bit will then be set as a rollover flag so that handleWasValid() continues to operate correctly. The handleWasValid() function returns
true
for any Handle where the lifecycle counter at the given index is greater-than-or-equal to the Handle’s lifecycle counter, or if the rollover flag is set.- Template Parameters
Mapped – The “value” type that is associated with a Handle.
Handle – The “key” type. Must be an unsigned integer or pointer type and 64-bit. This is an opaque type that cannot be dereferenced.
Allocator – The allocator that will be used to allocate memory. Must be capable of allocating large contiguous blocks of memory.
Public Types
-
using HandleRefType = HandleRef<Mapped, Handle, Allocator>
An alias to the HandleRef for this HandleDatabase.
-
using ConstHandleRefType = ConstHandleRef<Mapped, Handle, Allocator>
An alias to the ConstHandleRef for this HandleDatabase.
-
using scoped_handle_ref_type = HandleRefType
Deprecated: Use HandleRefType instead.
Public Functions
-
inline constexpr HandleDatabase() noexcept
Constructor.
- Thread Safety
The constructor must complete in a single thread before any other functions may be called on
*this
.
-
inline ~HandleDatabase()
Destructor.
- Thread Safety
All other functions on
*this
must be finished before the destructor can be called.
-
inline bool handleWasValid(Handle handle) const
Checks to see if a handle is valid or was ever valid in the past.
- Parameters
handle – The Handle to check.
- Returns
true
if ahandle
is currently valid or was valid in the past and has since been released;false
otherwise.
-
inline bool handleIsValid(Handle handle) const
Checks to see if a handle is currently valid.
- Parameters
handle – The Handle to check.
- Returns
true
if ahandle
is currently valid;false
otherwise.
-
template<class ...Args>
inline std::pair<Handle, Mapped*> createHandle(Args&&... args) Creates a new Mapped type with the given arguments.
- Parameters
args – The arguments to pass to the Mapped constructor.
- Returns
A
std::pair
of the Handle that can uniquely identify the new Mapped type, and a pointer to the newly constructed Mapped type.
-
inline Mapped *getValueFromHandle(Handle handle) const
Attempts to find the Mapped type represented by
handle
.- Thread Safety
Not thread-safe as the Mapped type could be destroyed if another thread calls release().
- Parameters
handle – A handle previously returned from createHandle(). Invalid or previously-valid handles will merely result in a
nullptr
result without an assert or any other side-effects.- Returns
A pointer to a valid Mapped type if the handle was valid;
nullptr
otherwise.
-
inline Handle getHandleFromValue(const Mapped *mapped) const
Retrieves the Handle representing
mapped
.Warning
Providing
mapped
that is no longer valid or was not returned from one of the HandleDatabase functions is undefined.- Parameters
mapped – A Mapped type previously created with createHandle().
- Returns
The Handle representing
mapped
.
-
inline Mapped *tryAddRef(Handle handle)
Atomically attempts to add a reference for the given Handle.
- Parameters
handle – A handle previously returned from createHandle(). Invalid or previously-valid handles will merely result in a
nullptr
result without an assert or any other side-effects.- Returns
A pointer to a valid Mapped type if a reference could be added;
nullptr
otherwise.
-
inline void addRef(Handle handle)
Atomically adds a reference for the given Handle; fatal if
handle
is invalid.- Parameters
handle – A valid handle previously returned from createHandle(). Invalid or previously-valid handles will result in
std::terminate()
being called.
-
inline void addRef(Mapped *mapped)
Atomically adds a reference for the Handle representing
mapped
.Warning
Providing
mapped
that is no longer valid or was not returned from one of the HandleDatabase functions is undefined.- Parameters
mapped – A Mapped type previously created with createHandle().
-
inline bool release(Handle handle)
Atomically releases a reference for the given Handle, potentially freeing the associated Mapped type.
- Parameters
handle – A valid handle previously returned from createHandle(). Invalid or previously-valid handles will result in an assert in Debug builds, but return
false
with no side effects in Release builds.- Returns
true
if the last reference was released andhandle
is no longer valid;false
if Handle is not valid or is previously-valid or a non-final reference was released.
-
inline bool release(Mapped *mapped)
Atomically releases a reference for the Handle representing
mapped
.Warning
Provided
mapped
that is no longer valid or was not returned from one of the HandleDatabase functions is undefined.- Parameters
mapped – A Mapped type previously created with createHandle().
- Returns
true
if the last reference was released andmapped
is no longer valid;false
if a reference other than the last reference was released.
-
inline bool releaseIfLastRef(Handle handle)
Atomically releases a reference if and only if it’s the last reference.
- Parameters
handle – A valid handle previously returned from createHandle(). Invalid or previously-valid handles will result in an assert in Debug builds, but return
false
with no side effects in Release builds.- Returns
true
if the last reference was released andhandle
is no longer valid;false
otherwise.
-
inline bool releaseIfLastRef(Mapped *mapped)
Atomically releases a reference if and only if it’s the last reference.
Warning
Provided
mapped
that is no longer valid or was not returned from one of the HandleDatabase functions is undefined.- Parameters
mapped – A Mapped type previously created with createHandle().
- Returns
true
if the last reference was released andmapped
is no longer valid;false
otherwise.
-
inline HandleRefType makeScopedRef(Handle handle)
Attempts to atomically add a reference to
handle
, and returns a HandleRef tohandle
.- Parameters
handle – A handle previously returned from createHandle(). Invalid or previously-valid handles will merely result in an empty HandleRef result without an assert or any other side-effects.
- Returns
If tryAddRef() would return a valid Mapped type for
handle
, then a HandleRef that manages the reference is returned; otherwise an empty HandleRef is returned.
-
inline ConstHandleRefType makeScopedRef(Handle handle) const
Attempts to atomically add a reference to
handle
, and returns a ConstHandleRef tohandle
.- Parameters
handle – A handle previously returned from createHandle(). Invalid or previously-valid handles will merely result in an empty ConstHandleRef result without an assert or any other side-effects.
- Returns
If tryAddRef() would return a valid Mapped type for
handle
, then a ConstHandleRef that manages the reference is returned; otherwise an empty ConstHandleRef is returned.
-
template<class Func>
inline void forEachHandle(Func &&f) const Calls the provided
Func
invocable object for each valid handle and its associated mapped type.- Thread Safety
The
Mapped*
passed tof
is not safe to use if any other thread would be calling releaseIfLastRef() or release() to release the last reference of a Handle. In these situations it is instead recommended to try to add a reference to theHandle
passed tof
in order to assure validity.
- Parameters
f – An invocable object with signature
void(Handle, Mapped*)
. Note that there also exists a version offorEachHandle
that can acceptbool(Handle, Mapped*)
and can early out. Technically any non-bool return type is allowed but any return values are ignored. It is highly recommended to ignore theMapped*
unless it is guaranteed that no other threads may be modifying theHandleDatabase
, in which case it is recommend to instead try to add a reference to theHandle
in order to assure validity.
-
template<class Func>
inline bool forEachHandle(Func &&f) const Calls the provided
Func
invocable object for each valid handle and its associated mapped type until the invocable object returnsfalse
.- Thread Safety
The
Mapped*
passed tof
is not safe to use if any other thread would be calling releaseIfLastRef() or release() to release the last reference of a Handle. In these situations it is instead recommended to try to add a reference to theHandle
passed tof
in order to assure validity.
- Parameters
f – An invocable object with signature
bool(Handle, Mapped*)
. If the invocable object returnsfalse
, iteration terminates immediately and returnsfalse
. It is highly recommended to ignore theMapped*
unless it is guaranteed that no other threads may be modifying theHandleDatabase
, in which case it is recommend to instead try to add a reference to theHandle
in order to assure validity.- Returns
true
if iteration completed over the entirety of*this
;false
iff
returnedfalse
causing iteration to be aborted early.
-
inline size_t clear()
Iterates over all valid handles and sets their reference counts to zero, destroying the associated mapped type.
- Thread Safety
This function is NOT safe to call if any other thread is calling ANY other HandleDatabase function except for clear() or handleWasValid().
- Returns
The number of handles that were released to zero.