1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "base/scheduler.h"
31
32 #include <cstdlib>
33 #include <functional>
34 #include <map>
35 #include <utility>
36
37 #include "base/clock.h"
38 #include "base/logging.h"
39 #include "base/mutex.h"
40 #include "base/port.h"
41 #include "base/singleton.h"
42 #include "base/thread.h"
43 #include "base/unnamed_event.h"
44 #include "base/util.h"
45
46 namespace mozc {
47 namespace {
48
49 class TimerThread final : public Thread {
50 public:
TimerThread(std::function<void ()> callback,uint32 due_time,uint32 interval)51 TimerThread(std::function<void()> callback,
52 uint32 due_time,
53 uint32 interval)
54 : callback_(callback),
55 due_time_(due_time),
56 interval_(interval) {
57 CHECK(due_time_ != 0 || interval_ != 0)
58 << "Either of due_time or interval must be non 0.";
59 }
60
~TimerThread()61 ~TimerThread() override {
62 SignalQuit();
63 Join();
64 }
65
Run()66 void Run() override {
67 if (event_.Wait(due_time_)) {
68 VLOG(1) << "Received notification event";
69 return;
70 }
71
72 VLOG(2) << "call TimerCallback()";
73 callback_();
74
75 if (interval_ == 0) {
76 VLOG(2) << "Run() end";
77 return;
78 }
79
80 while (true) {
81 if (event_.Wait(interval_)) {
82 VLOG(1) << "Received notification event";
83 return;
84 }
85 VLOG(2) << "call TimerCallback()";
86 callback_();
87 }
88 }
89
90 private:
SignalQuit()91 void SignalQuit() {
92 const bool result = event_.Notify();
93 DCHECK(result);
94 }
95
96 std::function<void()> callback_;
97
98 // The amount of time to elapse before the timer is to be set to the
99 // signaled state for the first time, in milliseconds.
100 uint32 due_time_;
101
102 // The period of the timer, in milliseconds. If this is zero, the
103 // timer is one-shot timer. If this is greater than zero, the timer
104 // is periodic.
105 uint32 interval_;
106
107 UnnamedEvent event_;
108
109 DISALLOW_COPY_AND_ASSIGN(TimerThread);
110 };
111
112 class QueueTimer final {
113 public:
QueueTimer(std::function<void ()> callback,uint32 due_time,uint32 period)114 QueueTimer(std::function<void()> callback,
115 uint32 due_time,
116 uint32 period)
117 : timer_thread_(callback, due_time, period) {
118 }
119
Start()120 void Start() {
121 timer_thread_.Start("QueueTimer");
122 }
123
124 private:
125 TimerThread timer_thread_;
126
127 DISALLOW_COPY_AND_ASSIGN(QueueTimer);
128 };
129
130 class Job {
131 public:
Job(const Scheduler::JobSetting & setting)132 explicit Job(const Scheduler::JobSetting &setting) :
133 setting_(setting),
134 skip_count_(0),
135 backoff_count_(0),
136 timer_(NULL),
137 running_(false) {}
138
~Job()139 ~Job() {
140 set_timer(NULL);
141 }
142
setting() const143 const Scheduler::JobSetting setting() const {
144 return setting_;
145 }
146
set_skip_count(uint32 skip_count)147 void set_skip_count(uint32 skip_count) {
148 skip_count_ = skip_count;
149 }
150
skip_count() const151 uint32 skip_count() const {
152 return skip_count_;
153 }
154
set_backoff_count(uint32 backoff_count)155 void set_backoff_count(uint32 backoff_count) {
156 backoff_count_ = backoff_count;
157 }
158
backoff_count() const159 uint32 backoff_count() const {
160 return backoff_count_;
161 }
162
set_timer(QueueTimer * timer)163 void set_timer(QueueTimer *timer) {
164 if (timer_ != NULL) {
165 delete timer_;
166 }
167 timer_ = timer;
168 }
169
timer() const170 const QueueTimer *timer() const {
171 return timer_;
172 }
173
mutable_timer()174 QueueTimer *mutable_timer() {
175 return timer_;
176 }
177
set_running(bool running)178 void set_running(bool running) {
179 running_ = running;
180 }
181
running() const182 bool running() const {
183 return running_;
184 }
185
186 private:
187 Scheduler::JobSetting setting_;
188 uint32 skip_count_;
189 uint32 backoff_count_;
190 QueueTimer *timer_;
191 bool running_;
192
193 // TODO(hsumita): Use DISALLOW_COPY_AND_ASSIGN(Job).
194 };
195
196 class SchedulerImpl : public Scheduler::SchedulerInterface {
197 public:
SchedulerImpl()198 SchedulerImpl() {
199 Util::SetRandomSeed(static_cast<uint32>(Clock::GetTime()));
200 }
201
~SchedulerImpl()202 virtual ~SchedulerImpl() {
203 RemoveAllJobs();
204 }
205
RemoveAllJobs()206 virtual void RemoveAllJobs() {
207 scoped_lock l(&mutex_);
208 jobs_.clear();
209 }
210
ValidateSetting(const Scheduler::JobSetting & job_setting) const211 void ValidateSetting(const Scheduler::JobSetting &job_setting) const {
212 DCHECK_GT(job_setting.name().size(), 0);
213 DCHECK_NE(0, job_setting.default_interval());
214 DCHECK_NE(0, job_setting.max_interval());
215 // do not use DCHECK_NE as a type checker raises an error.
216 DCHECK(job_setting.callback() != NULL);
217 }
218
AddJob(const Scheduler::JobSetting & job_setting)219 virtual bool AddJob(const Scheduler::JobSetting &job_setting) {
220 scoped_lock l(&mutex_);
221
222 ValidateSetting(job_setting);
223 if (HasJob(job_setting.name())) {
224 LOG(WARNING) << "Job " << job_setting.name() << " is already registered";
225 return false;
226 }
227
228 std::pair<std::map<string, Job>::iterator, bool> insert_result =
229 jobs_.insert(std::make_pair(job_setting.name(), Job(job_setting)));
230 if (!insert_result.second) {
231 LOG(ERROR) << "insert failed";
232 return false;
233 }
234 Job *job = &insert_result.first->second;
235 DCHECK(job);
236
237 const uint32 delay = CalcDelay(job_setting);
238 // DON'T copy job instance after set_timer() not to delete timer twice.
239 // TODO(hsumita): Make Job class uncopiable.
240 job->set_timer(new QueueTimer(std::bind(TimerCallback, job), delay,
241 job_setting.default_interval()));
242 if (job->timer() == NULL) {
243 LOG(ERROR) << "failed to create QueueTimer";
244 return false;
245 }
246 job->mutable_timer()->Start();
247 return true;
248 }
249
RemoveJob(const string & name)250 virtual bool RemoveJob(const string &name) {
251 scoped_lock l(&mutex_);
252 if (!HasJob(name)) {
253 LOG(WARNING) << "Job " << name << " is not registered";
254 return false;
255 }
256 return (jobs_.erase(name) != 0);
257 }
258
HasJob(const string & name) const259 virtual bool HasJob(const string &name) const {
260 return (jobs_.find(name) != jobs_.end());
261 }
262
263 private:
TimerCallback(void * param)264 static void TimerCallback(void *param) {
265 Job *job = reinterpret_cast<Job *>(param);
266 DCHECK(job);
267 if (job->running()) {
268 return;
269 }
270 if (job->skip_count()) {
271 job->set_skip_count(job->skip_count() - 1);
272 VLOG(3) << "Backoff = " << job->backoff_count()
273 << " skip_count = " << job->skip_count();
274 return;
275 }
276 job->set_running(true);
277 Scheduler::JobSetting::CallbackFunc callback = job->setting().callback();
278 DCHECK(callback != NULL);
279 const bool success = callback(job->setting().data());
280 job->set_running(false);
281 if (success) {
282 job->set_backoff_count(0);
283 } else {
284 const uint32 new_backoff_count = (job->backoff_count() == 0) ?
285 1 : job->backoff_count() * 2;
286 if (new_backoff_count * job->setting().default_interval()
287 < job->setting().max_interval()) {
288 job->set_backoff_count(new_backoff_count);
289 }
290 job->set_skip_count(job->backoff_count());
291 }
292 }
293
CalcDelay(const Scheduler::JobSetting & job_setting)294 uint32 CalcDelay(const Scheduler::JobSetting &job_setting) {
295 uint32 delay = job_setting.delay_start();
296 if (job_setting.random_delay() != 0) {
297 delay += Util::Random(job_setting.random_delay());
298 }
299 return delay;
300 }
301
302 std::map<string, Job> jobs_;
303 Mutex mutex_;
304
305 DISALLOW_COPY_AND_ASSIGN(SchedulerImpl);
306 };
307
308 Scheduler::SchedulerInterface *g_scheduler_handler = NULL;
309
GetSchedulerHandler()310 Scheduler::SchedulerInterface *GetSchedulerHandler() {
311 if (g_scheduler_handler != NULL) {
312 return g_scheduler_handler;
313 } else {
314 return Singleton<SchedulerImpl>::get();
315 }
316 }
317 } // namespace
318
AddJob(const Scheduler::JobSetting & job_setting)319 bool Scheduler::AddJob(const Scheduler::JobSetting &job_setting) {
320 return GetSchedulerHandler()->AddJob(job_setting);
321 }
322
RemoveJob(const string & name)323 bool Scheduler::RemoveJob(const string &name) {
324 return GetSchedulerHandler()->RemoveJob(name);
325 }
326
RemoveAllJobs()327 void Scheduler::RemoveAllJobs() {
328 GetSchedulerHandler()->RemoveAllJobs();
329 }
330
HasJob(const string & name)331 bool Scheduler::HasJob(const string &name) {
332 return GetSchedulerHandler()->HasJob(name);
333 }
334
SetSchedulerHandler(SchedulerInterface * handler)335 void Scheduler::SetSchedulerHandler(SchedulerInterface *handler) {
336 g_scheduler_handler = handler;
337 }
338 } // namespace mozc
339