32
32
#define RID_OWNER_H
33
33
34
34
#include " core/os/memory.h"
35
- #include " core/os/mutex .h"
35
+ #include " core/os/spin_lock .h"
36
36
#include " core/string/print_string.h"
37
37
#include " core/templates/hash_set.h"
38
38
#include " core/templates/list.h"
43
43
#include < stdio.h>
44
44
#include < typeinfo>
45
45
46
+ #ifdef SANITIZERS_ENABLED
47
+ #ifdef __has_feature
48
+ #if __has_feature(thread_sanitizer)
49
+ #define TSAN_ENABLED
50
+ #endif
51
+ #elif defined(__SANITIZE_THREAD__)
52
+ #define TSAN_ENABLED
53
+ #endif
54
+ #endif
55
+
56
+ #ifdef TSAN_ENABLED
57
+ #include < sanitizer/tsan_interface.h>
58
+ #endif
59
+
46
60
class RID_AllocBase {
47
61
static SafeNumeric<uint64_t > base_id;
48
62
@@ -83,18 +97,18 @@ class RID_Alloc : public RID_AllocBase {
83
97
84
98
const char *description = nullptr ;
85
99
86
- mutable Mutex mutex ;
100
+ AcqRelSpinLock spin ;
87
101
88
102
_FORCE_INLINE_ RID _allocate_rid () {
89
103
if constexpr (THREAD_SAFE) {
90
- mutex .lock ();
104
+ spin .lock ();
91
105
}
92
106
93
107
if (alloc_count == max_alloc) {
94
108
// allocate a new chunk
95
109
uint32_t chunk_count = alloc_count == 0 ? 0 : (max_alloc / elements_in_chunk);
96
110
if (THREAD_SAFE && chunk_count == chunk_limit) {
97
- mutex .unlock ();
111
+ spin .unlock ();
98
112
if (description != nullptr ) {
99
113
ERR_FAIL_V_MSG (RID (), vformat (" Element limit for RID of type '%s' reached." , String (description)));
100
114
} else {
@@ -120,7 +134,8 @@ class RID_Alloc : public RID_AllocBase {
120
134
free_list_chunks[chunk_count][i] = alloc_count + i;
121
135
}
122
136
123
- max_alloc += elements_in_chunk;
137
+ // Store atomically to avoid data race with the load in get_or_null().
138
+ ((std::atomic<uint32_t > *)&max_alloc)->store (max_alloc + elements_in_chunk, std::memory_order_relaxed);
124
139
}
125
140
126
141
uint32_t free_index = free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk];
@@ -140,7 +155,7 @@ class RID_Alloc : public RID_AllocBase {
140
155
alloc_count++;
141
156
142
157
if constexpr (THREAD_SAFE) {
143
- mutex .unlock ();
158
+ spin .unlock ();
144
159
}
145
160
146
161
return _make_from_id (id);
@@ -168,9 +183,13 @@ class RID_Alloc : public RID_AllocBase {
168
183
return nullptr ;
169
184
}
170
185
186
+ spin.acquire ();
187
+
171
188
uint64_t id = p_rid.get_id ();
172
189
uint32_t idx = uint32_t (id & 0xFFFFFFFF );
173
- if (unlikely (idx >= max_alloc)) {
190
+ // Read atomically to avoid data race with the store in _allocate_rid().
191
+ uint32_t ma = ((std::atomic<uint32_t > *)&max_alloc)->load (std::memory_order_acquire);
192
+ if (unlikely (idx >= ma)) {
174
193
return nullptr ;
175
194
}
176
195
@@ -180,6 +199,9 @@ class RID_Alloc : public RID_AllocBase {
180
199
uint32_t validator = uint32_t (id >> 32 );
181
200
182
201
Chunk &c = chunks[idx_chunk][idx_element];
202
+ #ifdef TSAN_ENABLED
203
+ __tsan_acquire (&c.validator ); // We know not a race in practice.
204
+ #endif
183
205
if (unlikely (p_initialize)) {
184
206
if (unlikely (!(c.validator & 0x80000000 ))) {
185
207
ERR_FAIL_V_MSG (nullptr , " Initializing already initialized RID" );
@@ -189,14 +211,18 @@ class RID_Alloc : public RID_AllocBase {
189
211
ERR_FAIL_V_MSG (nullptr , " Attempting to initialize the wrong RID" );
190
212
}
191
213
192
- c.validator &= 0x7FFFFFFF ; // initialized
214
+ // Mark initialized.
215
+ c.validator &= 0x7FFFFFFF ;
193
216
194
217
} else if (unlikely (c.validator != validator)) {
195
218
if ((c.validator & 0x80000000 ) && c.validator != 0xFFFFFFFF ) {
196
219
ERR_FAIL_V_MSG (nullptr , " Attempting to use an uninitialized RID" );
197
220
}
198
221
return nullptr ;
199
222
}
223
+ #ifdef TSAN_ENABLED
224
+ __tsan_release (&c.validator );
225
+ #endif
200
226
201
227
T *ptr = &c.data ;
202
228
@@ -205,24 +231,39 @@ class RID_Alloc : public RID_AllocBase {
205
231
void initialize_rid (RID p_rid) {
206
232
T *mem = get_or_null (p_rid, true );
207
233
ERR_FAIL_NULL (mem);
234
+ #ifdef TSAN_ENABLED
235
+ __tsan_acquire (mem); // We know not a race in practice.
236
+ #endif
208
237
memnew_placement (mem, T);
238
+ #ifdef TSAN_ENABLED
239
+ __tsan_release (mem);
240
+ #endif
241
+ spin.release ();
209
242
}
243
+
210
244
void initialize_rid (RID p_rid, const T &p_value) {
211
245
T *mem = get_or_null (p_rid, true );
212
246
ERR_FAIL_NULL (mem);
247
+ #ifdef TSAN_ENABLED
248
+ __tsan_acquire (mem); // We know not a race in practice.
249
+ #endif
213
250
memnew_placement (mem, T (p_value));
251
+ #ifdef TSAN_ENABLED
252
+ __tsan_release (mem);
253
+ #endif
254
+ spin.release ();
214
255
}
215
256
216
257
_FORCE_INLINE_ bool owns (const RID &p_rid) const {
217
258
if constexpr (THREAD_SAFE) {
218
- mutex .lock ();
259
+ spin .lock ();
219
260
}
220
261
221
262
uint64_t id = p_rid.get_id ();
222
263
uint32_t idx = uint32_t (id & 0xFFFFFFFF );
223
264
if (unlikely (idx >= max_alloc)) {
224
265
if constexpr (THREAD_SAFE) {
225
- mutex .unlock ();
266
+ spin .unlock ();
226
267
}
227
268
return false ;
228
269
}
@@ -235,22 +276,22 @@ class RID_Alloc : public RID_AllocBase {
235
276
bool owned = (validator != 0x7FFFFFFF ) && (chunks[idx_chunk][idx_element].validator & 0x7FFFFFFF ) == validator;
236
277
237
278
if constexpr (THREAD_SAFE) {
238
- mutex .unlock ();
279
+ spin .unlock ();
239
280
}
240
281
241
282
return owned;
242
283
}
243
284
244
285
_FORCE_INLINE_ void free (const RID &p_rid) {
245
286
if constexpr (THREAD_SAFE) {
246
- mutex .lock ();
287
+ spin .lock ();
247
288
}
248
289
249
290
uint64_t id = p_rid.get_id ();
250
291
uint32_t idx = uint32_t (id & 0xFFFFFFFF );
251
292
if (unlikely (idx >= max_alloc)) {
252
293
if constexpr (THREAD_SAFE) {
253
- mutex .unlock ();
294
+ spin .unlock ();
254
295
}
255
296
ERR_FAIL ();
256
297
}
@@ -261,12 +302,12 @@ class RID_Alloc : public RID_AllocBase {
261
302
uint32_t validator = uint32_t (id >> 32 );
262
303
if (unlikely (chunks[idx_chunk][idx_element].validator & 0x80000000 )) {
263
304
if constexpr (THREAD_SAFE) {
264
- mutex .unlock ();
305
+ spin .unlock ();
265
306
}
266
307
ERR_FAIL_MSG (" Attempted to free an uninitialized or invalid RID" );
267
308
} else if (unlikely (chunks[idx_chunk][idx_element].validator != validator)) {
268
309
if constexpr (THREAD_SAFE) {
269
- mutex .unlock ();
310
+ spin .unlock ();
270
311
}
271
312
ERR_FAIL ();
272
313
}
@@ -278,7 +319,7 @@ class RID_Alloc : public RID_AllocBase {
278
319
free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk] = idx;
279
320
280
321
if constexpr (THREAD_SAFE) {
281
- mutex .unlock ();
322
+ spin .unlock ();
282
323
}
283
324
}
284
325
@@ -287,35 +328,35 @@ class RID_Alloc : public RID_AllocBase {
287
328
}
288
329
void get_owned_list (List<RID> *p_owned) const {
289
330
if constexpr (THREAD_SAFE) {
290
- mutex .lock ();
331
+ spin .lock ();
291
332
}
292
333
for (size_t i = 0 ; i < max_alloc; i++) {
293
- uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator ;
334
+ uint32_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator ;
294
335
if (validator != 0xFFFFFFFF ) {
295
- p_owned->push_back (_make_from_id ((validator << 32 ) | i));
336
+ p_owned->push_back (_make_from_id ((( uint64_t ) validator << 32 ) | i));
296
337
}
297
338
}
298
339
if constexpr (THREAD_SAFE) {
299
- mutex .unlock ();
340
+ spin .unlock ();
300
341
}
301
342
}
302
343
303
344
// used for fast iteration in the elements or RIDs
304
345
void fill_owned_buffer (RID *p_rid_buffer) const {
305
346
if constexpr (THREAD_SAFE) {
306
- mutex .lock ();
347
+ spin .lock ();
307
348
}
308
349
uint32_t idx = 0 ;
309
350
for (size_t i = 0 ; i < max_alloc; i++) {
310
- uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator ;
351
+ uint32_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator ;
311
352
if (validator != 0xFFFFFFFF ) {
312
- p_rid_buffer[idx] = _make_from_id ((validator << 32 ) | i);
353
+ p_rid_buffer[idx] = _make_from_id ((( uint64_t ) validator << 32 ) | i);
313
354
idx++;
314
355
}
315
356
}
316
357
317
358
if constexpr (THREAD_SAFE) {
318
- mutex .unlock ();
359
+ spin .unlock ();
319
360
}
320
361
}
321
362
@@ -329,16 +370,21 @@ class RID_Alloc : public RID_AllocBase {
329
370
chunk_limit = (p_maximum_number_of_elements / elements_in_chunk) + 1 ;
330
371
chunks = (Chunk **)memalloc (sizeof (Chunk *) * chunk_limit);
331
372
free_list_chunks = (uint32_t **)memalloc (sizeof (uint32_t *) * chunk_limit);
373
+ spin.release ();
332
374
}
333
375
}
334
376
335
377
~RID_Alloc () {
378
+ if constexpr (THREAD_SAFE) {
379
+ spin.lock ();
380
+ }
381
+
336
382
if (alloc_count) {
337
383
print_error (vformat (" ERROR: %d RID allocations of type '%s' were leaked at exit." ,
338
384
alloc_count, description ? description : typeid (T).name ()));
339
385
340
386
for (size_t i = 0 ; i < max_alloc; i++) {
341
- uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator ;
387
+ uint32_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator ;
342
388
if (validator & 0x80000000 ) {
343
389
continue ; // uninitialized
344
390
}
0 commit comments