| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "base/scheduler.h" |
| |
| #include <cstdlib> |
| #include <map> |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "base/mutex.h" |
| #include "base/port.h" |
| #include "base/scoped_ptr.h" |
| #include "base/singleton.h" |
| #include "base/timer.h" |
| #include "base/util.h" |
| |
| namespace mozc { |
| namespace { |
| class QueueTimer : public Timer { |
| public: |
| QueueTimer(void (*callback)(void *), // NOLINT |
| void *arg, |
| uint32 due_time, |
| uint32 period) |
| : callback_(callback), |
| arg_(arg), |
| due_time_(due_time), |
| period_(period) { |
| } |
| |
| virtual ~QueueTimer() {} |
| |
| bool Start() { |
| return Timer::Start(due_time_, period_); |
| } |
| |
| virtual void Signaled() { |
| callback_(arg_); |
| } |
| |
| private: |
| void (*callback_)(void *); |
| void *arg_; |
| uint32 due_time_; |
| uint32 period_; |
| |
| DISALLOW_COPY_AND_ASSIGN(QueueTimer); |
| }; |
| |
| class Job { |
| public: |
| explicit Job(const Scheduler::JobSetting &setting) : |
| setting_(setting), |
| skip_count_(0), |
| backoff_count_(0), |
| timer_(NULL), |
| running_(false) {} |
| |
| ~Job() { |
| set_timer(NULL); |
| } |
| |
| const Scheduler::JobSetting setting() const { |
| return setting_; |
| } |
| |
| void set_skip_count(uint32 skip_count) { |
| skip_count_ = skip_count; |
| } |
| |
| uint32 skip_count() const { |
| return skip_count_; |
| } |
| |
| void set_backoff_count(uint32 backoff_count) { |
| backoff_count_ = backoff_count; |
| } |
| |
| uint32 backoff_count() const { |
| return backoff_count_; |
| } |
| |
| void set_timer(QueueTimer *timer) { |
| if (timer_ != NULL) { |
| delete timer_; |
| } |
| timer_ = timer; |
| } |
| |
| const QueueTimer *timer() const { |
| return timer_; |
| } |
| |
| QueueTimer *mutable_timer() { |
| return timer_; |
| } |
| |
| void set_running(bool running) { |
| running_ = running; |
| } |
| |
| bool running() const { |
| return running_; |
| } |
| |
| private: |
| Scheduler::JobSetting setting_; |
| uint32 skip_count_; |
| uint32 backoff_count_; |
| QueueTimer *timer_; |
| bool running_; |
| |
| // TODO(hsumita): Use DISALLOW_COPY_AND_ASSIGN(Job). |
| }; |
| |
| class SchedulerImpl : public Scheduler::SchedulerInterface { |
| public: |
| SchedulerImpl() { |
| Util::SetRandomSeed(static_cast<uint32>(Util::GetTime())); |
| } |
| |
| virtual ~SchedulerImpl() { |
| RemoveAllJobs(); |
| } |
| |
| virtual void RemoveAllJobs() { |
| scoped_lock l(&mutex_); |
| jobs_.clear(); |
| } |
| |
| void ValidateSetting(const Scheduler::JobSetting &job_setting) const { |
| DCHECK_GT(job_setting.name().size(), 0); |
| DCHECK_NE(0, job_setting.default_interval()); |
| DCHECK_NE(0, job_setting.max_interval()); |
| // do not use DCHECK_NE as a type checker raises an error. |
| DCHECK(job_setting.callback() != NULL); |
| } |
| |
| virtual bool AddJob(const Scheduler::JobSetting &job_setting) { |
| scoped_lock l(&mutex_); |
| |
| ValidateSetting(job_setting); |
| if (HasJob(job_setting.name())) { |
| LOG(WARNING) << "Job " << job_setting.name() << " is already registered"; |
| return false; |
| } |
| |
| pair<map<string, Job>::iterator, bool> insert_result |
| = jobs_.insert(make_pair(job_setting.name(), Job(job_setting))); |
| if (!insert_result.second) { |
| LOG(ERROR) << "insert failed"; |
| return false; |
| } |
| Job *job = &insert_result.first->second; |
| DCHECK(job); |
| |
| const uint32 delay = CalcDelay(job_setting); |
| // DON'T copy job instance after set_timer() not to delete timer twice. |
| // TODO(hsumita): Make Job class uncopiable. |
| job->set_timer(new QueueTimer(&TimerCallback, job, delay, |
| job_setting.default_interval())); |
| if (job->timer() == NULL) { |
| LOG(ERROR) << "failed to create QueueTimer"; |
| return false; |
| } |
| const bool started = job->mutable_timer()->Start(); |
| if (started) { |
| return true; |
| } else { |
| job->set_timer(NULL); |
| return false; |
| } |
| } |
| |
| virtual bool RemoveJob(const string &name) { |
| scoped_lock l(&mutex_); |
| if (!HasJob(name)) { |
| LOG(WARNING) << "Job " << name << " is not registered"; |
| return false; |
| } |
| return (jobs_.erase(name) != 0); |
| } |
| |
| private: |
| static void TimerCallback(void *param) { |
| Job *job = reinterpret_cast<Job *>(param); |
| DCHECK(job); |
| if (job->running()) { |
| return; |
| } |
| if (job->skip_count()) { |
| job->set_skip_count(job->skip_count() - 1); |
| VLOG(3) << "Backoff = " << job->backoff_count() |
| << " skip_count = " << job->skip_count(); |
| return; |
| } |
| job->set_running(true); |
| Scheduler::JobSetting::CallbackFunc callback = job->setting().callback(); |
| DCHECK(callback != NULL); |
| const bool success = callback(job->setting().data()); |
| job->set_running(false); |
| if (success) { |
| job->set_backoff_count(0); |
| } else { |
| const uint32 new_backoff_count = (job->backoff_count() == 0) ? |
| 1 : job->backoff_count() * 2; |
| if (new_backoff_count * job->setting().default_interval() |
| < job->setting().max_interval()) { |
| job->set_backoff_count(new_backoff_count); |
| } |
| job->set_skip_count(job->backoff_count()); |
| } |
| } |
| |
| bool HasJob(const string &name) const { |
| return (jobs_.find(name) != jobs_.end()); |
| } |
| |
| uint32 CalcDelay(const Scheduler::JobSetting &job_setting) { |
| uint32 delay = job_setting.delay_start(); |
| if (job_setting.random_delay() != 0) { |
| delay += Util::Random(job_setting.random_delay()); |
| } |
| return delay; |
| } |
| |
| map<string, Job> jobs_; |
| Mutex mutex_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SchedulerImpl); |
| }; |
| |
| Scheduler::SchedulerInterface *g_scheduler_handler = NULL; |
| |
| Scheduler::SchedulerInterface *GetSchedulerHandler() { |
| if (g_scheduler_handler != NULL) { |
| return g_scheduler_handler; |
| } else { |
| return Singleton<SchedulerImpl>::get(); |
| } |
| } |
| } // namespace |
| |
| bool Scheduler::AddJob(const Scheduler::JobSetting &job_setting) { |
| return GetSchedulerHandler()->AddJob(job_setting); |
| } |
| |
| bool Scheduler::RemoveJob(const string &name) { |
| return GetSchedulerHandler()->RemoveJob(name); |
| } |
| |
| void Scheduler::RemoveAllJobs() { |
| GetSchedulerHandler()->RemoveAllJobs(); |
| } |
| |
| void Scheduler::SetSchedulerHandler(SchedulerInterface *handler) { |
| g_scheduler_handler = handler; |
| } |
| } // namespace mozc |