1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2002-2011 Free Software Foundation Europe e.V.
5    Copyright (C) 2019-2019 Bareos GmbH & Co. KG
6 
7    This program is Free Software; you can redistribute it and/or
8    modify it under the terms of version three of the GNU Affero General Public
9    License as published by the Free Software Foundation and included
10    in the file LICENSE.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15    Affero General Public License for more details.
16 
17    You should have received a copy of the GNU Affero General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA.
21 */
22 
23 #include "include/bareos.h"
24 #include "lib/timer_thread.h"
25 #include "include/jcr.h"
26 #include "lib/thread_specific_data.h"
27 #include "include/make_unique.h"
28 
29 #include <atomic>
30 #include <algorithm>
31 #include <cassert>
32 #include <chrono>
33 #include <condition_variable>
34 #include <mutex>
35 #include <thread>
36 
37 namespace TimerThread {
38 
39 /* clang-format off */
40 static std::chrono::milliseconds idle_timeout_interval_milliseconds(60 * 1000);
41 static std::atomic<std::time_t> calendar_time_on_last_timer_run(time(nullptr));
42 /* clang-format on */
43 
44 static std::mutex timer_sleep_mutex;
45 static std::condition_variable timer_sleep_condition;
46 static bool wakeup_event_occured = false;
47 
48 static void TimerThread(void);
49 
50 enum class TimerThreadState
51 {
52   IS_NOT_INITIALZED,
53   IS_STARTING,
54   IS_RUNNING,
55   IS_SHUTTING_DOWN,
56   IS_SHUT_DOWN
57 };
58 
59 static std::atomic<TimerThreadState> timer_thread_state(
60     TimerThreadState::IS_NOT_INITIALZED);
61 static std::atomic<bool> quit_timer_thread(false);
62 
63 static std::unique_ptr<std::thread> timer_thread;
64 static std::mutex controlled_items_list_mutex;
65 
66 static std::vector<TimerThread::Timer*> controlled_items_list;
67 
Start(void)68 bool Start(void)
69 {
70   if (timer_thread_state != TimerThreadState::IS_NOT_INITIALZED
71       && timer_thread_state != TimerThreadState::IS_SHUT_DOWN) {
72     return false;
73   }
74 
75   Dmsg0(800, "Starting timer thread\n");
76 
77   quit_timer_thread = false;
78   timer_thread = std::make_unique<std::thread>(TimerThread);
79 
80   int timeout = 0;
81   while (timer_thread_state.load() != TimerThreadState::IS_RUNNING
82          && ++timeout < 2000) {
83     std::this_thread::sleep_for(std::chrono::milliseconds(1));
84   }
85 
86   return true;
87 }
88 
WakeTimer()89 static void WakeTimer()
90 {
91   std::lock_guard<std::mutex> l(timer_sleep_mutex);
92   wakeup_event_occured = true;
93   timer_sleep_condition.notify_one();
94 }
95 
Stop(void)96 void Stop(void)
97 {
98   if (timer_thread_state != TimerThreadState::IS_RUNNING) { return; }
99 
100   quit_timer_thread = true;
101   WakeTimer();
102 
103   timer_thread->join();
104 }
105 
NewTimer(void)106 TimerThread::Timer* NewTimer(void)
107 {
108   TimerThread::Timer* t = new TimerThread::Timer;
109 
110   std::lock_guard<std::mutex> l(controlled_items_list_mutex);
111   controlled_items_list.push_back(t);
112 
113   if (timer_thread_state != TimerThreadState::IS_RUNNING) { Start(); }
114 
115   return t;
116 }
117 
RegisterTimer(TimerThread::Timer * t)118 bool RegisterTimer(TimerThread::Timer* t)
119 {
120   assert(t->user_callback != nullptr);
121 
122   TimerThread::Timer wd_copy;
123 
124   {
125     std::lock_guard<std::mutex> l(controlled_items_list_mutex);
126 
127     if (std::find(controlled_items_list.begin(), controlled_items_list.end(), t)
128         == controlled_items_list.end()) {
129       return false;
130     }
131 
132     t->scheduled_run_timepoint = std::chrono::steady_clock::now() + t->interval;
133     t->is_active = true;
134 
135     wd_copy = *t;
136   }
137 
138   Dmsg3(800, "Registered timer interval %d%s\n", wd_copy.interval,
139         wd_copy.single_shot ? " one shot" : "");
140 
141   WakeTimer();
142 
143   return true;
144 }
145 
UnregisterTimer(TimerThread::Timer * t)146 bool UnregisterTimer(TimerThread::Timer* t)
147 {
148   std::lock_guard<std::mutex> l(controlled_items_list_mutex);
149 
150   auto pos = std::find(controlled_items_list.begin(),
151                        controlled_items_list.end(), t);
152 
153   if (pos != controlled_items_list.end()) {
154     if ((*pos)->user_destructor) { (*pos)->user_destructor((*pos)); }
155     delete (*pos);
156     controlled_items_list.erase(pos);
157     Dmsg1(800, "Unregistered timer %p\n", t);
158     return true;
159   } else {
160     Dmsg1(800, "Failed to unregister timer %p\n", t);
161     return false;
162   }
163 }
164 
IsRegisteredTimer(const TimerThread::Timer * t)165 bool IsRegisteredTimer(const TimerThread::Timer* t)
166 {
167   std::lock_guard<std::mutex> l(controlled_items_list_mutex);
168 
169   auto pos = std::find(controlled_items_list.begin(),
170                        controlled_items_list.end(), t);
171 
172   return pos != controlled_items_list.end();
173 }
174 
Cleanup()175 static void Cleanup()
176 {
177   std::lock_guard<std::mutex> l(controlled_items_list_mutex);
178 
179   for (auto p : controlled_items_list) {
180     if (p->user_destructor) { p->user_destructor(p); }
181     delete p;
182   }
183   controlled_items_list.clear();
184 }
185 
SleepUntil(std::chrono::steady_clock::time_point next_timer_run)186 static void SleepUntil(std::chrono::steady_clock::time_point next_timer_run)
187 {
188   std::unique_lock<std::mutex> l(timer_sleep_mutex);
189   timer_sleep_condition.wait_until(l, next_timer_run,
190                                    []() { return wakeup_event_occured; });
191   wakeup_event_occured = false;
192 }
193 
LogMessage(TimerThread::Timer * p)194 static void LogMessage(TimerThread::Timer* p)
195 {
196   Dmsg2(3400, "Timer callback p=0x%p scheduled_run_timepoint=%d\n", p,
197         p->scheduled_run_timepoint);
198 }
199 
RunOneItem(TimerThread::Timer * p,std::chrono::steady_clock::time_point & next_timer_run)200 static bool RunOneItem(TimerThread::Timer* p,
201                        std::chrono::steady_clock::time_point& next_timer_run)
202 {
203   std::chrono::time_point<std::chrono::steady_clock> last_timer_run_timepoint
204       = std::chrono::steady_clock::now();
205 
206   bool remove_from_list = false;
207   if (p->is_active && last_timer_run_timepoint > p->scheduled_run_timepoint) {
208     LogMessage(p);
209     p->user_callback(p);
210     if (p->single_shot) {
211       if (p->user_destructor) { p->user_destructor(p); }
212       delete p;
213       remove_from_list = true;
214     } else {
215       p->scheduled_run_timepoint = last_timer_run_timepoint + p->interval;
216     }
217   }
218   next_timer_run = min(p->scheduled_run_timepoint, next_timer_run);
219   return remove_from_list;
220 }
221 
RunAllItemsAndRemoveOneShotItems(std::chrono::steady_clock::time_point & next_timer_run)222 static void RunAllItemsAndRemoveOneShotItems(
223     std::chrono::steady_clock::time_point& next_timer_run)
224 {
225   std::unique_lock<std::mutex> l(controlled_items_list_mutex);
226 
227   auto new_end_of_vector = std::remove_if(  // one_shot items will be removed
228       controlled_items_list.begin(), controlled_items_list.end(),
229       [&next_timer_run](TimerThread::Timer* p) {
230         calendar_time_on_last_timer_run = time(nullptr);
231 
232         return RunOneItem(p, next_timer_run);
233       });
234 
235   controlled_items_list.erase(new_end_of_vector, controlled_items_list.end());
236 }
237 
TimerThread(void)238 static void TimerThread(void)
239 {
240   SetJcrInThreadSpecificData(nullptr);
241 
242   Dmsg0(800, "Timer thread started\n");
243   timer_thread_state = TimerThreadState::IS_RUNNING;
244 
245   while (!quit_timer_thread) {
246     std::chrono::steady_clock::time_point next_timer_run
247         = std::chrono::steady_clock::now() + idle_timeout_interval_milliseconds;
248 
249     RunAllItemsAndRemoveOneShotItems(next_timer_run);
250 
251     SleepUntil(next_timer_run);
252   }  // while (!quit_timer_thread)
253 
254   Cleanup();
255   timer_thread_state = TimerThreadState::IS_SHUT_DOWN;
256 }
257 
258 class TimerThreadGuard {
259  public:
260   TimerThreadGuard() = default;
~TimerThreadGuard()261   ~TimerThreadGuard()
262   {
263     if (timer_thread_state == TimerThreadState::IS_RUNNING) { Stop(); }
264   }
265 };
266 
267 static TimerThreadGuard timer_thread_guard;
268 
SetTimerIdleSleepTime(std::chrono::seconds time)269 void SetTimerIdleSleepTime(std::chrono::seconds time)
270 {
271   std::lock_guard<std::mutex> lg(controlled_items_list_mutex);
272   idle_timeout_interval_milliseconds = time;
273 }
274 
CurrentThreadIsTimerThread()275 bool CurrentThreadIsTimerThread()
276 {
277   return (timer_thread_state == TimerThreadState::IS_RUNNING
278           && (std::this_thread::get_id() == timer_thread->get_id()));
279 }
280 
GetCalenderTimeOnLastTimerThreadRun()281 std::time_t GetCalenderTimeOnLastTimerThreadRun()
282 {
283   return calendar_time_on_last_timer_run;
284 }
285 
286 }  // namespace TimerThread
287