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 | /** |
18 | * Simple timeout queue. Call user-specified callbacks when their timeouts |
19 | * expire. |
20 | * |
21 | * This class assumes that "time" is an int64_t and doesn't care about time |
22 | * units (seconds, milliseconds, etc). You call runOnce() / runLoop() using |
23 | * the same time units that you use to specify callbacks. |
24 | * |
25 | * @author Tudor Bosman (tudorb@fb.com) |
26 | */ |
27 | |
28 | #pragma once |
29 | |
30 | #include <cstdint> |
31 | #include <functional> |
32 | |
33 | #include <boost/multi_index/indexed_by.hpp> |
34 | #include <boost/multi_index/member.hpp> |
35 | #include <boost/multi_index/ordered_index.hpp> |
36 | #include <boost/multi_index_container.hpp> |
37 | |
38 | namespace folly { |
39 | |
40 | class TimeoutQueue { |
41 | public: |
42 | typedef int64_t Id; |
43 | typedef std::function<void(Id, int64_t)> Callback; |
44 | |
45 | TimeoutQueue() : nextId_(1) {} |
46 | |
47 | /** |
48 | * Add a one-time timeout event that will fire "delay" time units from "now" |
49 | * (that is, the first time that run*() is called with a time value >= now |
50 | * + delay). |
51 | */ |
52 | Id add(int64_t now, int64_t delay, Callback callback); |
53 | |
54 | /** |
55 | * Add a repeating timeout event that will fire every "interval" time units |
56 | * (it will first fire when run*() is called with a time value >= |
57 | * now + interval). |
58 | * |
59 | * run*() will always invoke each repeating event at most once, even if |
60 | * more than one "interval" period has passed. |
61 | */ |
62 | Id addRepeating(int64_t now, int64_t interval, Callback callback); |
63 | |
64 | /** |
65 | * Erase a given timeout event, returns true if the event was actually |
66 | * erased and false if it didn't exist in our queue. |
67 | */ |
68 | bool erase(Id id); |
69 | |
70 | /** |
71 | * Process all events that are due at times <= "now" by calling their |
72 | * callbacks. |
73 | * |
74 | * Callbacks are allowed to call back into the queue and add / erase events; |
75 | * they might create more events that are already due. In this case, |
76 | * runOnce() will only go through the queue once, and return a "next |
77 | * expiration" time in the past or present (<= now); runLoop() |
78 | * will process the queue again, until there are no events already due. |
79 | * |
80 | * Note that it is then possible for runLoop to never return if |
81 | * callbacks re-add themselves to the queue (or if you have repeating |
82 | * callbacks with an interval of 0). |
83 | * |
84 | * Return the time that the next event will be due (same as |
85 | * nextExpiration(), below) |
86 | */ |
87 | int64_t runOnce(int64_t now) { |
88 | return runInternal(now, true); |
89 | } |
90 | int64_t runLoop(int64_t now) { |
91 | return runInternal(now, false); |
92 | } |
93 | |
94 | /** |
95 | * Return the time that the next event will be due. |
96 | */ |
97 | int64_t nextExpiration() const; |
98 | |
99 | private: |
100 | int64_t runInternal(int64_t now, bool runOnce); |
101 | // noncopyable |
102 | TimeoutQueue(const TimeoutQueue&) = delete; |
103 | TimeoutQueue& operator=(const TimeoutQueue&) = delete; |
104 | |
105 | struct Event { |
106 | Id id; |
107 | int64_t expiration; |
108 | int64_t repeatInterval; |
109 | Callback callback; |
110 | }; |
111 | |
112 | typedef boost::multi_index_container< |
113 | Event, |
114 | boost::multi_index::indexed_by< |
115 | boost::multi_index::ordered_unique< |
116 | boost::multi_index::member<Event, Id, &Event::id>>, |
117 | boost::multi_index::ordered_non_unique< |
118 | boost::multi_index::member<Event, int64_t, &Event::expiration>>>> |
119 | Set; |
120 | |
121 | enum { |
122 | BY_ID = 0, |
123 | BY_EXPIRATION = 1, |
124 | }; |
125 | |
126 | Set timeouts_; |
127 | Id nextId_; |
128 | }; |
129 | |
130 | } // namespace folly |
131 | |