Seastar
High performance C++ framework for concurrent servers
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>
17 #ifndef SEASTAR_MODULE
18 #include <boost/intrusive/list.hpp>
19 #include <array>
20 #include <bitset>
21 #include <chrono>
22 #include <limits>
23 #include <memory>
24 #endif
25 
26 namespace seastar {
27 
39 template<typename Timer, boost::intrusive::list_member_hook<> Timer::*link>
40 class timer_set {
41 public:
42  using time_point = typename Timer::time_point;
43  using timer_list_t = boost::intrusive::list<Timer, boost::intrusive::member_hook<Timer, boost::intrusive::list_member_hook<>, link>>;
44 private:
45  using duration = typename Timer::duration;
46  using timestamp_t = typename Timer::duration::rep;
47 
48  static constexpr timestamp_t max_timestamp = std::numeric_limits<timestamp_t>::max();
49  static constexpr int timestamp_bits = std::numeric_limits<timestamp_t>::digits;
50 
51  // The last bucket is reserved for active timers with timeout <= _last.
52  static constexpr int n_buckets = timestamp_bits + 1;
53 
54  std::array<timer_list_t, n_buckets> _buckets;
55  timestamp_t _last;
56  timestamp_t _next;
57 
58  std::bitset<n_buckets> _non_empty_buckets;
59 private:
60  static timestamp_t get_timestamp(time_point _time_point) noexcept
61  {
62  return _time_point.time_since_epoch().count();
63  }
64 
65  static timestamp_t get_timestamp(Timer& timer) noexcept
66  {
67  return get_timestamp(timer.get_timeout());
68  }
69 
70  int get_index(timestamp_t timestamp) const noexcept
71  {
72  if (timestamp <= _last) {
73  return n_buckets - 1;
74  }
75 
76  auto index = bitsets::count_leading_zeros(timestamp ^ _last);
77  assert(index < n_buckets - 1);
78  return index;
79  }
80 
81  int get_index(Timer& timer) const noexcept
82  {
83  return get_index(get_timestamp(timer));
84  }
85 
86  int get_last_non_empty_bucket() const noexcept
87  {
88  return bitsets::get_last_set(_non_empty_buckets);
89  }
90 public:
91  timer_set() noexcept
92  : _last(0)
93  , _next(max_timestamp)
94  , _non_empty_buckets(0)
95  {
96  }
97 
98  ~timer_set() {
99  for (auto&& list : _buckets) {
100  while (!list.empty()) {
101  auto& timer = *list.begin();
102  timer.cancel();
103  }
104  }
105  }
106 
124  bool insert(Timer& timer) noexcept
125  {
126  auto timestamp = get_timestamp(timer);
127  auto index = get_index(timestamp);
128 
129  _buckets[index].push_back(timer);
130  _non_empty_buckets[index] = true;
131 
132  if (timestamp < _next) {
133  _next = timestamp;
134  return true;
135  }
136  return false;
137  }
138 
150  void remove(Timer& timer) noexcept
151  {
152  auto index = get_index(timer);
153  auto& list = _buckets[index];
154  list.erase(list.iterator_to(timer));
155  if (list.empty()) {
156  _non_empty_buckets[index] = false;
157  }
158  }
159 
174  timer_list_t expire(time_point now) noexcept
175  {
176  timer_list_t exp;
177  auto timestamp = get_timestamp(now);
178 
179  if (timestamp < _last) {
180  abort();
181  }
182 
183  auto index = get_index(timestamp);
184 
185  for (int i : bitsets::for_each_set(_non_empty_buckets, index + 1)) {
186  exp.splice(exp.end(), _buckets[i]);
187  _non_empty_buckets[i] = false;
188  }
189 
190  _last = timestamp;
191  _next = max_timestamp;
192 
193  auto& list = _buckets[index];
194  while (!list.empty()) {
195  auto& timer = *list.begin();
196  list.pop_front();
197  if (timer.get_timeout() <= now) {
198  exp.push_back(timer);
199  } else {
200  insert(timer);
201  }
202  }
203 
204  _non_empty_buckets[index] = !list.empty();
205 
206  if (_next == max_timestamp && _non_empty_buckets.any()) {
207  for (auto& timer : _buckets[get_last_non_empty_bucket()]) {
208  _next = std::min(_next, get_timestamp(timer));
209  }
210  }
211  return exp;
212  }
213 
220  time_point get_next_timeout() const noexcept
221  {
222  return time_point(duration(std::max(_last, _next)));
223  }
224 
228  void clear() noexcept
229  {
230  for (int i : bitsets::for_each_set(_non_empty_buckets)) {
231  _buckets[i].clear();
232  }
233  }
234 
235  size_t size() const noexcept
236  {
237  size_t res = 0;
238  for (int i : bitsets::for_each_set(_non_empty_buckets)) {
239  res += _buckets[i].size();
240  }
241  return res;
242  }
243 
247  bool empty() const noexcept
248  {
249  return _non_empty_buckets.none();
250  }
251 
252  time_point now() noexcept {
253  return Timer::clock::now();
254  }
255 };
256 }
Definition: timer-set.hh:40
bool insert(Timer &timer) noexcept
Definition: timer-set.hh:124
void clear() noexcept
Definition: timer-set.hh:228
time_point get_next_timeout() const noexcept
Definition: timer-set.hh:220
timer_list_t expire(time_point now) noexcept
Definition: timer-set.hh:174
void remove(Timer &timer) noexcept
Definition: timer-set.hh:150
bool empty() const noexcept
Definition: timer-set.hh:247
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