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