proxygen
HHWheelTimer.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2014-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
18 
19 #include <cassert>
20 
21 #include <folly/Memory.h>
22 #include <folly/Optional.h>
23 #include <folly/ScopeGuard.h>
25 #include <folly/io/async/Request.h>
26 #include <folly/lang/Bits.h>
27 
28 using std::chrono::milliseconds;
29 
30 namespace folly {
31 
45 
47  if (isScheduled()) {
48  cancelTimeout();
49  }
50 }
51 
53  HHWheelTimer* wheel,
54  std::chrono::milliseconds timeout) {
55  assert(wheel_ == nullptr);
56  assert(expiration_ == decltype(expiration_){});
57 
58  wheel_ = wheel;
59 
60  expiration_ = getCurTime() + timeout;
61 }
62 
64  if (--wheel_->count_ <= 0) {
65  assert(wheel_->count_ == 0);
66  wheel_->AsyncTimeout::cancelTimeout();
67  }
68  unlink();
69  if ((-1 != bucket_) && (wheel_->buckets_[0][bucket_].empty())) {
70  auto bi = makeBitIterator(wheel_->bitmap_.begin());
71  *(bi + bucket_) = false;
72  }
73 
74  wheel_ = nullptr;
75  expiration_ = {};
76 }
77 
79  folly::TimeoutManager* timeoutMananger,
80  std::chrono::milliseconds intervalMS,
82  std::chrono::milliseconds defaultTimeoutMS)
83  : AsyncTimeout(timeoutMananger, internal),
84  interval_(intervalMS),
85  defaultTimeout_(defaultTimeoutMS),
86  lastTick_(1),
87  expireTick_(1),
88  count_(0),
91  bitmap_.resize((WHEEL_SIZE / sizeof(std::size_t)) / 8, 0);
92 }
93 
95  // Ensure this gets done, but right before destruction finishes.
96  auto destructionPublisherGuard = folly::makeGuard([&] {
97  // Inform the subscriber that this instance is doomed.
100  }
101  });
102  while (!timeouts.empty()) {
103  auto* cb = &timeouts.front();
104  timeouts.pop_front();
105  cb->cancelTimeout();
106  cb->callbackCanceled();
107  }
108  cancelAll();
109 }
110 
112  Callback* callback,
113  std::chrono::milliseconds timeout) {
114  auto nextTick = calcNextTick();
115  int64_t due = timeToWheelTicks(timeout) + nextTick;
116  int64_t diff = due - nextTick;
118 
119  auto bi = makeBitIterator(bitmap_.begin());
120 
121  if (diff < 0) {
122  list = &buckets_[0][nextTick & WHEEL_MASK];
123  *(bi + (nextTick & WHEEL_MASK)) = true;
124  callback->bucket_ = nextTick & WHEEL_MASK;
125  } else if (diff < WHEEL_SIZE) {
126  list = &buckets_[0][due & WHEEL_MASK];
127  *(bi + (due & WHEEL_MASK)) = true;
128  callback->bucket_ = due & WHEEL_MASK;
129  } else if (diff < 1 << (2 * WHEEL_BITS)) {
130  list = &buckets_[1][(due >> WHEEL_BITS) & WHEEL_MASK];
131  } else if (diff < 1 << (3 * WHEEL_BITS)) {
132  list = &buckets_[2][(due >> 2 * WHEEL_BITS) & WHEEL_MASK];
133  } else {
134  /* in largest slot */
135  if (diff > LARGEST_SLOT) {
136  diff = LARGEST_SLOT;
137  due = diff + nextTick;
138  }
139  list = &buckets_[3][(due >> 3 * WHEEL_BITS) & WHEEL_MASK];
140  }
141  list->push_back(*callback);
142 }
143 
145  Callback* callback,
146  std::chrono::milliseconds timeout) {
147  // Cancel the callback if it happens to be scheduled already.
148  callback->cancelTimeout();
149 
151 
152  count_++;
153 
154  callback->setScheduled(this, timeout);
155  scheduleTimeoutImpl(callback, timeout);
156 
157  /* If we're calling callbacks, timer will be reset after all
158  * callbacks are called.
159  */
162  }
163 }
164 
166  CHECK(std::chrono::milliseconds(-1) != defaultTimeout_)
167  << "Default timeout was not initialized";
168  scheduleTimeout(callback, defaultTimeout_);
169 }
170 
171 bool HHWheelTimer::cascadeTimers(int bucket, int tick) {
172  CallbackList cbs;
173  cbs.swap(buckets_[bucket][tick]);
174  while (!cbs.empty()) {
175  auto* cb = &cbs.front();
176  cbs.pop_front();
177  scheduleTimeoutImpl(cb, cb->getTimeRemaining(getCurTime()));
178  }
179 
180  // If tick is zero, timeoutExpired will cascade the next bucket.
181  return tick == 0;
182 }
183 
185  auto nextTick = calcNextTick();
186 
187  // If the last smart pointer for "this" is reset inside the callback's
188  // timeoutExpired(), then the guard will detect that it is time to bail from
189  // this method.
190  auto isDestroyed = false;
191  // If scheduleTimeout is called from a callback in this function, it may
192  // cause inconsistencies in the state of this object. As such, we need
193  // to treat these calls slightly differently.
195  processingCallbacksGuard_ = &isDestroyed;
196  auto reEntryGuard = folly::makeGuard([&] {
197  if (!isDestroyed) {
198  processingCallbacksGuard_ = nullptr;
199  }
200  });
201 
202  // timeoutExpired() can only be invoked directly from the event base loop.
203  // It should never be invoked recursively.
204  //
206  while (lastTick_ < nextTick) {
207  int idx = lastTick_ & WHEEL_MASK;
208 
209  auto bi = makeBitIterator(bitmap_.begin());
210  *(bi + idx) = false;
211 
212  lastTick_++;
213  CallbackList* cbs = &buckets_[0][idx];
214  while (!cbs->empty()) {
215  auto* cb = &cbs->front();
216  cbs->pop_front();
217  timeouts.push_back(*cb);
218  }
219 
220  if (idx == 0) {
221  // Cascade timers
222  if (cascadeTimers(1, (lastTick_ >> WHEEL_BITS) & WHEEL_MASK) &&
223  cascadeTimers(2, (lastTick_ >> (2 * WHEEL_BITS)) & WHEEL_MASK)) {
224  cascadeTimers(3, (lastTick_ >> (3 * WHEEL_BITS)) & WHEEL_MASK);
225  }
226  }
227  }
228 
229  while (!timeouts.empty()) {
230  auto* cb = &timeouts.front();
231  timeouts.pop_front();
232  count_--;
233  cb->wheel_ = nullptr;
234  cb->expiration_ = {};
235  RequestContextScopeGuard rctx(cb->requestContext_);
236  cb->timeoutExpired();
237  if (isDestroyed) {
238  // The HHWheelTimer itself has been destroyed. The other callbacks
239  // will have been cancelled from the destructor. Bail before causing
240  // damage.
241  return;
242  }
243  }
245 }
246 
248  size_t count = 0;
249 
250  if (count_ != 0) {
251  const std::size_t numElements = WHEEL_BUCKETS * WHEEL_SIZE;
252  auto maxBuckets = std::min(numElements, count_);
253  auto buckets = std::make_unique<CallbackList[]>(maxBuckets);
254  size_t countBuckets = 0;
255  for (auto& tick : buckets_) {
256  for (auto& bucket : tick) {
257  if (bucket.empty()) {
258  continue;
259  }
260  count += bucket.size();
261  std::swap(bucket, buckets[countBuckets++]);
262  if (count >= count_) {
263  break;
264  }
265  }
266  }
267 
268  for (size_t i = 0; i < countBuckets; ++i) {
269  auto& bucket = buckets[i];
270  while (!bucket.empty()) {
271  auto& cb = bucket.front();
272  cb.cancelTimeout();
273  cb.callbackCanceled();
274  }
275  }
276  }
277 
278  return count;
279 }
280 
282  auto nextTick = calcNextTick();
283  int64_t tick = 1;
284 
285  if (nextTick & WHEEL_MASK) {
286  auto bi = makeBitIterator(bitmap_.begin());
287  auto bi_end = makeBitIterator(bitmap_.end());
288  auto it = folly::findFirstSet(bi + (nextTick & WHEEL_MASK), bi_end);
289  if (it == bi_end) {
290  tick = WHEEL_SIZE - ((nextTick - 1) & WHEEL_MASK);
291  } else {
292  tick = std::distance(bi + (nextTick & WHEEL_MASK), it) + 1;
293  }
294  }
295 
296  if (count_ > 0) {
297  if (!this->AsyncTimeout::isScheduled() ||
298  (expireTick_ > tick + nextTick - 1)) {
300  expireTick_ = tick + nextTick - 1;
301  }
302  } else {
304  }
305 }
306 
308  auto intervals = (getCurTime() - startTime_) / interval_;
309  // Slow eventbases will have skew between the actual time and the
310  // callback time. To avoid racing the next scheduleNextTimeout()
311  // call, always schedule new timeouts against the actual
312  // timeoutExpired() time.
314  return intervals;
315  } else {
316  return lastTick_;
317  }
318 }
319 
320 } // namespace folly
static constexpr unsigned int WHEEL_MASK
Definition: HHWheelTimer.h:287
void timeoutExpired() noexceptoverride
BitIterator< BaseIter > findFirstSet(BitIterator< BaseIter >, BitIterator< BaseIter >)
Definition: BitIterator.h:170
static constexpr int WHEEL_BUCKETS
Definition: HHWheelTimer.h:284
static constexpr int WHEEL_BITS
Definition: HHWheelTimer.h:285
BitIterator< BaseIter > makeBitIterator(const BaseIter &iter)
Definition: BitIterator.h:161
static constexpr uint32_t LARGEST_SLOT
Definition: HHWheelTimer.h:288
std::vector< std::size_t > bitmap_
Definition: HHWheelTimer.h:292
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
static int DEFAULT_TICK_INTERVAL
Definition: HHWheelTimer.h:163
requires E e noexcept(noexcept(s.error(std::move(e))))
HHWheelTimer(folly::TimeoutManager *timeoutManager, std::chrono::milliseconds intervalMS=std::chrono::milliseconds(DEFAULT_TICK_INTERVAL), AsyncTimeout::InternalEnum internal=AsyncTimeout::InternalEnum::NORMAL, std::chrono::milliseconds defaultTimeoutMS=std::chrono::milliseconds(-1))
#define nullptr
Definition: http_parser.c:41
void scheduleTimeout(Callback *callback, std::chrono::milliseconds timeout)
std::chrono::steady_clock::time_point startTime_
Definition: HHWheelTimer.h:302
CallbackList buckets_[WHEEL_BUCKETS][WHEEL_SIZE]
Definition: HHWheelTimer.h:291
static std::shared_ptr< RequestContext > saveContext()
Definition: Request.h:196
LogLevel min
Definition: LogLevel.cpp:30
std::shared_ptr< RequestContext > requestContext_
Definition: HHWheelTimer.h:147
void setScheduled(HHWheelTimer *wheel, std::chrono::milliseconds)
Encoder::MutableCompressedList list
void scheduleTimeoutImpl(Callback *callback, std::chrono::milliseconds timeout)
static constexpr unsigned int WHEEL_SIZE
Definition: HHWheelTimer.h:286
virtual std::chrono::steady_clock::time_point getCurTime()
Definition: HHWheelTimer.h:121
std::chrono::steady_clock::time_point expiration_
Definition: HHWheelTimer.h:140
bool * processingCallbacksGuard_
Definition: HHWheelTimer.h:308
CallbackList timeouts
Definition: HHWheelTimer.h:309
std::chrono::milliseconds defaultTimeout_
Definition: HHWheelTimer.h:282
FOLLY_NODISCARD detail::ScopeGuardImplDecay< F, true > makeGuard(F &&f) noexcept(noexcept(detail::ScopeGuardImplDecay< F, true >(static_cast< F && >(f))))
Definition: ScopeGuard.h:184
uint64_t diff(uint64_t a, uint64_t b)
Definition: FutexTest.cpp:135
int64_t timeToWheelTicks(std::chrono::milliseconds t)
Definition: HHWheelTimer.h:294
~HHWheelTimer() override
bool scheduleTimeout(uint32_t milliseconds)
bool isScheduled() const
std::size_t count() const
Definition: HHWheelTimer.h:252
Callback::List CallbackList
Definition: HHWheelTimer.h:290
void swap(SwapTrackingAlloc< T > &, SwapTrackingAlloc< T > &)
Definition: F14TestUtil.h:414
bool cascadeTimers(int bucket, int tick)
std::size_t count_
Definition: HHWheelTimer.h:301
std::chrono::milliseconds interval_
Definition: HHWheelTimer.h:281
std::chrono::steady_clock::time_point getCurTime()
Definition: HHWheelTimer.h:311