1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 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
24static const int MILLION = 1000000;
25
26///// Timeval /////
27
28int Timeval::operator>=(const Timeval& arg2) const {
29 return seconds() > arg2.seconds()
30 || (seconds() == arg2.seconds()
31 && useconds() >= arg2.useconds());
32}
33
34void Timeval::operator+=(const DelayInterval& arg2) {
35 secs() += arg2.seconds(); usecs() += arg2.useconds();
36 if (useconds() >= MILLION) {
37 usecs() -= MILLION;
38 ++secs();
39 }
40}
41
42void 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
53DelayInterval 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
70DelayInterval 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
84const DelayInterval DELAY_ZERO(0, 0);
85const DelayInterval DELAY_SECOND(1, 0);
86const DelayInterval DELAY_MINUTE = 60*DELAY_SECOND;
87const DelayInterval DELAY_HOUR = 60*DELAY_MINUTE;
88const DelayInterval DELAY_DAY = 24*DELAY_HOUR;
89const DelayInterval ETERNITY(INT_MAX, MILLION-1);
90// used internally to make the implementation work
91
92
93///// DelayQueueEntry /////
94
95intptr_t DelayQueueEntry::tokenCounter = 0;
96
97DelayQueueEntry::DelayQueueEntry(DelayInterval delay)
98 : fDeltaTimeRemaining(delay) {
99 fNext = fPrev = this;
100 fToken = ++tokenCounter;
101}
102
103DelayQueueEntry::~DelayQueueEntry() {
104}
105
106void DelayQueueEntry::handleTimeout() {
107 delete this;
108}
109
110
111///// DelayQueue /////
112
113DelayQueue::DelayQueue()
114 : DelayQueueEntry(ETERNITY) {
115 fLastSyncTime = TimeNow();
116}
117
118DelayQueue::~DelayQueue() {
119 while (fNext != this) {
120 DelayQueueEntry* entryToRemove = fNext;
121 removeEntry(entryToRemove);
122 delete entryToRemove;
123 }
124}
125
126void 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
143void DelayQueue::updateEntry(DelayQueueEntry* entry, DelayInterval newDelay) {
144 if (entry == NULL) return;
145
146 removeEntry(entry);
147 entry->fDeltaTimeRemaining = newDelay;
148 addEntry(entry);
149}
150
151void DelayQueue::updateEntry(intptr_t tokenToFind, DelayInterval newDelay) {
152 DelayQueueEntry* entry = findEntryByToken(tokenToFind);
153 updateEntry(entry, newDelay);
154}
155
156void 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
166DelayQueueEntry* DelayQueue::removeEntry(intptr_t tokenToFind) {
167 DelayQueueEntry* entry = findEntryByToken(tokenToFind);
168 removeEntry(entry);
169 return entry;
170}
171
172DelayInterval const& DelayQueue::timeToNextAlarm() {
173 if (head()->fDeltaTimeRemaining == DELAY_ZERO) return DELAY_ZERO; // a common case
174
175 synchronize();
176 return head()->fDeltaTimeRemaining;
177}
178
179void 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
191DelayQueueEntry* 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
201void 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
225_EventTime TimeNow() {
226 struct timeval tvNow;
227
228 gettimeofday(&tvNow, NULL);
229
230 return _EventTime(tvNow.tv_sec, tvNow.tv_usec);
231}
232
233const _EventTime THE_END_OF_TIME(INT_MAX);
234