Seastar
High performance C++ framework for concurrent servers
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
timer-set.hh
1/*
2 * Copyright (C) 2014 Cloudius Systems, Ltd.
3 */
4
5/*
6 * Imported from OSv:
7 *
8 * Copyright (C) 2014 Cloudius Systems, Ltd.
9 *
10 * This work is open source software, licensed under the terms of the
11 * BSD license as described in the LICENSE file in the top-level directory.
12 */
13
14#pragma once
15
16#include <seastar/core/bitset-iter.hh>
18#include <seastar/util/assert.hh>
19#ifndef SEASTAR_MODULE
20#include <boost/intrusive/list.hpp>
21#include <exception>
22#include <array>
23#include <bitset>
24#include <limits>
25#endif
26
27namespace seastar {
28
29namespace internal {
30void log_timer_callback_exception(std::exception_ptr) noexcept;
31}
32
44template<typename Timer, boost::intrusive::list_member_hook<> Timer::*link>
45class timer_set {
46public:
47 using time_point = typename Timer::time_point;
48 using timer_list_t = boost::intrusive::list<Timer, boost::intrusive::member_hook<Timer, boost::intrusive::list_member_hook<>, link>>;
49private:
50 using duration = typename Timer::duration;
51 using timestamp_t = typename Timer::duration::rep;
52
53 static constexpr timestamp_t max_timestamp = std::numeric_limits<timestamp_t>::max();
54 static constexpr int timestamp_bits = std::numeric_limits<timestamp_t>::digits;
55
56 // The last bucket is reserved for active timers with timeout <= _last.
57 static constexpr int n_buckets = timestamp_bits + 1;
58
59 std::array<timer_list_t, n_buckets> _buckets;
60 timestamp_t _last;
61 timestamp_t _next;
62
63 std::bitset<n_buckets> _non_empty_buckets;
64private:
65 static timestamp_t get_timestamp(time_point _time_point) noexcept
66 {
67 return _time_point.time_since_epoch().count();
68 }
69
70 static timestamp_t get_timestamp(Timer& timer) noexcept
71 {
72 return get_timestamp(timer.get_timeout());
73 }
74
75 int get_index(timestamp_t timestamp) const noexcept
76 {
77 if (timestamp <= _last) {
78 return n_buckets - 1;
79 }
80
81 auto index = bitsets::count_leading_zeros(timestamp ^ _last);
82 SEASTAR_ASSERT(index < n_buckets - 1);
83 return index;
84 }
85
86 int get_index(Timer& timer) const noexcept
87 {
88 return get_index(get_timestamp(timer));
89 }
90
91 int get_last_non_empty_bucket() const noexcept
92 {
93 return bitsets::get_last_set(_non_empty_buckets);
94 }
95
96public:
97 timer_set() noexcept
98 : _last(0)
99 , _next(max_timestamp)
100 , _non_empty_buckets(0)
101 {
102 }
103
104 ~timer_set() {
105 for (auto&& list : _buckets) {
106 while (!list.empty()) {
107 auto& timer = *list.begin();
108 timer.cancel();
109 }
110 }
111 }
112
130 bool insert(Timer& timer) noexcept
131 {
132 auto timestamp = get_timestamp(timer);
133 auto index = get_index(timestamp);
134
135 _buckets[index].push_back(timer);
136 _non_empty_buckets[index] = true;
137
138 if (timestamp < _next) {
139 _next = timestamp;
140 return true;
141 }
142 return false;
143 }
144
156 void remove(Timer& timer) noexcept
157 {
158 auto index = get_index(timer);
159 auto& list = _buckets[index];
160 list.erase(list.iterator_to(timer));
161 if (list.empty()) {
162 _non_empty_buckets[index] = false;
163 }
164 }
165
169 void remove(Timer& timer, timer_list_t& expired) noexcept
170 {
171 if (timer._expired) {
172 expired.erase(expired.iterator_to(timer));
173 timer._expired = false;
174 } else {
175 remove(timer);
176 }
177 }
178
193 timer_list_t expire(time_point now) noexcept
194 {
195 timer_list_t exp;
196 auto timestamp = get_timestamp(now);
197
198 if (timestamp < _last) {
199 abort();
200 }
201
202 auto index = get_index(timestamp);
203
204 for (int i : bitsets::for_each_set(_non_empty_buckets, index + 1)) {
205 exp.splice(exp.end(), _buckets[i]);
206 _non_empty_buckets[i] = false;
207 }
208
209 _last = timestamp;
210 _next = max_timestamp;
211
212 auto& list = _buckets[index];
213 while (!list.empty()) {
214 auto& timer = *list.begin();
215 list.pop_front();
216 if (timer.get_timeout() <= now) {
217 exp.push_back(timer);
218 } else {
219 insert(timer);
220 }
221 }
222
223 _non_empty_buckets[index] = !list.empty();
224
225 if (_next == max_timestamp && _non_empty_buckets.any()) {
226 for (auto& timer : _buckets[get_last_non_empty_bucket()]) {
227 _next = std::min(_next, get_timestamp(timer));
228 }
229 }
230 return exp;
231 }
232
233 template <typename EnableFunc>
234 void complete(timer_list_t& expired_timers, EnableFunc&& enable_fn) noexcept(noexcept(enable_fn())) {
235 expired_timers = expire(this->now());
236 for (auto& t : expired_timers) {
237 t._expired = true;
238 }
239 const auto prev_sg = current_scheduling_group();
240 while (!expired_timers.empty()) {
241 auto t = &*expired_timers.begin();
242 expired_timers.pop_front();
243 t->_queued = false;
244 if (t->_armed) {
245 t->_armed = false;
246 if (t->_period) {
247 t->readd_periodic();
248 }
249 try {
250 *internal::current_scheduling_group_ptr() = t->_sg;
251 t->_callback();
252 } catch (...) {
253 internal::log_timer_callback_exception(std::current_exception());
254 }
255 }
256 }
257 // complete_timers() can be called from the context of run_tasks()
258 // as well so we need to restore the previous scheduling group (set by run_tasks()).
259 *internal::current_scheduling_group_ptr() = prev_sg;
260 enable_fn();
261 }
262
269 time_point get_next_timeout() const noexcept
270 {
271 return time_point(duration(std::max(_last, _next)));
272 }
273
277 void clear() noexcept
278 {
279 for (int i : bitsets::for_each_set(_non_empty_buckets)) {
280 _buckets[i].clear();
281 }
282 }
283
284 size_t size() const noexcept
285 {
286 size_t res = 0;
287 for (int i : bitsets::for_each_set(_non_empty_buckets)) {
288 res += _buckets[i].size();
289 }
290 return res;
291 }
292
296 bool empty() const noexcept
297 {
298 return _non_empty_buckets.none();
299 }
300
301 time_point now() noexcept {
302 return Timer::clock::now();
303 }
304};
305}
Definition: timer-set.hh:45
bool insert(Timer &timer) noexcept
Definition: timer-set.hh:130
void clear() noexcept
Definition: timer-set.hh:277
time_point get_next_timeout() const noexcept
Definition: timer-set.hh:269
void remove(Timer &timer, timer_list_t &expired) noexcept
Definition: timer-set.hh:169
timer_list_t expire(time_point now) noexcept
Definition: timer-set.hh:193
void remove(Timer &timer) noexcept
Definition: timer-set.hh:156
bool empty() const noexcept
Definition: timer-set.hh:296
Definition: timer.hh:84
time_point get_timeout() const noexcept
Definition: timer.hh:218
bool cancel() noexcept
future now()
Returns a ready future.
Definition: later.hh:35
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
scheduling_group current_scheduling_group() noexcept
Returns the current scheduling group.
Definition: scheduling.hh:405