1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
15 #ifndef COMMON_CEPH_TIMER_H
16 #define COMMON_CEPH_TIMER_H
18 #include <condition_variable>
20 #include <boost/intrusive/set.hpp>
24 /// Newly constructed timer should be suspended at point of
27 struct construct_suspended_t { };
28 constexpr construct_suspended_t construct_suspended { };
30 namespace timer_detail {
31 using boost::intrusive::member_hook;
32 using boost::intrusive::set_member_hook;
33 using boost::intrusive::link_mode;
34 using boost::intrusive::normal_link;
35 using boost::intrusive::set;
36 using boost::intrusive::constant_time_size;
37 using boost::intrusive::compare;
39 // Compared to the SafeTimer this does fewer allocations (you
40 // don't have to allocate a new Context every time you
41 // want to cue the next tick.)
43 // It also does not share a lock with the caller. If you call
44 // cancel event, it either cancels the event (and returns true) or
45 // you missed it. If this does not work for you, you can set up a
46 // flag and mutex of your own.
48 // You get to pick your clock. I like mono_clock, since I usually
49 // want to wait FOR a given duration. real_clock is worthwhile if
50 // you want to wait UNTIL a specific moment of wallclock time. If
51 // you want you can set up a timer that executes a function after
52 // you use up ten seconds of CPU time.
56 using sh = set_member_hook<link_mode<normal_link> >;
59 typename TC::time_point t;
61 std::function<void()> f;
66 event() : t(TC::time_point::min()), id(0) {}
67 event(uint64_t _id) : t(TC::time_point::min()), id(_id) {}
68 event(typename TC::time_point _t, uint64_t _id,
69 std::function<void()>&& _f) : t(_t), id(_id), f(_f) {}
70 event(typename TC::time_point _t, uint64_t _id,
71 const std::function<void()>& _f) : t(_t), id(_id), f(_f) {}
72 bool operator <(const event& e) {
73 return t == e.t ? id < e.id : t < e.t;
77 bool operator()(const event& e1, const event& e2) const {
78 return e1.t == e2.t ? e1.id < e2.id : e1.t < e2.t;
82 bool operator()(const event& e1, const event& e2) const {
87 using schedule_type = set<event,
88 member_hook<event, sh, &event::schedule_link>,
89 constant_time_size<false>,
90 compare<SchedCompare> >;
92 schedule_type schedule;
94 using event_set_type = set<event,
95 member_hook<event, sh, &event::event_link>,
96 constant_time_size<false>,
97 compare<EventCompare> >;
99 event_set_type events;
102 using lock_guard = std::lock_guard<std::mutex>;
103 using unique_lock = std::unique_lock<std::mutex>;
104 std::condition_variable cond;
106 event* running{ nullptr };
107 uint64_t next_id{ 0 };
112 void timer_thread() {
115 typename TC::time_point now = TC::now();
117 while (!schedule.empty()) {
118 auto p = schedule.begin();
119 // Should we wait for the future?
127 // Since we have only one thread it is impossible to have more
128 // than one running event
138 } // Otherwise the event requeued itself
141 if (schedule.empty())
144 cond.wait_until(l, schedule.begin()->t);
152 thread = std::thread(&timer::timer_thread, this);
155 // Create a suspended timer, jobs will be executed in order when
157 timer(construct_suspended_t) {
162 timer(const timer &) = delete;
163 timer& operator=(const timer &) = delete;
170 // Suspend operation of the timer (and let its thread die).
183 // Resume operation of the timer. (Must have been previously
191 assert(!thread.joinable());
192 thread = std::thread(&timer::timer_thread, this);
195 // Schedule an event in the relative future
196 template<typename Callable, typename... Args>
197 uint64_t add_event(typename TC::duration duration,
198 Callable&& f, Args&&... args) {
199 typename TC::time_point when = TC::now();
201 return add_event(when,
202 std::forward<Callable>(f),
203 std::forward<Args>(args)...);
206 // Schedule an event in the absolute future
207 template<typename Callable, typename... Args>
208 uint64_t add_event(typename TC::time_point when,
209 Callable&& f, Args&&... args) {
210 std::lock_guard<std::mutex> l(lock);
211 event& e = *(new event(
213 std::forward<std::function<void()> >(
214 std::bind(std::forward<Callable>(f),
215 std::forward<Args>(args)...))));
216 auto i = schedule.insert(e);
219 /* If the event we have just inserted comes before everything
220 * else, we need to adjust our timeout. */
221 if (i.first == schedule.begin())
224 // Previously each event was a context, identified by a
225 // pointer, and each context to be called only once. Since you
226 // can queue the same function pointer, member function,
227 // lambda, or functor up multiple times, identifying things by
228 // function for the purposes of cancellation is no longer
233 // Adjust the timeout of a currently-scheduled event (relative)
234 bool adjust_event(uint64_t id, typename TC::duration duration) {
235 return adjust_event(id, TC::now() + duration);
238 // Adjust the timeout of a currently-scheduled event (absolute)
239 bool adjust_event(uint64_t id, typename TC::time_point when) {
240 std::lock_guard<std::mutex> l(lock);
243 typename event_set_type::iterator it = events.find(key);
245 if (it == events.end())
257 // Cancel an event. If the event has already come and gone (or you
258 // never submitted it) you will receive false. Otherwise you will
259 // receive true and it is guaranteed the event will not execute.
260 bool cancel_event(const uint64_t id) {
261 std::lock_guard<std::mutex> l(lock);
263 auto p = events.find(dummy);
264 if (p == events.end()) {
276 // Reschedules a currently running event in the relative
277 // future. Must be called only from an event executed by this
278 // timer. If you have a function that can be called either from
279 // this timer or some other way, it is your responsibility to make
280 // sure it can tell the difference only does not call
281 // reschedule_me in the non-timer case.
283 // Returns an event id. If you had an event_id from the first
284 // scheduling, replace it with this return value.
285 uint64_t reschedule_me(typename TC::duration duration) {
286 return reschedule_me(TC::now() + duration);
289 // Reschedules a currently running event in the absolute
290 // future. Must be called only from an event executed by this
291 // timer. if you have a function that can be called either from
292 // this timer or some other way, it is your responsibility to make
293 // sure it can tell the difference only does not call
294 // reschedule_me in the non-timer case.
296 // Returns an event id. If you had an event_id from the first
297 // scheduling, replace it with this return value.
298 uint64_t reschedule_me(typename TC::time_point when) {
299 if (std::this_thread::get_id() != thread.get_id())
300 throw std::make_error_condition(std::errc::operation_not_permitted);
301 std::lock_guard<std::mutex> l(lock);
303 uint64_t id = ++next_id;
305 schedule.insert(*running);
306 events.insert(*running);
308 // Hacky, but keeps us from being deleted
311 // Same function, but you get a new ID.
315 // Remove all events from the queue.
316 void cancel_all_events() {
317 std::lock_guard<std::mutex> l(lock);
318 while (!events.empty()) {
319 auto p = events.begin();
329 using timer_detail::timer;