blob: 0af3b9da3dadade31c20bb36486c01fd4e0161c0 [file] [log] [blame]
/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2020, Live Networks, Inc. All rights reserved
// Help by Carlo Bonamico to get working for Windows
// Delay queue
// Implementation
#include "DelayQueue.hh"
#include "GroupsockHelper.hh"
static const int MILLION = 1000000;
///// Timeval /////
int Timeval::operator>=(const Timeval& arg2) const {
return seconds() > arg2.seconds()
|| (seconds() == arg2.seconds()
&& useconds() >= arg2.useconds());
}
void Timeval::operator+=(const DelayInterval& arg2) {
secs() += arg2.seconds(); usecs() += arg2.useconds();
if (useconds() >= MILLION) {
usecs() -= MILLION;
++secs();
}
}
void Timeval::operator-=(const DelayInterval& arg2) {
secs() -= arg2.seconds(); usecs() -= arg2.useconds();
if ((int)useconds() < 0) {
usecs() += MILLION;
--secs();
}
if ((int)seconds() < 0)
secs() = usecs() = 0;
}
DelayInterval operator-(const Timeval& arg1, const Timeval& arg2) {
time_base_seconds secs = arg1.seconds() - arg2.seconds();
time_base_seconds usecs = arg1.useconds() - arg2.useconds();
if ((int)usecs < 0) {
usecs += MILLION;
--secs;
}
if ((int)secs < 0)
return DELAY_ZERO;
else
return DelayInterval(secs, usecs);
}
///// DelayInterval /////
DelayInterval operator*(short arg1, const DelayInterval& arg2) {
time_base_seconds result_seconds = arg1*arg2.seconds();
time_base_seconds result_useconds = arg1*arg2.useconds();
time_base_seconds carry = result_useconds/MILLION;
result_useconds -= carry*MILLION;
result_seconds += carry;
return DelayInterval(result_seconds, result_useconds);
}
#ifndef INT_MAX
#define INT_MAX 0x7FFFFFFF
#endif
const DelayInterval DELAY_ZERO(0, 0);
const DelayInterval DELAY_SECOND(1, 0);
const DelayInterval DELAY_MINUTE = 60*DELAY_SECOND;
const DelayInterval DELAY_HOUR = 60*DELAY_MINUTE;
const DelayInterval DELAY_DAY = 24*DELAY_HOUR;
const DelayInterval ETERNITY(INT_MAX, MILLION-1);
// used internally to make the implementation work
///// DelayQueueEntry /////
intptr_t DelayQueueEntry::tokenCounter = 0;
DelayQueueEntry::DelayQueueEntry(DelayInterval delay)
: fDeltaTimeRemaining(delay) {
fNext = fPrev = this;
fToken = ++tokenCounter;
}
DelayQueueEntry::~DelayQueueEntry() {
}
void DelayQueueEntry::handleTimeout() {
delete this;
}
///// DelayQueue /////
DelayQueue::DelayQueue()
: DelayQueueEntry(ETERNITY) {
fLastSyncTime = TimeNow();
}
DelayQueue::~DelayQueue() {
while (fNext != this) {
DelayQueueEntry* entryToRemove = fNext;
removeEntry(entryToRemove);
delete entryToRemove;
}
}
void DelayQueue::addEntry(DelayQueueEntry* newEntry) {
synchronize();
DelayQueueEntry* cur = head();
while (newEntry->fDeltaTimeRemaining >= cur->fDeltaTimeRemaining) {
newEntry->fDeltaTimeRemaining -= cur->fDeltaTimeRemaining;
cur = cur->fNext;
}
cur->fDeltaTimeRemaining -= newEntry->fDeltaTimeRemaining;
// Add "newEntry" to the queue, just before "cur":
newEntry->fNext = cur;
newEntry->fPrev = cur->fPrev;
cur->fPrev = newEntry->fPrev->fNext = newEntry;
}
void DelayQueue::updateEntry(DelayQueueEntry* entry, DelayInterval newDelay) {
if (entry == NULL) return;
removeEntry(entry);
entry->fDeltaTimeRemaining = newDelay;
addEntry(entry);
}
void DelayQueue::updateEntry(intptr_t tokenToFind, DelayInterval newDelay) {
DelayQueueEntry* entry = findEntryByToken(tokenToFind);
updateEntry(entry, newDelay);
}
void DelayQueue::removeEntry(DelayQueueEntry* entry) {
if (entry == NULL || entry->fNext == NULL) return;
entry->fNext->fDeltaTimeRemaining += entry->fDeltaTimeRemaining;
entry->fPrev->fNext = entry->fNext;
entry->fNext->fPrev = entry->fPrev;
entry->fNext = entry->fPrev = NULL;
// in case we should try to remove it again
}
DelayQueueEntry* DelayQueue::removeEntry(intptr_t tokenToFind) {
DelayQueueEntry* entry = findEntryByToken(tokenToFind);
removeEntry(entry);
return entry;
}
DelayInterval const& DelayQueue::timeToNextAlarm() {
if (head()->fDeltaTimeRemaining == DELAY_ZERO) return DELAY_ZERO; // a common case
synchronize();
return head()->fDeltaTimeRemaining;
}
void DelayQueue::handleAlarm() {
if (head()->fDeltaTimeRemaining != DELAY_ZERO) synchronize();
if (head()->fDeltaTimeRemaining == DELAY_ZERO) {
// This event is due to be handled:
DelayQueueEntry* toRemove = head();
removeEntry(toRemove); // do this first, in case handler accesses queue
toRemove->handleTimeout();
}
}
DelayQueueEntry* DelayQueue::findEntryByToken(intptr_t tokenToFind) {
DelayQueueEntry* cur = head();
while (cur != this) {
if (cur->token() == tokenToFind) return cur;
cur = cur->fNext;
}
return NULL;
}
void DelayQueue::synchronize() {
// First, figure out how much time has elapsed since the last sync:
_EventTime timeNow = TimeNow();
if (timeNow < fLastSyncTime) {
// The system clock has apparently gone back in time; reset our sync time and return:
fLastSyncTime = timeNow;
return;
}
DelayInterval timeSinceLastSync = timeNow - fLastSyncTime;
fLastSyncTime = timeNow;
// Then, adjust the delay queue for any entries whose time is up:
DelayQueueEntry* curEntry = head();
while (timeSinceLastSync >= curEntry->fDeltaTimeRemaining) {
timeSinceLastSync -= curEntry->fDeltaTimeRemaining;
curEntry->fDeltaTimeRemaining = DELAY_ZERO;
curEntry = curEntry->fNext;
}
curEntry->fDeltaTimeRemaining -= timeSinceLastSync;
}
///// _EventTime /////
_EventTime TimeNow() {
struct timeval tvNow;
gettimeofday(&tvNow, NULL);
return _EventTime(tvNow.tv_sec, tvNow.tv_usec);
}
const _EventTime THE_END_OF_TIME(INT_MAX);