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