periodic_task.cc
1 // Copyright 2018, Beeri 15. All rights reserved.
2 // Author: Roman Gershman (romange@gmail.com)
3 //
4 #include "util/asio/periodic_task.h"
5 
6 #include "base/logging.h"
7 #include "util/asio/yield.h"
8 #include "util/stats/varz_stats.h"
9 
10 
11 namespace util {
12 
13 DEFINE_VARZ(VarzCount, task_hang_times);
14 
15 void PeriodicTask::Cancel() {
16  if ((state_ & ALARMED) == 0)
17  return;
18  VLOG(1) << "Cancel";
19  state_ |= SHUTDOWN;
20  timer_.expires_after(duration_t(0));
21  error_code ec;
22  while (state_ & ALARMED) {
23  timer_.async_wait(fibers_ext::yield[ec]);
24  }
25  state_ &= ~uint8_t(SHUTDOWN);
26  VLOG(1) << "Cancel Finish";
27 }
28 
29 void PeriodicTask::Alarm() {
30  CHECK_EQ(0, state_ & ALARMED) << "Can not Start on already alarmed timer, run Cancel first";
31  last_ = timer_t::clock_type::now();
32  timer_.expires_at(last_ + d_);
33  state_ |= ALARMED;
34 }
35 
36 void PeriodicWorkerTask::ResetErrorState() {
37  if (IsHanging()) {
38  task_hang_times.IncBy(-1);
39  is_hanging_ = false;
40  }
41 
42  number_skips_ = 0;
43 }
44 
45 void PeriodicWorkerTask::Epilog() {
46  // We need to lock m_ because otherwise the object could be destroyed right after
47  // is_running_ was reset and before cond_ notified. Then this function would have a data race.
48  std::lock_guard<::boost::fibers::mutex> lock(m_);
49  is_running_.store(false);
50  cond_.notify_all();
51 }
52 
53 void PeriodicWorkerTask::HandleSkipRun() {
54  ++number_skips_;
55  if (!is_hanging_ && number_skips_ > opts_.skip_run_margin) {
56  is_hanging_ = true;
57  task_hang_times.Inc();
58  }
59  if (IsHanging()) {
60  LOG(ERROR) << "Task " << opts_.name << " hands for " << number_skips_ << " times";
61  }
62 }
63 
64 void PeriodicWorkerTask::Cancel() {
65  pt_.Cancel();
66 
67  std::unique_lock<::boost::fibers::mutex> lock(m_);
68  cond_.wait(lock, [this]() { return !is_running_; });
69 
70  ResetErrorState();
71  VLOG(1) << "PeriodicWorkerTask::Cancel end";
72 }
73 
74 } // namespace util