tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RegisteredThread.h (6002B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef RegisteredThread_h
      8 #define RegisteredThread_h
      9 
     10 #include "platform.h"
     11 #include "ThreadInfo.h"
     12 
     13 namespace mozilla {
     14 namespace baseprofiler {
     15 
     16 // This class contains the state for a single thread that is accessible without
     17 // protection from gPSMutex in platform.cpp. Because there is no external
     18 // protection against data races, it must provide internal protection. Hence
     19 // the "Racy" prefix.
     20 //
     21 class RacyRegisteredThread final {
     22 public:
     23  explicit RacyRegisteredThread(BaseProfilerThreadId aThreadId)
     24      : mThreadId(aThreadId), mSleep(AWAKE), mIsBeingProfiled(false) {}
     25 
     26  ~RacyRegisteredThread() {}
     27 
     28  void SetIsBeingProfiled(bool aIsBeingProfiled) {
     29    mIsBeingProfiled = aIsBeingProfiled;
     30  }
     31 
     32  bool IsBeingProfiled() const { return mIsBeingProfiled; }
     33 
     34  // This is called on every profiler restart. Put things that should happen at
     35  // that time here.
     36  void ReinitializeOnResume() {
     37    // This is needed to cause an initial sample to be taken from sleeping
     38    // threads that had been observed prior to the profiler stopping and
     39    // restarting. Otherwise sleeping threads would not have any samples to
     40    // copy forward while sleeping.
     41    (void)mSleep.compareExchange(SLEEPING_OBSERVED, SLEEPING_NOT_OBSERVED);
     42  }
     43 
     44  // This returns true for the second and subsequent calls in each sleep cycle.
     45  bool CanDuplicateLastSampleDueToSleep() {
     46    if (mSleep == AWAKE) {
     47      return false;
     48    }
     49 
     50    if (mSleep.compareExchange(SLEEPING_NOT_OBSERVED, SLEEPING_OBSERVED)) {
     51      return false;
     52    }
     53 
     54    return true;
     55  }
     56 
     57  // Call this whenever the current thread sleeps. Calling it twice in a row
     58  // without an intervening setAwake() call is an error.
     59  void SetSleeping() {
     60    MOZ_ASSERT(mSleep == AWAKE);
     61    mSleep = SLEEPING_NOT_OBSERVED;
     62  }
     63 
     64  // Call this whenever the current thread wakes. Calling it twice in a row
     65  // without an intervening setSleeping() call is an error.
     66  void SetAwake() {
     67    MOZ_ASSERT(mSleep != AWAKE);
     68    mSleep = AWAKE;
     69  }
     70 
     71  bool IsSleeping() { return mSleep != AWAKE; }
     72 
     73  BaseProfilerThreadId ThreadId() const { return mThreadId; }
     74 
     75  class ProfilingStack& ProfilingStack() { return mProfilingStack; }
     76  const class ProfilingStack& ProfilingStack() const { return mProfilingStack; }
     77 
     78 private:
     79  class ProfilingStack mProfilingStack;
     80 
     81  // mThreadId contains the thread ID of the current thread. It is safe to read
     82  // this from multiple threads concurrently, as it will never be mutated.
     83  const BaseProfilerThreadId mThreadId;
     84 
     85  // mSleep tracks whether the thread is sleeping, and if so, whether it has
     86  // been previously observed. This is used for an optimization: in some cases,
     87  // when a thread is asleep, we duplicate the previous sample, which is
     88  // cheaper than taking a new sample.
     89  //
     90  // mSleep is atomic because it is accessed from multiple threads.
     91  //
     92  // - It is written only by this thread, via setSleeping() and setAwake().
     93  //
     94  // - It is read by SamplerThread::Run().
     95  //
     96  // There are two cases where racing between threads can cause an issue.
     97  //
     98  // - If CanDuplicateLastSampleDueToSleep() returns false but that result is
     99  //   invalidated before being acted upon, we will take a full sample
    100  //   unnecessarily. This is additional work but won't cause any correctness
    101  //   issues. (In actual fact, this case is impossible. In order to go from
    102  //   CanDuplicateLastSampleDueToSleep() returning false to it returning true
    103  //   requires an intermediate call to it in order for mSleep to go from
    104  //   SLEEPING_NOT_OBSERVED to SLEEPING_OBSERVED.)
    105  //
    106  // - If CanDuplicateLastSampleDueToSleep() returns true but that result is
    107  //   invalidated before being acted upon -- i.e. the thread wakes up before
    108  //   DuplicateLastSample() is called -- we will duplicate the previous
    109  //   sample. This is inaccurate, but only slightly... we will effectively
    110  //   treat the thread as having slept a tiny bit longer than it really did.
    111  //
    112  // This latter inaccuracy could be avoided by moving the
    113  // CanDuplicateLastSampleDueToSleep() check within the thread-freezing code,
    114  // e.g. the section where Tick() is called. But that would reduce the
    115  // effectiveness of the optimization because more code would have to be run
    116  // before we can tell that duplication is allowed.
    117  //
    118  static const int AWAKE = 0;
    119  static const int SLEEPING_NOT_OBSERVED = 1;
    120  static const int SLEEPING_OBSERVED = 2;
    121  Atomic<int> mSleep;
    122 
    123  // Is this thread being profiled? (e.g., should markers be recorded?)
    124  Atomic<bool, MemoryOrdering::Relaxed> mIsBeingProfiled;
    125 };
    126 
    127 // This class contains information that's relevant to a single thread only
    128 // while that thread is running and registered with the profiler, but
    129 // regardless of whether the profiler is running. All accesses to it are
    130 // protected by the profiler state lock.
    131 class RegisteredThread final {
    132 public:
    133  RegisteredThread(ThreadInfo* aInfo, void* aStackTop);
    134  ~RegisteredThread();
    135 
    136  class RacyRegisteredThread& RacyRegisteredThread() {
    137    return mRacyRegisteredThread;
    138  }
    139  const class RacyRegisteredThread& RacyRegisteredThread() const {
    140    return mRacyRegisteredThread;
    141  }
    142 
    143  PlatformData* GetPlatformData() const { return mPlatformData.get(); }
    144  const void* StackTop() const { return mStackTop; }
    145 
    146  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
    147 
    148  const RefPtr<ThreadInfo> Info() const { return mThreadInfo; }
    149 
    150 private:
    151  class RacyRegisteredThread mRacyRegisteredThread;
    152 
    153  const UniquePlatformData mPlatformData;
    154  const void* mStackTop;
    155 
    156  const RefPtr<ThreadInfo> mThreadInfo;
    157 };
    158 
    159 }  // namespace baseprofiler
    160 }  // namespace mozilla
    161 
    162 #endif  // RegisteredThread_h