1 | /* |
2 | * Copyright 2011-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 | |
17 | #include <folly/TimeoutQueue.h> |
18 | #include <algorithm> |
19 | #include <vector> |
20 | |
21 | namespace folly { |
22 | |
23 | TimeoutQueue::Id |
24 | TimeoutQueue::add(int64_t now, int64_t delay, Callback callback) { |
25 | Id id = nextId_++; |
26 | timeouts_.insert({id, now + delay, -1, std::move(callback)}); |
27 | return id; |
28 | } |
29 | |
30 | TimeoutQueue::Id |
31 | TimeoutQueue::addRepeating(int64_t now, int64_t interval, Callback callback) { |
32 | Id id = nextId_++; |
33 | timeouts_.insert({id, now + interval, interval, std::move(callback)}); |
34 | return id; |
35 | } |
36 | |
37 | int64_t TimeoutQueue::nextExpiration() const { |
38 | return ( |
39 | timeouts_.empty() ? std::numeric_limits<int64_t>::max() |
40 | : timeouts_.get<BY_EXPIRATION>().begin()->expiration); |
41 | } |
42 | |
43 | bool TimeoutQueue::erase(Id id) { |
44 | return timeouts_.get<BY_ID>().erase(id); |
45 | } |
46 | |
47 | int64_t TimeoutQueue::runInternal(int64_t now, bool onceOnly) { |
48 | auto& byExpiration = timeouts_.get<BY_EXPIRATION>(); |
49 | int64_t nextExp; |
50 | do { |
51 | const auto end = byExpiration.upper_bound(now); |
52 | std::vector<Event> expired; |
53 | std::move(byExpiration.begin(), end, std::back_inserter(expired)); |
54 | byExpiration.erase(byExpiration.begin(), end); |
55 | for (const auto& event : expired) { |
56 | // Reinsert if repeating, do this before executing callbacks |
57 | // so the callbacks have a chance to call erase |
58 | if (event.repeatInterval >= 0) { |
59 | timeouts_.insert({event.id, |
60 | now + event.repeatInterval, |
61 | event.repeatInterval, |
62 | event.callback}); |
63 | } |
64 | } |
65 | |
66 | // Call callbacks |
67 | for (const auto& event : expired) { |
68 | event.callback(event.id, now); |
69 | } |
70 | nextExp = nextExpiration(); |
71 | } while (!onceOnly && nextExp <= now); |
72 | return nextExp; |
73 | } |
74 | |
75 | } // namespace folly |
76 | |