The open source OpenXR runtime
at main 546 lines 17 kB view raw
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