1 /**********
2 This library is free software; you can redistribute it and/or modify it under
3 the terms of the GNU Lesser General Public License as published by the
4 Free Software Foundation; either version 3 of the License, or (at your
5 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7 This library is distributed in the hope that it will be useful, but WITHOUT
8 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10 more details.
11
12 You should have received a copy of the GNU Lesser General Public License
13 along with this library; if not, write to the Free Software Foundation, Inc.,
14 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15 **********/
16 // Copyright (c) 1996-2020, Live Networks, Inc. All rights reserved
17 // Help by Carlo Bonamico to get working for Windows
18 // Delay queue
19 // Implementation
20
21 #include "DelayQueue.hh"
22 #include "GroupsockHelper.hh"
23
24 static const int MILLION = 1000000;
25
26 ///// Timeval /////
27
operator >=(const Timeval & arg2) const28 int Timeval::operator>=(const Timeval& arg2) const {
29 return seconds() > arg2.seconds()
30 || (seconds() == arg2.seconds()
31 && useconds() >= arg2.useconds());
32 }
33
operator +=(const DelayInterval & arg2)34 void Timeval::operator+=(const DelayInterval& arg2) {
35 secs() += arg2.seconds(); usecs() += arg2.useconds();
36 if (useconds() >= MILLION) {
37 usecs() -= MILLION;
38 ++secs();
39 }
40 }
41
operator -=(const DelayInterval & arg2)42 void Timeval::operator-=(const DelayInterval& arg2) {
43 secs() -= arg2.seconds(); usecs() -= arg2.useconds();
44 if ((int)useconds() < 0) {
45 usecs() += MILLION;
46 --secs();
47 }
48 if ((int)seconds() < 0)
49 secs() = usecs() = 0;
50
51 }
52
operator -(const Timeval & arg1,const Timeval & arg2)53 DelayInterval operator-(const Timeval& arg1, const Timeval& arg2) {
54 time_base_seconds secs = arg1.seconds() - arg2.seconds();
55 time_base_seconds usecs = arg1.useconds() - arg2.useconds();
56
57 if ((int)usecs < 0) {
58 usecs += MILLION;
59 --secs;
60 }
61 if ((int)secs < 0)
62 return DELAY_ZERO;
63 else
64 return DelayInterval(secs, usecs);
65 }
66
67
68 ///// DelayInterval /////
69
operator *(short arg1,const DelayInterval & arg2)70 DelayInterval operator*(short arg1, const DelayInterval& arg2) {
71 time_base_seconds result_seconds = arg1*arg2.seconds();
72 time_base_seconds result_useconds = arg1*arg2.useconds();
73
74 time_base_seconds carry = result_useconds/MILLION;
75 result_useconds -= carry*MILLION;
76 result_seconds += carry;
77
78 return DelayInterval(result_seconds, result_useconds);
79 }
80
81 #ifndef INT_MAX
82 #define INT_MAX 0x7FFFFFFF
83 #endif
84 const DelayInterval DELAY_ZERO(0, 0);
85 const DelayInterval DELAY_SECOND(1, 0);
86 const DelayInterval DELAY_MINUTE = 60*DELAY_SECOND;
87 const DelayInterval DELAY_HOUR = 60*DELAY_MINUTE;
88 const DelayInterval DELAY_DAY = 24*DELAY_HOUR;
89 const DelayInterval ETERNITY(INT_MAX, MILLION-1);
90 // used internally to make the implementation work
91
92
93 ///// DelayQueueEntry /////
94
95 intptr_t DelayQueueEntry::tokenCounter = 0;
96
DelayQueueEntry(DelayInterval delay)97 DelayQueueEntry::DelayQueueEntry(DelayInterval delay)
98 : fDeltaTimeRemaining(delay) {
99 fNext = fPrev = this;
100 fToken = ++tokenCounter;
101 }
102
~DelayQueueEntry()103 DelayQueueEntry::~DelayQueueEntry() {
104 }
105
handleTimeout()106 void DelayQueueEntry::handleTimeout() {
107 delete this;
108 }
109
110
111 ///// DelayQueue /////
112
DelayQueue()113 DelayQueue::DelayQueue()
114 : DelayQueueEntry(ETERNITY) {
115 fLastSyncTime = TimeNow();
116 }
117
~DelayQueue()118 DelayQueue::~DelayQueue() {
119 while (fNext != this) {
120 DelayQueueEntry* entryToRemove = fNext;
121 removeEntry(entryToRemove);
122 delete entryToRemove;
123 }
124 }
125
addEntry(DelayQueueEntry * newEntry)126 void DelayQueue::addEntry(DelayQueueEntry* newEntry) {
127 synchronize();
128
129 DelayQueueEntry* cur = head();
130 while (newEntry->fDeltaTimeRemaining >= cur->fDeltaTimeRemaining) {
131 newEntry->fDeltaTimeRemaining -= cur->fDeltaTimeRemaining;
132 cur = cur->fNext;
133 }
134
135 cur->fDeltaTimeRemaining -= newEntry->fDeltaTimeRemaining;
136
137 // Add "newEntry" to the queue, just before "cur":
138 newEntry->fNext = cur;
139 newEntry->fPrev = cur->fPrev;
140 cur->fPrev = newEntry->fPrev->fNext = newEntry;
141 }
142
updateEntry(DelayQueueEntry * entry,DelayInterval newDelay)143 void DelayQueue::updateEntry(DelayQueueEntry* entry, DelayInterval newDelay) {
144 if (entry == NULL) return;
145
146 removeEntry(entry);
147 entry->fDeltaTimeRemaining = newDelay;
148 addEntry(entry);
149 }
150
updateEntry(intptr_t tokenToFind,DelayInterval newDelay)151 void DelayQueue::updateEntry(intptr_t tokenToFind, DelayInterval newDelay) {
152 DelayQueueEntry* entry = findEntryByToken(tokenToFind);
153 updateEntry(entry, newDelay);
154 }
155
removeEntry(DelayQueueEntry * entry)156 void DelayQueue::removeEntry(DelayQueueEntry* entry) {
157 if (entry == NULL || entry->fNext == NULL) return;
158
159 entry->fNext->fDeltaTimeRemaining += entry->fDeltaTimeRemaining;
160 entry->fPrev->fNext = entry->fNext;
161 entry->fNext->fPrev = entry->fPrev;
162 entry->fNext = entry->fPrev = NULL;
163 // in case we should try to remove it again
164 }
165
removeEntry(intptr_t tokenToFind)166 DelayQueueEntry* DelayQueue::removeEntry(intptr_t tokenToFind) {
167 DelayQueueEntry* entry = findEntryByToken(tokenToFind);
168 removeEntry(entry);
169 return entry;
170 }
171
timeToNextAlarm()172 DelayInterval const& DelayQueue::timeToNextAlarm() {
173 if (head()->fDeltaTimeRemaining == DELAY_ZERO) return DELAY_ZERO; // a common case
174
175 synchronize();
176 return head()->fDeltaTimeRemaining;
177 }
178
handleAlarm()179 void DelayQueue::handleAlarm() {
180 if (head()->fDeltaTimeRemaining != DELAY_ZERO) synchronize();
181
182 if (head()->fDeltaTimeRemaining == DELAY_ZERO) {
183 // This event is due to be handled:
184 DelayQueueEntry* toRemove = head();
185 removeEntry(toRemove); // do this first, in case handler accesses queue
186
187 toRemove->handleTimeout();
188 }
189 }
190
findEntryByToken(intptr_t tokenToFind)191 DelayQueueEntry* DelayQueue::findEntryByToken(intptr_t tokenToFind) {
192 DelayQueueEntry* cur = head();
193 while (cur != this) {
194 if (cur->token() == tokenToFind) return cur;
195 cur = cur->fNext;
196 }
197
198 return NULL;
199 }
200
synchronize()201 void DelayQueue::synchronize() {
202 // First, figure out how much time has elapsed since the last sync:
203 _EventTime timeNow = TimeNow();
204 if (timeNow < fLastSyncTime) {
205 // The system clock has apparently gone back in time; reset our sync time and return:
206 fLastSyncTime = timeNow;
207 return;
208 }
209 DelayInterval timeSinceLastSync = timeNow - fLastSyncTime;
210 fLastSyncTime = timeNow;
211
212 // Then, adjust the delay queue for any entries whose time is up:
213 DelayQueueEntry* curEntry = head();
214 while (timeSinceLastSync >= curEntry->fDeltaTimeRemaining) {
215 timeSinceLastSync -= curEntry->fDeltaTimeRemaining;
216 curEntry->fDeltaTimeRemaining = DELAY_ZERO;
217 curEntry = curEntry->fNext;
218 }
219 curEntry->fDeltaTimeRemaining -= timeSinceLastSync;
220 }
221
222
223 ///// _EventTime /////
224
TimeNow()225 _EventTime TimeNow() {
226 struct timeval tvNow;
227
228 gettimeofday(&tvNow, NULL);
229
230 return _EventTime(tvNow.tv_sec, tvNow.tv_usec);
231 }
232
233 const _EventTime THE_END_OF_TIME(INT_MAX);
234