1 // Copyright 2019 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "platform/base/trace_logging_activation.h" 6 7 #include <atomic> 8 #include <cassert> 9 #include <thread> 10 11 namespace openscreen { 12 13 namespace { 14 15 // If tracing is active, this is a valid pointer to an object that implements 16 // the TraceLoggingPlatform interface. If tracing is not active, this is 17 // nullptr. 18 std::atomic<TraceLoggingPlatform*> g_current_destination{}; 19 20 // The count of threads currently calling into the current TraceLoggingPlatform. 21 std::atomic<int> g_use_count{}; 22 PinCurrentDestination()23inline TraceLoggingPlatform* PinCurrentDestination() { 24 // NOTE: It's important to increment the global use count *before* loading the 25 // pointer, to ensure the referent is pinned-down (i.e., any thread executing 26 // StopTracing() stays blocked) until CurrentTracingDestination's destructor 27 // calls UnpinCurrentDestination(). 28 g_use_count.fetch_add(1); 29 return g_current_destination.load(std::memory_order_relaxed); 30 } 31 UnpinCurrentDestination()32inline void UnpinCurrentDestination() { 33 g_use_count.fetch_sub(1); 34 } 35 36 } // namespace 37 StartTracing(TraceLoggingPlatform * destination)38void StartTracing(TraceLoggingPlatform* destination) { 39 assert(destination); 40 auto* const old_destination = g_current_destination.exchange(destination); 41 (void)old_destination; // Prevent "unused variable" compiler warnings. 42 assert(old_destination == nullptr || old_destination == destination); 43 } 44 StopTracing()45void StopTracing() { 46 auto* const old_destination = g_current_destination.exchange(nullptr); 47 if (!old_destination) { 48 return; // Already stopped. 49 } 50 51 // Block the current thread until the global use count goes to zero. At that 52 // point, there can no longer be any dangling references. Theoretically, this 53 // loop may never terminate; but in practice, that should never happen. If it 54 // did happen, that would mean one or more CPU cores are continuously spending 55 // most of their time executing the TraceLoggingPlatform methods, yet those 56 // methods are supposed to be super-cheap and take near-zero time to execute! 57 int iters = 0; 58 while (g_use_count.load(std::memory_order_relaxed) != 0) { 59 assert(iters < 1024); 60 std::this_thread::yield(); 61 ++iters; 62 } 63 } 64 CurrentTracingDestination()65CurrentTracingDestination::CurrentTracingDestination() 66 : destination_(PinCurrentDestination()) {} 67 ~CurrentTracingDestination()68CurrentTracingDestination::~CurrentTracingDestination() { 69 UnpinCurrentDestination(); 70 } 71 72 } // namespace openscreen 73