Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Core/LockFreeHashMap.h
9906 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#pragma once
6
7
#include <Jolt/Core/NonCopyable.h>
8
#include <Jolt/Core/Atomics.h>
9
10
JPH_NAMESPACE_BEGIN
11
12
/// Allocator for a lock free hash map
13
class LFHMAllocator : public NonCopyable
14
{
15
public:
16
/// Destructor
17
inline ~LFHMAllocator();
18
19
/// Initialize the allocator
20
/// @param inObjectStoreSizeBytes Number of bytes to reserve for all key value pairs
21
inline void Init(uint inObjectStoreSizeBytes);
22
23
/// Clear all allocations
24
inline void Clear();
25
26
/// Allocate a new block of data
27
/// @param inBlockSize Size of block to allocate (will potentially return a smaller block if memory is full).
28
/// @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.
29
/// @param ioEnd Should be the byte beyond the current memory block on input, will contain the byte beyond the allocated block on return.
30
inline void Allocate(uint32 inBlockSize, uint32 &ioBegin, uint32 &ioEnd);
31
32
/// Convert a pointer to an offset
33
template <class T>
34
inline uint32 ToOffset(const T *inData) const;
35
36
/// Convert an offset to a pointer
37
template <class T>
38
inline T * FromOffset(uint32 inOffset) const;
39
40
private:
41
uint8 * mObjectStore = nullptr; ///< This contains a contiguous list of objects (possibly of varying size)
42
uint32 mObjectStoreSizeBytes = 0; ///< The size of mObjectStore in bytes
43
atomic<uint32> mWriteOffset { 0 }; ///< Next offset to write to in mObjectStore
44
};
45
46
/// Allocator context object for a lock free hash map that allocates a larger memory block at once and hands it out in smaller portions.
47
/// This avoids contention on the atomic LFHMAllocator::mWriteOffset.
48
class LFHMAllocatorContext : public NonCopyable
49
{
50
public:
51
/// Construct a new allocator context
52
inline LFHMAllocatorContext(LFHMAllocator &inAllocator, uint32 inBlockSize);
53
54
/// @brief Allocate data block
55
/// @param inSize Size of block to allocate.
56
/// @param inAlignment Alignment of block to allocate.
57
/// @param outWriteOffset Offset in buffer where block is located
58
/// @return True if allocation succeeded
59
inline bool Allocate(uint32 inSize, uint32 inAlignment, uint32 &outWriteOffset);
60
61
private:
62
LFHMAllocator & mAllocator;
63
uint32 mBlockSize;
64
uint32 mBegin = 0;
65
uint32 mEnd = 0;
66
};
67
68
/// Very simple lock free hash map that only allows insertion, retrieval and provides a fixed amount of buckets and fixed storage.
69
/// Note: This class currently assumes key and value are simple types that need no calls to the destructor.
70
template <class Key, class Value>
71
class LockFreeHashMap : public NonCopyable
72
{
73
public:
74
using MapType = LockFreeHashMap<Key, Value>;
75
76
/// Destructor
77
explicit LockFreeHashMap(LFHMAllocator &inAllocator) : mAllocator(inAllocator) { }
78
~LockFreeHashMap();
79
80
/// Initialization
81
/// @param inMaxBuckets Max amount of buckets to use in the hashmap. Must be power of 2.
82
void Init(uint32 inMaxBuckets);
83
84
/// Remove all elements.
85
/// Note that this cannot happen simultaneously with adding new elements.
86
void Clear();
87
88
/// Get the current amount of buckets that the map is using
89
uint32 GetNumBuckets() const { return mNumBuckets; }
90
91
/// Get the maximum amount of buckets that this map supports
92
uint32 GetMaxBuckets() const { return mMaxBuckets; }
93
94
/// 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.
95
/// 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.
96
void SetNumBuckets(uint32 inNumBuckets);
97
98
/// A key / value pair that is inserted in the map
99
class KeyValue
100
{
101
public:
102
const Key & GetKey() const { return mKey; }
103
Value & GetValue() { return mValue; }
104
const Value & GetValue() const { return mValue; }
105
106
private:
107
template <class K, class V> friend class LockFreeHashMap;
108
109
Key mKey; ///< Key for this entry
110
uint32 mNextOffset; ///< Offset in mObjectStore of next KeyValue entry with same hash
111
Value mValue; ///< Value for this entry + optionally extra bytes
112
};
113
114
/// Insert a new element, returns null if map full.
115
/// Multiple threads can be inserting in the map at the same time.
116
template <class... Params>
117
inline KeyValue * Create(LFHMAllocatorContext &ioContext, const Key &inKey, uint64 inKeyHash, int inExtraBytes, Params &&... inConstructorParams);
118
119
/// Find an element, returns null if not found
120
inline const KeyValue * Find(const Key &inKey, uint64 inKeyHash) const;
121
122
/// Value of an invalid handle
123
const static uint32 cInvalidHandle = uint32(-1);
124
125
/// Get convert key value pair to uint32 handle
126
inline uint32 ToHandle(const KeyValue *inKeyValue) const;
127
128
/// Convert uint32 handle back to key and value
129
inline const KeyValue * FromHandle(uint32 inHandle) const;
130
131
#ifdef JPH_ENABLE_ASSERTS
132
/// Get the number of key value pairs that this map currently contains.
133
/// Available only when asserts are enabled because adding elements creates contention on this atomic and negatively affects performance.
134
inline uint32 GetNumKeyValues() const { return mNumKeyValues; }
135
#endif // JPH_ENABLE_ASSERTS
136
137
/// Get all key/value pairs
138
inline void GetAllKeyValues(Array<const KeyValue *> &outAll) const;
139
140
/// Non-const iterator
141
struct Iterator
142
{
143
/// Comparison
144
bool operator == (const Iterator &inRHS) const { return mMap == inRHS.mMap && mBucket == inRHS.mBucket && mOffset == inRHS.mOffset; }
145
bool operator != (const Iterator &inRHS) const { return !(*this == inRHS); }
146
147
/// Convert to key value pair
148
KeyValue & operator * ();
149
150
/// Next item
151
Iterator & operator ++ ();
152
153
MapType * mMap;
154
uint32 mBucket;
155
uint32 mOffset;
156
};
157
158
/// Iterate over the map, note that it is not safe to do this in parallel to Clear().
159
/// 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.
160
Iterator begin();
161
Iterator end();
162
163
#ifdef JPH_DEBUG
164
/// Output stats about this map to the log
165
void TraceStats() const;
166
#endif
167
168
private:
169
LFHMAllocator & mAllocator; ///< Allocator used to allocate key value pairs
170
171
#ifdef JPH_ENABLE_ASSERTS
172
atomic<uint32> mNumKeyValues = 0; ///< Number of key value pairs in the store
173
#endif // JPH_ENABLE_ASSERTS
174
175
atomic<uint32> * mBuckets = nullptr; ///< This contains the offset in mObjectStore of the first object with a particular hash
176
uint32 mNumBuckets = 0; ///< Current number of buckets
177
uint32 mMaxBuckets = 0; ///< Maximum number of buckets
178
};
179
180
JPH_NAMESPACE_END
181
182
#include "LockFreeHashMap.inl"
183
184