summaryrefslogtreecommitdiffstats
path: root/xpcom/threads/IdlePeriodState.h
blob: d50fce64be74df7c9f8d390ccf36b51a9a93feba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_IdlePeriodState_h
#define mozilla_IdlePeriodState_h

/**
 * A class for tracking the state of our idle period.  This includes keeping
 * track of both the state of our process-local idle period estimate and, for
 * content processes, managing communication with the parent process for
 * cross-pprocess idle detection.
 */

#include "mozilla/MemoryReporting.h"
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
#include "nsCOMPtr.h"

#include <stdint.h>

class nsIIdlePeriod;

namespace mozilla {
class TaskManager;
namespace ipc {
class IdleSchedulerChild;
}  // namespace ipc

class IdlePeriodState {
 public:
  explicit IdlePeriodState(already_AddRefed<nsIIdlePeriod>&& aIdlePeriod);

  ~IdlePeriodState();

  // Integration with memory reporting.
  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;

  // Notification that whoever we are tracking idle state for has found a
  // non-idle task to process.
  //
  // Must not be called while holding any locks.
  void FlagNotIdle();

  // Notification that whoever we are tracking idle state for has no more
  // tasks (idle or not) to process.
  //
  // aProofOfUnlock is the proof that our caller unlocked its mutex.
  void RanOutOfTasks(const MutexAutoUnlock& aProofOfUnlock);

  // Notification that whoever we are tracking idle state has idle tasks that
  // they are considering ready to run and that we should keep claiming they are
  // ready to run until they call ForgetPendingTaskGuarantee().
  void EnforcePendingTaskGuarantee() {
    mHasPendingEventsPromisedIdleEvent = true;
  }

  // Notification that whoever we are tracking idle state for is done with our
  // "we have an idle event ready to run" guarantee.  When this happens, we can
  // reset mHasPendingEventsPromisedIdleEvent to false, because we have
  // fulfilled our contract.
  void ForgetPendingTaskGuarantee() {
    mHasPendingEventsPromisedIdleEvent = false;
  }

  // Update our cached idle deadline so consumers can use it while holding
  // locks. Consumers must ClearCachedIdleDeadline() once they are done.
  void UpdateCachedIdleDeadline(const MutexAutoUnlock& aProofOfUnlock) {
    mCachedIdleDeadline = GetIdleDeadlineInternal(false, aProofOfUnlock);
  }

  // If we have local idle deadline, but don't have an idle token, this will
  // request such from the parent process when this is called in a child
  // process.
  void RequestIdleDeadlineIfNeeded(const MutexAutoUnlock& aProofOfUnlock) {
    GetIdleDeadlineInternal(false, aProofOfUnlock);
  }

  // Reset our cached idle deadline, so we stop allowing idle runnables to run.
  void ClearCachedIdleDeadline() { mCachedIdleDeadline = TimeStamp(); }

  // Get the current cached idle deadline.  This may return a null timestamp.
  TimeStamp GetCachedIdleDeadline() { return mCachedIdleDeadline; }

  // Peek our current idle deadline into mCachedIdleDeadline.  This can cause
  // mCachedIdleDeadline to be a null timestamp (which means we are not idle
  // right now).  This method does not have any side-effects on our state, apart
  // from guaranteeing that if it returns non-null then GetDeadlineForIdleTask
  // will return non-null until ForgetPendingTaskGuarantee() is called, and its
  // effects on mCachedIdleDeadline.
  //
  // aProofOfUnlock is the proof that our caller unlocked its mutex.
  void CachePeekedIdleDeadline(const MutexAutoUnlock& aProofOfUnlock) {
    mCachedIdleDeadline = GetIdleDeadlineInternal(true, aProofOfUnlock);
  }

  void SetIdleToken(uint64_t aId, TimeDuration aDuration);

  bool IsActive() { return mActive; }

 protected:
  void EnsureIsActive() {
    if (!mActive) {
      SetActive();
    }
  }

  void EnsureIsPaused(const MutexAutoUnlock& aProofOfUnlock) {
    if (mActive) {
      SetPaused(aProofOfUnlock);
    }
  }

  // Returns a null TimeStamp if we're not in the idle period.
  TimeStamp GetLocalIdleDeadline(bool& aShuttingDown,
                                 const MutexAutoUnlock& aProofOfUnlock);

  // Gets the idle token, which is the end time of the idle period.
  //
  // aProofOfUnlock is the proof that our caller unlocked its mutex.
  TimeStamp GetIdleToken(TimeStamp aLocalIdlePeriodHint,
                         const MutexAutoUnlock& aProofOfUnlock);

  // In case of child processes, requests idle time from the cross-process
  // idle scheduler.
  void RequestIdleToken(TimeStamp aLocalIdlePeriodHint);

  // Mark that we don't have idle time to use, nor are expecting to get an idle
  // token from the idle scheduler.  This must be called while not holding any
  // locks, but some of the callers aren't holding locks to start with, so
  // consumers just need to make sure they are not holding locks when they call
  // this.
  void ClearIdleToken();

  // SetActive should be called when the event queue is running any type of
  // tasks.
  void SetActive();
  // SetPaused should be called once the event queue doesn't have more
  // tasks to process, or is waiting for the idle token.
  //
  // aProofOfUnlock is the proof that our caller unlocked its mutex.
  void SetPaused(const MutexAutoUnlock& aProofOfUnlock);

  // Get or peek our idle deadline.  When peeking, we generally don't change any
  // of our internal state.  When getting, we may request an idle token as
  // needed.
  //
  // aProofOfUnlock is the proof that our caller unlocked its mutex.
  TimeStamp GetIdleDeadlineInternal(bool aIsPeek,
                                    const MutexAutoUnlock& aProofOfUnlock);

  // Whether we should be getting an idle token (i.e. are a content process
  // and are using cross process idle scheduling).
  bool ShouldGetIdleToken();

  // Set to true if we have claimed we have a ready-to-run idle task when asked.
  // In that case, we will ensure that we allow at least one task to run when
  // someone tries to run a task, even if we have run out of idle period at that
  // point.  This ensures that we never fail to produce a task to run if we
  // claim we have a task ready to run.
  bool mHasPendingEventsPromisedIdleEvent = false;

  // mIdlePeriod keeps track of the current idle period. Calling
  // mIdlePeriod->GetIdlePeriodHint() will give an estimate of when
  // the current idle period will end.
  nsCOMPtr<nsIIdlePeriod> mIdlePeriod;

  // If non-null, this timestamp represents the end time of the idle period.  An
  // idle period starts when we get the idle token from the parent process and
  // ends when either there are no more things we want to run at idle priority
  // or mIdleToken < TimeStamp::Now(), so we have reached our idle deadline.
  TimeStamp mIdleToken;

  // The id of the last idle request to the cross-process idle scheduler.
  uint64_t mIdleRequestId = 0;

  // If we're in a content process, we use mIdleScheduler to communicate with
  // the parent process for purposes of cross-process idle tracking.
  RefPtr<mozilla::ipc::IdleSchedulerChild> mIdleScheduler;

  // Our cached idle deadline.  This is set by UpdateCachedIdleDeadline() and
  // cleared by ClearCachedIdleDeadline().  Consumers should do the former while
  // not holding any locks, but may do the latter while holding locks.
  TimeStamp mCachedIdleDeadline;

  // mIdleSchedulerInitialized is true if our mIdleScheduler has been
  // initialized.  It may be null even after initialiazation, in various
  // situations.
  bool mIdleSchedulerInitialized = false;

  // mActive is true when the PrioritizedEventQueue or TaskController we are
  // associated with is running tasks.
  bool mActive = true;
};

}  // namespace mozilla

#endif  // mozilla_IdlePeriodState_h