xref: /aosp_15_r20/external/openscreen/platform/base/trace_logging_activation.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
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()23 inline 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()32 inline void UnpinCurrentDestination() {
33   g_use_count.fetch_sub(1);
34 }
35 
36 }  // namespace
37 
StartTracing(TraceLoggingPlatform * destination)38 void 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()45 void 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()65 CurrentTracingDestination::CurrentTracingDestination()
66     : destination_(PinCurrentDestination()) {}
67 
~CurrentTracingDestination()68 CurrentTracingDestination::~CurrentTracingDestination() {
69   UnpinCurrentDestination();
70 }
71 
72 }  // namespace openscreen
73