1
// Copyright 2011 Google Inc.
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
7
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
15
// Author: jmarantz@google.com (Joshua Marantz)
17
#ifndef PAGESPEED_KERNEL_THREAD_SCHEDULER_H_
18
#define PAGESPEED_KERNEL_THREAD_SCHEDULER_H_
22
#include "pagespeed/kernel/base/basictypes.h"
23
#include "pagespeed/kernel/base/condvar.h"
24
#include "pagespeed/kernel/base/function.h"
25
#include "pagespeed/kernel/base/scoped_ptr.h"
26
#include "pagespeed/kernel/base/thread_annotations.h"
27
#include "pagespeed/kernel/base/thread_system.h"
28
#include "pagespeed/kernel/base/timer.h"
29
#include "pagespeed/kernel/thread/queued_worker_pool.h"
31
// TODO(jmarantz): The Scheduler should cancel all outstanding operations
32
// on destruction. Deploying this requires further analysis of shutdown
34
#define SCHEDULER_CANCEL_OUTSTANDING_ALARMS_ON_DESTRUCTION 0
36
namespace net_instaweb {
38
// Implements a simple scheduler that allows a thread to block until either time
39
// expires, or a condition variable is signaled. Also permits various alarms to
40
// be scheduled; these are lightweight short-lived callbacks that must be safely
41
// runnable from any thread in any lock state in which scheduler invocations
42
// occur. Finally, implements a hybrid between these: a callback that can be
43
// run when the condition variable is signaled.
45
// This class is designed to be overridden, but only to re-implement its
46
// internal notion of blocking to permit time to be mocked by MockScheduler.
49
// A callback for a scheduler alarm, with an associated wakeup time (absolute
50
// time after which the callback will be invoked with Run() by the scheduler).
51
// Alarm should be treated as an opaque type.
54
// Sorting comparator for Alarms, so that they can be retrieved in time
55
// order. For use by std::set, thus public.
56
struct CompareAlarms {
57
bool operator()(const Alarm* a, const Alarm* b) const;
60
Scheduler(ThreadSystem* thread_system, Timer* timer);
63
ThreadSystem::CondvarCapableMutex* mutex() LOCK_RETURNED(mutex_) {
67
// Optionally check that mutex is locked for debugging purposes.
68
void DCheckLocked() EXCLUSIVE_LOCKS_REQUIRED(mutex()) {
69
mutex_->DCheckLocked();
72
// Condition-style methods: The following three methods provide a simple
73
// condition-variable-style interface that can be used to coordinate the
74
// threads sharing the scheduler.
76
// Wait at most timeout_ms, or until Signal() is called.
77
void BlockingTimedWaitMs(int64 timeout_ms) EXCLUSIVE_LOCKS_REQUIRED(mutex()) {
78
BlockingTimedWaitUs(timeout_ms * Timer::kMsUs);
80
void BlockingTimedWaitUs(int64 timeout_us) EXCLUSIVE_LOCKS_REQUIRED(mutex());
82
// Non-blocking invocation of callback either when Signal() is called, or
83
// after timeout_ms have passed. Ownership of callback passes to the
84
// scheduler, which deallocates it after invocation. mutex() must be held on
85
// the initial call, and is locked for the duration of callback. Note that
86
// callback may be invoked in a different thread from the calling thread.
87
void TimedWaitMs(int64 timeout_ms, Function* callback)
88
EXCLUSIVE_LOCKS_REQUIRED(mutex());
90
// Signal threads in BlockingTimedWait[Ms,Us] and invoke TimedWaitMs
91
// callbacks. Performs outstanding work, including any triggered by the
92
// signal, before returning; note that this means it may drop the scheduler
93
// lock internally while doing callback invocation, which is different from
94
// the usual condition variable signal semantics.
95
void Signal() EXCLUSIVE_LOCKS_REQUIRED(mutex());
97
// Alarms. The following two methods provide a mechanism for scheduling
98
// alarm tasks, each run at a particular time.
100
// Schedules an alarm for absolute time wakeup_time_us, using the passed-in
101
// Function* as the alarm callback. Returns the created Alarm. Performs
102
// outstanding work. The returned alarm will own the callback and will clean
103
// itself and the callback when it is run or cancelled. NOTE in particular
104
// that calls to CancelAlarm must ensure the callback has not been invoked
105
// yet. This is why the scheduler mutex must be held for CancelAlarm.
106
Alarm* AddAlarmAtUs(int64 wakeup_time_us, Function* callback);
108
// Cancels an alarm, calling the Cancel() method and deleting the alarm
109
// object. Scheduler mutex must be held before call to ensure that alarm is
110
// not called back before cancellation occurs. Doesn't perform outstanding
111
// work. Returns true if the cancellation occurred. If false is returned,
112
// the alarm is already being run / has been run in another thread; if the
113
// alarm deletes itself on Cancel(), it may no longer safely be used.
115
// Note that once the user callback for the alarm returns it's no longer
116
// safe to call this (but this method is safe to call when the scheduler has
117
// committed to running the callback, it will just return false), so it's the
118
// caller's responsibility to properly synchronize between its callback and
119
// its invocation of this.
120
bool CancelAlarm(Alarm* alarm) EXCLUSIVE_LOCKS_REQUIRED(mutex());
122
// Finally, ProcessAlarmsOrWaitUs provides a mechanism to ensure that pending
123
// alarms are executed in the absence of other scheduler activity.
124
// ProcessAlarmsOrWaitUs: handle outstanding alarms, or if there are none wait
125
// until the next wakeup and handle alarms then before relinquishing control.
126
// Idle no longer than timeout_us. Passing in timeout_us=0 will run without
128
void ProcessAlarmsOrWaitUs(int64 timeout_us)
129
EXCLUSIVE_LOCKS_REQUIRED(mutex());
131
// Obtain the timer that the scheduler is using internally. Important if you
132
// and the scheduler want to agree on the passage of time.
133
Timer* timer() { return timer_; }
135
// Obtain the thread system used by the scheduler.
136
ThreadSystem* thread_system() { return thread_system_; }
138
// Internal method to kick the system because something of interest to the
139
// overridden AwaitWakeup method has happened. Exported here because C++
141
void Wakeup() { condvar_->Broadcast(); }
143
// These methods notify the scheduler of work sequences that may run work
144
// on it. They are only used for time simulations in MockScheduler and
145
// are no-ops during normal usage.
146
virtual void RegisterWorker(QueuedWorkerPool::Sequence* w);
147
virtual void UnregisterWorker(QueuedWorkerPool::Sequence* w);
149
// Run any alarms that have reached their deadline. Returns the time of the
150
// next deadline, or 0 if no further deadlines loom. Sets *ran_alarms if
151
// non-NULL and any alarms were run, otherwise leaves it untouched.
152
int64 RunAlarms(bool* ran_alarms) EXCLUSIVE_LOCKS_REQUIRED(mutex());
155
// Internal method to await a wakeup event. Block until wakeup_time_us (an
156
// absolute time since the epoch), or until something interesting (such as a
157
// call to Signal) occurs. This is virtual to permit us to mock it out (the
158
// mock simply advances time). This maybe called with 0 in case where there
159
// are no timers currently active.
160
virtual void AwaitWakeupUntilUs(int64 wakeup_time_us);
162
bool running_waiting_alarms() const { return running_waiting_alarms_; }
165
class CondVarTimeout;
166
class CondVarCallbackTimeout;
167
friend class SchedulerTest;
169
typedef std::set<Alarm*, CompareAlarms> AlarmSet;
171
void AddAlarmMutexHeldUs(int64 wakeup_time_us, Alarm* alarm);
172
void CancelWaiting(Alarm* alarm);
173
bool NoPendingAlarms();
175
ThreadSystem* thread_system_;
177
scoped_ptr<ThreadSystem::CondvarCapableMutex> mutex_;
178
// condvar_ tracks whether interesting (next-wakeup decreasing or
179
// signal_count_ increasing) events occur.
180
scoped_ptr<ThreadSystem::Condvar> condvar_;
181
uint32 index_; // Used to disambiguate alarms with equal deadlines
182
AlarmSet outstanding_alarms_; // Priority queue of future alarms
183
// An alarm may be deleted iff it is successfully removed from
184
// outstanding_alarms_.
185
int64 signal_count_; // Number of times Signal has been called
186
AlarmSet waiting_alarms_; // Alarms waiting for signal_count to change
187
bool running_waiting_alarms_; // True if we're in process of invoking
189
DISALLOW_COPY_AND_ASSIGN(Scheduler);
192
// A simple adapter class that permits blocking until an alarm has been run or
193
// cancelled. Designed for stack allocation.
195
// Note that success_ is guarded by the acquire/release semantics of
196
// atomic_bool and by monotonicity of done_. Field order (==initialization
197
// order) is important here.
198
class SchedulerBlockingFunction : public Function {
200
explicit SchedulerBlockingFunction(Scheduler* scheduler);
201
virtual ~SchedulerBlockingFunction();
203
virtual void Cancel();
204
// Block until called back, returning true for Run and false for Cancel.
207
Scheduler* scheduler_;
209
bool done_; // protected by scheduler_->mutex()
210
DISALLOW_COPY_AND_ASSIGN(SchedulerBlockingFunction);
213
} // namespace net_instaweb
215
#endif // PAGESPEED_KERNEL_THREAD_SCHEDULER_H_