The open source OpenXR runtime
1#ifndef __TRACYLOCK_HPP__
2#define __TRACYLOCK_HPP__
3
4#include <atomic>
5#include <limits>
6
7#include "../common/TracySystem.hpp"
8#include "../common/TracyAlign.hpp"
9#include "TracyProfiler.hpp"
10
11namespace tracy
12{
13
14class LockableCtx
15{
16public:
17 tracy_force_inline LockableCtx( const SourceLocationData* srcloc )
18 : m_id( GetLockCounter().fetch_add( 1, std::memory_order_relaxed ) )
19#ifdef TRACY_ON_DEMAND
20 , m_lockCount( 0 )
21 , m_active( false )
22#endif
23 {
24 assert( m_id != std::numeric_limits<uint32_t>::max() );
25
26 auto item = Profiler::QueueSerial();
27 MemWrite( &item->hdr.type, QueueType::LockAnnounce );
28 MemWrite( &item->lockAnnounce.id, m_id );
29 MemWrite( &item->lockAnnounce.time, Profiler::GetTime() );
30 MemWrite( &item->lockAnnounce.lckloc, (uint64_t)srcloc );
31 MemWrite( &item->lockAnnounce.type, LockType::Lockable );
32#ifdef TRACY_ON_DEMAND
33 GetProfiler().DeferItem( *item );
34#endif
35 Profiler::QueueSerialFinish();
36 }
37
38 LockableCtx( const LockableCtx& ) = delete;
39 LockableCtx& operator=( const LockableCtx& ) = delete;
40
41 tracy_force_inline ~LockableCtx()
42 {
43 auto item = Profiler::QueueSerial();
44 MemWrite( &item->hdr.type, QueueType::LockTerminate );
45 MemWrite( &item->lockTerminate.id, m_id );
46 MemWrite( &item->lockTerminate.time, Profiler::GetTime() );
47#ifdef TRACY_ON_DEMAND
48 GetProfiler().DeferItem( *item );
49#endif
50 Profiler::QueueSerialFinish();
51 }
52
53 tracy_force_inline bool BeforeLock()
54 {
55#ifdef TRACY_ON_DEMAND
56 bool queue = false;
57 const auto locks = m_lockCount.fetch_add( 1, std::memory_order_relaxed );
58 const auto active = m_active.load( std::memory_order_relaxed );
59 if( locks == 0 || active )
60 {
61 const bool connected = GetProfiler().IsConnected();
62 if( active != connected ) m_active.store( connected, std::memory_order_relaxed );
63 if( connected ) queue = true;
64 }
65 if( !queue ) return false;
66#endif
67
68 auto item = Profiler::QueueSerial();
69 MemWrite( &item->hdr.type, QueueType::LockWait );
70 MemWrite( &item->lockWait.thread, GetThreadHandle() );
71 MemWrite( &item->lockWait.id, m_id );
72 MemWrite( &item->lockWait.time, Profiler::GetTime() );
73 Profiler::QueueSerialFinish();
74 return true;
75 }
76
77 tracy_force_inline void AfterLock()
78 {
79 auto item = Profiler::QueueSerial();
80 MemWrite( &item->hdr.type, QueueType::LockObtain );
81 MemWrite( &item->lockObtain.thread, GetThreadHandle() );
82 MemWrite( &item->lockObtain.id, m_id );
83 MemWrite( &item->lockObtain.time, Profiler::GetTime() );
84 Profiler::QueueSerialFinish();
85 }
86
87 tracy_force_inline void AfterUnlock()
88 {
89#ifdef TRACY_ON_DEMAND
90 m_lockCount.fetch_sub( 1, std::memory_order_relaxed );
91 if( !m_active.load( std::memory_order_relaxed ) ) return;
92 if( !GetProfiler().IsConnected() )
93 {
94 m_active.store( false, std::memory_order_relaxed );
95 return;
96 }
97#endif
98
99 auto item = Profiler::QueueSerial();
100 MemWrite( &item->hdr.type, QueueType::LockRelease );
101 MemWrite( &item->lockRelease.id, m_id );
102 MemWrite( &item->lockRelease.time, Profiler::GetTime() );
103 Profiler::QueueSerialFinish();
104 }
105
106 tracy_force_inline void AfterTryLock( bool acquired )
107 {
108#ifdef TRACY_ON_DEMAND
109 if( !acquired ) return;
110
111 bool queue = false;
112 const auto locks = m_lockCount.fetch_add( 1, std::memory_order_relaxed );
113 const auto active = m_active.load( std::memory_order_relaxed );
114 if( locks == 0 || active )
115 {
116 const bool connected = GetProfiler().IsConnected();
117 if( active != connected ) m_active.store( connected, std::memory_order_relaxed );
118 if( connected ) queue = true;
119 }
120 if( !queue ) return;
121#endif
122
123 if( acquired )
124 {
125 auto item = Profiler::QueueSerial();
126 MemWrite( &item->hdr.type, QueueType::LockObtain );
127 MemWrite( &item->lockObtain.thread, GetThreadHandle() );
128 MemWrite( &item->lockObtain.id, m_id );
129 MemWrite( &item->lockObtain.time, Profiler::GetTime() );
130 Profiler::QueueSerialFinish();
131 }
132 }
133
134 tracy_force_inline void Mark( const SourceLocationData* srcloc )
135 {
136#ifdef TRACY_ON_DEMAND
137 const auto active = m_active.load( std::memory_order_relaxed );
138 if( !active ) return;
139 const auto connected = GetProfiler().IsConnected();
140 if( !connected )
141 {
142 if( active ) m_active.store( false, std::memory_order_relaxed );
143 return;
144 }
145#endif
146
147 auto item = Profiler::QueueSerial();
148 MemWrite( &item->hdr.type, QueueType::LockMark );
149 MemWrite( &item->lockMark.thread, GetThreadHandle() );
150 MemWrite( &item->lockMark.id, m_id );
151 MemWrite( &item->lockMark.srcloc, (uint64_t)srcloc );
152 Profiler::QueueSerialFinish();
153 }
154
155 tracy_force_inline void CustomName( const char* name, size_t size )
156 {
157 assert( size < std::numeric_limits<uint16_t>::max() );
158 auto ptr = (char*)tracy_malloc( size );
159 memcpy( ptr, name, size );
160 auto item = Profiler::QueueSerial();
161 MemWrite( &item->hdr.type, QueueType::LockName );
162 MemWrite( &item->lockNameFat.id, m_id );
163 MemWrite( &item->lockNameFat.name, (uint64_t)ptr );
164 MemWrite( &item->lockNameFat.size, (uint16_t)size );
165#ifdef TRACY_ON_DEMAND
166 GetProfiler().DeferItem( *item );
167#endif
168 Profiler::QueueSerialFinish();
169 }
170
171private:
172 uint32_t m_id;
173
174#ifdef TRACY_ON_DEMAND
175 std::atomic<uint32_t> m_lockCount;
176 std::atomic<bool> m_active;
177#endif
178};
179
180template<class T>
181class Lockable
182{
183public:
184 tracy_force_inline Lockable( const SourceLocationData* srcloc )
185 : m_ctx( srcloc )
186 {
187 }
188
189 Lockable( const Lockable& ) = delete;
190 Lockable& operator=( const Lockable& ) = delete;
191
192 tracy_force_inline void lock()
193 {
194 const auto runAfter = m_ctx.BeforeLock();
195 m_lockable.lock();
196 if( runAfter ) m_ctx.AfterLock();
197 }
198
199 tracy_force_inline void unlock()
200 {
201 m_lockable.unlock();
202 m_ctx.AfterUnlock();
203 }
204
205 tracy_force_inline bool try_lock()
206 {
207 const auto acquired = m_lockable.try_lock();
208 m_ctx.AfterTryLock( acquired );
209 return acquired;
210 }
211
212 tracy_force_inline void Mark( const SourceLocationData* srcloc )
213 {
214 m_ctx.Mark( srcloc );
215 }
216
217 tracy_force_inline void CustomName( const char* name, size_t size )
218 {
219 m_ctx.CustomName( name, size );
220 }
221
222private:
223 T m_lockable;
224 LockableCtx m_ctx;
225};
226
227
228class SharedLockableCtx
229{
230public:
231 tracy_force_inline SharedLockableCtx( const SourceLocationData* srcloc )
232 : m_id( GetLockCounter().fetch_add( 1, std::memory_order_relaxed ) )
233#ifdef TRACY_ON_DEMAND
234 , m_lockCount( 0 )
235 , m_active( false )
236#endif
237 {
238 assert( m_id != std::numeric_limits<uint32_t>::max() );
239
240 auto item = Profiler::QueueSerial();
241 MemWrite( &item->hdr.type, QueueType::LockAnnounce );
242 MemWrite( &item->lockAnnounce.id, m_id );
243 MemWrite( &item->lockAnnounce.time, Profiler::GetTime() );
244 MemWrite( &item->lockAnnounce.lckloc, (uint64_t)srcloc );
245 MemWrite( &item->lockAnnounce.type, LockType::SharedLockable );
246#ifdef TRACY_ON_DEMAND
247 GetProfiler().DeferItem( *item );
248#endif
249 Profiler::QueueSerialFinish();
250 }
251
252 SharedLockableCtx( const SharedLockableCtx& ) = delete;
253 SharedLockableCtx& operator=( const SharedLockableCtx& ) = delete;
254
255 tracy_force_inline ~SharedLockableCtx()
256 {
257 auto item = Profiler::QueueSerial();
258 MemWrite( &item->hdr.type, QueueType::LockTerminate );
259 MemWrite( &item->lockTerminate.id, m_id );
260 MemWrite( &item->lockTerminate.time, Profiler::GetTime() );
261#ifdef TRACY_ON_DEMAND
262 GetProfiler().DeferItem( *item );
263#endif
264 Profiler::QueueSerialFinish();
265 }
266
267 tracy_force_inline bool BeforeLock()
268 {
269#ifdef TRACY_ON_DEMAND
270 bool queue = false;
271 const auto locks = m_lockCount.fetch_add( 1, std::memory_order_relaxed );
272 const auto active = m_active.load( std::memory_order_relaxed );
273 if( locks == 0 || active )
274 {
275 const bool connected = GetProfiler().IsConnected();
276 if( active != connected ) m_active.store( connected, std::memory_order_relaxed );
277 if( connected ) queue = true;
278 }
279 if( !queue ) return false;
280#endif
281
282 auto item = Profiler::QueueSerial();
283 MemWrite( &item->hdr.type, QueueType::LockWait );
284 MemWrite( &item->lockWait.thread, GetThreadHandle() );
285 MemWrite( &item->lockWait.id, m_id );
286 MemWrite( &item->lockWait.time, Profiler::GetTime() );
287 Profiler::QueueSerialFinish();
288 return true;
289 }
290
291 tracy_force_inline void AfterLock()
292 {
293 auto item = Profiler::QueueSerial();
294 MemWrite( &item->hdr.type, QueueType::LockObtain );
295 MemWrite( &item->lockObtain.thread, GetThreadHandle() );
296 MemWrite( &item->lockObtain.id, m_id );
297 MemWrite( &item->lockObtain.time, Profiler::GetTime() );
298 Profiler::QueueSerialFinish();
299 }
300
301 tracy_force_inline void AfterUnlock()
302 {
303#ifdef TRACY_ON_DEMAND
304 m_lockCount.fetch_sub( 1, std::memory_order_relaxed );
305 if( !m_active.load( std::memory_order_relaxed ) ) return;
306 if( !GetProfiler().IsConnected() )
307 {
308 m_active.store( false, std::memory_order_relaxed );
309 return;
310 }
311#endif
312
313 auto item = Profiler::QueueSerial();
314 MemWrite( &item->hdr.type, QueueType::LockRelease );
315 MemWrite( &item->lockRelease.id, m_id );
316 MemWrite( &item->lockRelease.time, Profiler::GetTime() );
317 Profiler::QueueSerialFinish();
318 }
319
320 tracy_force_inline void AfterTryLock( bool acquired )
321 {
322#ifdef TRACY_ON_DEMAND
323 if( !acquired ) return;
324
325 bool queue = false;
326 const auto locks = m_lockCount.fetch_add( 1, std::memory_order_relaxed );
327 const auto active = m_active.load( std::memory_order_relaxed );
328 if( locks == 0 || active )
329 {
330 const bool connected = GetProfiler().IsConnected();
331 if( active != connected ) m_active.store( connected, std::memory_order_relaxed );
332 if( connected ) queue = true;
333 }
334 if( !queue ) return;
335#endif
336
337 if( acquired )
338 {
339 auto item = Profiler::QueueSerial();
340 MemWrite( &item->hdr.type, QueueType::LockObtain );
341 MemWrite( &item->lockObtain.thread, GetThreadHandle() );
342 MemWrite( &item->lockObtain.id, m_id );
343 MemWrite( &item->lockObtain.time, Profiler::GetTime() );
344 Profiler::QueueSerialFinish();
345 }
346 }
347
348 tracy_force_inline bool BeforeLockShared()
349 {
350#ifdef TRACY_ON_DEMAND
351 bool queue = false;
352 const auto locks = m_lockCount.fetch_add( 1, std::memory_order_relaxed );
353 const auto active = m_active.load( std::memory_order_relaxed );
354 if( locks == 0 || active )
355 {
356 const bool connected = GetProfiler().IsConnected();
357 if( active != connected ) m_active.store( connected, std::memory_order_relaxed );
358 if( connected ) queue = true;
359 }
360 if( !queue ) return false;
361#endif
362
363 auto item = Profiler::QueueSerial();
364 MemWrite( &item->hdr.type, QueueType::LockSharedWait );
365 MemWrite( &item->lockWait.thread, GetThreadHandle() );
366 MemWrite( &item->lockWait.id, m_id );
367 MemWrite( &item->lockWait.time, Profiler::GetTime() );
368 Profiler::QueueSerialFinish();
369 return true;
370 }
371
372 tracy_force_inline void AfterLockShared()
373 {
374 auto item = Profiler::QueueSerial();
375 MemWrite( &item->hdr.type, QueueType::LockSharedObtain );
376 MemWrite( &item->lockObtain.thread, GetThreadHandle() );
377 MemWrite( &item->lockObtain.id, m_id );
378 MemWrite( &item->lockObtain.time, Profiler::GetTime() );
379 Profiler::QueueSerialFinish();
380 }
381
382 tracy_force_inline void AfterUnlockShared()
383 {
384#ifdef TRACY_ON_DEMAND
385 m_lockCount.fetch_sub( 1, std::memory_order_relaxed );
386 if( !m_active.load( std::memory_order_relaxed ) ) return;
387 if( !GetProfiler().IsConnected() )
388 {
389 m_active.store( false, std::memory_order_relaxed );
390 return;
391 }
392#endif
393
394 auto item = Profiler::QueueSerial();
395 MemWrite( &item->hdr.type, QueueType::LockSharedRelease );
396 MemWrite( &item->lockReleaseShared.thread, GetThreadHandle() );
397 MemWrite( &item->lockReleaseShared.id, m_id );
398 MemWrite( &item->lockReleaseShared.time, Profiler::GetTime() );
399 Profiler::QueueSerialFinish();
400 }
401
402 tracy_force_inline void AfterTryLockShared( bool acquired )
403 {
404#ifdef TRACY_ON_DEMAND
405 if( !acquired ) return;
406
407 bool queue = false;
408 const auto locks = m_lockCount.fetch_add( 1, std::memory_order_relaxed );
409 const auto active = m_active.load( std::memory_order_relaxed );
410 if( locks == 0 || active )
411 {
412 const bool connected = GetProfiler().IsConnected();
413 if( active != connected ) m_active.store( connected, std::memory_order_relaxed );
414 if( connected ) queue = true;
415 }
416 if( !queue ) return;
417#endif
418
419 if( acquired )
420 {
421 auto item = Profiler::QueueSerial();
422 MemWrite( &item->hdr.type, QueueType::LockSharedObtain );
423 MemWrite( &item->lockObtain.thread, GetThreadHandle() );
424 MemWrite( &item->lockObtain.id, m_id );
425 MemWrite( &item->lockObtain.time, Profiler::GetTime() );
426 Profiler::QueueSerialFinish();
427 }
428 }
429
430 tracy_force_inline void Mark( const SourceLocationData* srcloc )
431 {
432#ifdef TRACY_ON_DEMAND
433 const auto active = m_active.load( std::memory_order_relaxed );
434 if( !active ) return;
435 const auto connected = GetProfiler().IsConnected();
436 if( !connected )
437 {
438 if( active ) m_active.store( false, std::memory_order_relaxed );
439 return;
440 }
441#endif
442
443 auto item = Profiler::QueueSerial();
444 MemWrite( &item->hdr.type, QueueType::LockMark );
445 MemWrite( &item->lockMark.thread, GetThreadHandle() );
446 MemWrite( &item->lockMark.id, m_id );
447 MemWrite( &item->lockMark.srcloc, (uint64_t)srcloc );
448 Profiler::QueueSerialFinish();
449 }
450
451 tracy_force_inline void CustomName( const char* name, size_t size )
452 {
453 assert( size < std::numeric_limits<uint16_t>::max() );
454 auto ptr = (char*)tracy_malloc( size );
455 memcpy( ptr, name, size );
456 auto item = Profiler::QueueSerial();
457 MemWrite( &item->hdr.type, QueueType::LockName );
458 MemWrite( &item->lockNameFat.id, m_id );
459 MemWrite( &item->lockNameFat.name, (uint64_t)ptr );
460 MemWrite( &item->lockNameFat.size, (uint16_t)size );
461#ifdef TRACY_ON_DEMAND
462 GetProfiler().DeferItem( *item );
463#endif
464 Profiler::QueueSerialFinish();
465 }
466
467private:
468 uint32_t m_id;
469
470#ifdef TRACY_ON_DEMAND
471 std::atomic<uint32_t> m_lockCount;
472 std::atomic<bool> m_active;
473#endif
474};
475
476template<class T>
477class SharedLockable
478{
479public:
480 tracy_force_inline SharedLockable( const SourceLocationData* srcloc )
481 : m_ctx( srcloc )
482 {
483 }
484
485 SharedLockable( const SharedLockable& ) = delete;
486 SharedLockable& operator=( const SharedLockable& ) = delete;
487
488 tracy_force_inline void lock()
489 {
490 const auto runAfter = m_ctx.BeforeLock();
491 m_lockable.lock();
492 if( runAfter ) m_ctx.AfterLock();
493 }
494
495 tracy_force_inline void unlock()
496 {
497 m_lockable.unlock();
498 m_ctx.AfterUnlock();
499 }
500
501 tracy_force_inline bool try_lock()
502 {
503 const auto acquired = m_lockable.try_lock();
504 m_ctx.AfterTryLock( acquired );
505 return acquired;
506 }
507
508 tracy_force_inline void lock_shared()
509 {
510 const auto runAfter = m_ctx.BeforeLockShared();
511 m_lockable.lock_shared();
512 if( runAfter ) m_ctx.AfterLockShared();
513 }
514
515 tracy_force_inline void unlock_shared()
516 {
517 m_lockable.unlock_shared();
518 m_ctx.AfterUnlockShared();
519 }
520
521 tracy_force_inline bool try_lock_shared()
522 {
523 const auto acquired = m_lockable.try_lock_shared();
524 m_ctx.AfterTryLockShared( acquired );
525 return acquired;
526 }
527
528 tracy_force_inline void Mark( const SourceLocationData* srcloc )
529 {
530 m_ctx.Mark( srcloc );
531 }
532
533 tracy_force_inline void CustomName( const char* name, size_t size )
534 {
535 m_ctx.CustomName( name, size );
536 }
537
538private:
539 T m_lockable;
540 SharedLockableCtx m_ctx;
541};
542
543
544}
545
546#endif