Path: blob/master/thirdparty/jolt_physics/Jolt/Core/LockFreeHashMap.h
9906 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#pragma once56#include <Jolt/Core/NonCopyable.h>7#include <Jolt/Core/Atomics.h>89JPH_NAMESPACE_BEGIN1011/// Allocator for a lock free hash map12class LFHMAllocator : public NonCopyable13{14public:15/// Destructor16inline ~LFHMAllocator();1718/// Initialize the allocator19/// @param inObjectStoreSizeBytes Number of bytes to reserve for all key value pairs20inline void Init(uint inObjectStoreSizeBytes);2122/// Clear all allocations23inline void Clear();2425/// Allocate a new block of data26/// @param inBlockSize Size of block to allocate (will potentially return a smaller block if memory is full).27/// @param ioBegin Should be the start of the first free byte in current memory block on input, will contain the start of the first free byte in allocated block on return.28/// @param ioEnd Should be the byte beyond the current memory block on input, will contain the byte beyond the allocated block on return.29inline void Allocate(uint32 inBlockSize, uint32 &ioBegin, uint32 &ioEnd);3031/// Convert a pointer to an offset32template <class T>33inline uint32 ToOffset(const T *inData) const;3435/// Convert an offset to a pointer36template <class T>37inline T * FromOffset(uint32 inOffset) const;3839private:40uint8 * mObjectStore = nullptr; ///< This contains a contiguous list of objects (possibly of varying size)41uint32 mObjectStoreSizeBytes = 0; ///< The size of mObjectStore in bytes42atomic<uint32> mWriteOffset { 0 }; ///< Next offset to write to in mObjectStore43};4445/// Allocator context object for a lock free hash map that allocates a larger memory block at once and hands it out in smaller portions.46/// This avoids contention on the atomic LFHMAllocator::mWriteOffset.47class LFHMAllocatorContext : public NonCopyable48{49public:50/// Construct a new allocator context51inline LFHMAllocatorContext(LFHMAllocator &inAllocator, uint32 inBlockSize);5253/// @brief Allocate data block54/// @param inSize Size of block to allocate.55/// @param inAlignment Alignment of block to allocate.56/// @param outWriteOffset Offset in buffer where block is located57/// @return True if allocation succeeded58inline bool Allocate(uint32 inSize, uint32 inAlignment, uint32 &outWriteOffset);5960private:61LFHMAllocator & mAllocator;62uint32 mBlockSize;63uint32 mBegin = 0;64uint32 mEnd = 0;65};6667/// Very simple lock free hash map that only allows insertion, retrieval and provides a fixed amount of buckets and fixed storage.68/// Note: This class currently assumes key and value are simple types that need no calls to the destructor.69template <class Key, class Value>70class LockFreeHashMap : public NonCopyable71{72public:73using MapType = LockFreeHashMap<Key, Value>;7475/// Destructor76explicit LockFreeHashMap(LFHMAllocator &inAllocator) : mAllocator(inAllocator) { }77~LockFreeHashMap();7879/// Initialization80/// @param inMaxBuckets Max amount of buckets to use in the hashmap. Must be power of 2.81void Init(uint32 inMaxBuckets);8283/// Remove all elements.84/// Note that this cannot happen simultaneously with adding new elements.85void Clear();8687/// Get the current amount of buckets that the map is using88uint32 GetNumBuckets() const { return mNumBuckets; }8990/// Get the maximum amount of buckets that this map supports91uint32 GetMaxBuckets() const { return mMaxBuckets; }9293/// Update the number of buckets. This must be done after clearing the map and cannot be done concurrently with any other operations on the map.94/// Note that the number of buckets can never become bigger than the specified max buckets during initialization and that it must be a power of 2.95void SetNumBuckets(uint32 inNumBuckets);9697/// A key / value pair that is inserted in the map98class KeyValue99{100public:101const Key & GetKey() const { return mKey; }102Value & GetValue() { return mValue; }103const Value & GetValue() const { return mValue; }104105private:106template <class K, class V> friend class LockFreeHashMap;107108Key mKey; ///< Key for this entry109uint32 mNextOffset; ///< Offset in mObjectStore of next KeyValue entry with same hash110Value mValue; ///< Value for this entry + optionally extra bytes111};112113/// Insert a new element, returns null if map full.114/// Multiple threads can be inserting in the map at the same time.115template <class... Params>116inline KeyValue * Create(LFHMAllocatorContext &ioContext, const Key &inKey, uint64 inKeyHash, int inExtraBytes, Params &&... inConstructorParams);117118/// Find an element, returns null if not found119inline const KeyValue * Find(const Key &inKey, uint64 inKeyHash) const;120121/// Value of an invalid handle122const static uint32 cInvalidHandle = uint32(-1);123124/// Get convert key value pair to uint32 handle125inline uint32 ToHandle(const KeyValue *inKeyValue) const;126127/// Convert uint32 handle back to key and value128inline const KeyValue * FromHandle(uint32 inHandle) const;129130#ifdef JPH_ENABLE_ASSERTS131/// Get the number of key value pairs that this map currently contains.132/// Available only when asserts are enabled because adding elements creates contention on this atomic and negatively affects performance.133inline uint32 GetNumKeyValues() const { return mNumKeyValues; }134#endif // JPH_ENABLE_ASSERTS135136/// Get all key/value pairs137inline void GetAllKeyValues(Array<const KeyValue *> &outAll) const;138139/// Non-const iterator140struct Iterator141{142/// Comparison143bool operator == (const Iterator &inRHS) const { return mMap == inRHS.mMap && mBucket == inRHS.mBucket && mOffset == inRHS.mOffset; }144bool operator != (const Iterator &inRHS) const { return !(*this == inRHS); }145146/// Convert to key value pair147KeyValue & operator * ();148149/// Next item150Iterator & operator ++ ();151152MapType * mMap;153uint32 mBucket;154uint32 mOffset;155};156157/// Iterate over the map, note that it is not safe to do this in parallel to Clear().158/// It is safe to do this while adding elements to the map, but newly added elements may or may not be returned by the iterator.159Iterator begin();160Iterator end();161162#ifdef JPH_DEBUG163/// Output stats about this map to the log164void TraceStats() const;165#endif166167private:168LFHMAllocator & mAllocator; ///< Allocator used to allocate key value pairs169170#ifdef JPH_ENABLE_ASSERTS171atomic<uint32> mNumKeyValues = 0; ///< Number of key value pairs in the store172#endif // JPH_ENABLE_ASSERTS173174atomic<uint32> * mBuckets = nullptr; ///< This contains the offset in mObjectStore of the first object with a particular hash175uint32 mNumBuckets = 0; ///< Current number of buckets176uint32 mMaxBuckets = 0; ///< Maximum number of buckets177};178179JPH_NAMESPACE_END180181#include "LockFreeHashMap.inl"182183184