240 lines
7.6 KiB
C++
Executable File
240 lines
7.6 KiB
C++
Executable File
/**********
|
|
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 2.1 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-2016 Live Networks, Inc. All rights reserved.
|
|
// Basic Usage Environment: for a simple, non-scripted, console application
|
|
// Implementation
|
|
|
|
#include "BasicUsageEnvironment0.hh"
|
|
#include "HandlerSet.hh"
|
|
|
|
////////// A subclass of DelayQueueEntry,
|
|
////////// used to implement BasicTaskScheduler0::scheduleDelayedTask()
|
|
|
|
class AlarmHandler: public DelayQueueEntry {
|
|
public:
|
|
AlarmHandler(TaskFunc* proc, void* clientData, DelayInterval timeToDelay)
|
|
: DelayQueueEntry(timeToDelay), fProc(proc), fClientData(clientData) {
|
|
}
|
|
|
|
private: // redefined virtual functions
|
|
virtual void handleTimeout() {
|
|
(*fProc)(fClientData);
|
|
DelayQueueEntry::handleTimeout();
|
|
}
|
|
|
|
private:
|
|
TaskFunc* fProc;
|
|
void* fClientData;
|
|
};
|
|
|
|
|
|
////////// BasicTaskScheduler0 //////////
|
|
|
|
BasicTaskScheduler0::BasicTaskScheduler0()
|
|
: fLastHandledSocketNum(-1), fTriggersAwaitingHandling(0), fLastUsedTriggerMask(1), fLastUsedTriggerNum(MAX_NUM_EVENT_TRIGGERS-1) {
|
|
fHandlers = new HandlerSet;
|
|
for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
|
|
fTriggeredEventHandlers[i] = NULL;
|
|
fTriggeredEventClientDatas[i] = NULL;
|
|
}
|
|
}
|
|
|
|
BasicTaskScheduler0::~BasicTaskScheduler0() {
|
|
delete fHandlers;
|
|
}
|
|
|
|
TaskToken BasicTaskScheduler0::scheduleDelayedTask(int64_t microseconds,
|
|
TaskFunc* proc,
|
|
void* clientData) {
|
|
if (microseconds < 0) microseconds = 0;
|
|
DelayInterval timeToDelay((long)(microseconds/1000000), (long)(microseconds%1000000));
|
|
AlarmHandler* alarmHandler = new AlarmHandler(proc, clientData, timeToDelay);
|
|
fDelayQueue.addEntry(alarmHandler);
|
|
|
|
return (void*)(alarmHandler->token());
|
|
}
|
|
|
|
void BasicTaskScheduler0::unscheduleDelayedTask(TaskToken& prevTask) {
|
|
if (prevTask == NULL) {
|
|
return;
|
|
}
|
|
|
|
DelayQueueEntry* alarmHandler = fDelayQueue.removeEntry((intptr_t)prevTask);
|
|
prevTask = NULL;
|
|
delete alarmHandler;
|
|
}
|
|
|
|
void BasicTaskScheduler0::doEventLoop(char volatile* watchVariable) {
|
|
// Repeatedly loop, handling readble sockets and timed events:
|
|
while (1) {
|
|
if (watchVariable != NULL && *watchVariable != 0) break;
|
|
SingleStep();
|
|
}
|
|
}
|
|
|
|
EventTriggerId BasicTaskScheduler0::createEventTrigger(TaskFunc* eventHandlerProc) {
|
|
unsigned i = fLastUsedTriggerNum;
|
|
EventTriggerId mask = fLastUsedTriggerMask;
|
|
|
|
do {
|
|
i = (i+1)%MAX_NUM_EVENT_TRIGGERS;
|
|
mask >>= 1;
|
|
if (mask == 0) mask = 0x80000000;
|
|
|
|
if (fTriggeredEventHandlers[i] == NULL) {
|
|
// This trigger number is free; use it:
|
|
fTriggeredEventHandlers[i] = eventHandlerProc;
|
|
fTriggeredEventClientDatas[i] = NULL; // sanity
|
|
|
|
fLastUsedTriggerMask = mask;
|
|
fLastUsedTriggerNum = i;
|
|
|
|
return mask;
|
|
}
|
|
} while (i != fLastUsedTriggerNum);
|
|
|
|
// All available event triggers are allocated; return 0 instead:
|
|
return 0;
|
|
}
|
|
|
|
void BasicTaskScheduler0::deleteEventTrigger(EventTriggerId eventTriggerId) {
|
|
fTriggersAwaitingHandling &=~ eventTriggerId;
|
|
|
|
if (eventTriggerId == fLastUsedTriggerMask) { // common-case optimization:
|
|
fTriggeredEventHandlers[fLastUsedTriggerNum] = NULL;
|
|
fTriggeredEventClientDatas[fLastUsedTriggerNum] = NULL;
|
|
} else {
|
|
// "eventTriggerId" should have just one bit set.
|
|
// However, we do the reasonable thing if the user happened to 'or' together two or more "EventTriggerId"s:
|
|
EventTriggerId mask = 0x80000000;
|
|
for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
|
|
if ((eventTriggerId&mask) != 0) {
|
|
fTriggeredEventHandlers[i] = NULL;
|
|
fTriggeredEventClientDatas[i] = NULL;
|
|
}
|
|
mask >>= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BasicTaskScheduler0::triggerEvent(EventTriggerId eventTriggerId, void* clientData) {
|
|
// First, record the "clientData". (Note that we allow "eventTriggerId" to be a combination of bits for multiple events.)
|
|
EventTriggerId mask = 0x80000000;
|
|
for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
|
|
if ((eventTriggerId&mask) != 0) {
|
|
fTriggeredEventClientDatas[i] = clientData;
|
|
}
|
|
mask >>= 1;
|
|
}
|
|
|
|
// Then, note this event as being ready to be handled.
|
|
// (Note that because this function (unlike others in the library) can be called from an external thread, we do this last, to
|
|
// reduce the risk of a race condition.)
|
|
fTriggersAwaitingHandling |= eventTriggerId;
|
|
}
|
|
|
|
|
|
////////// HandlerSet (etc.) implementation //////////
|
|
|
|
HandlerDescriptor::HandlerDescriptor(HandlerDescriptor* nextHandler)
|
|
: conditionSet(0), handlerProc(NULL) {
|
|
// Link this descriptor into a doubly-linked list:
|
|
if (nextHandler == this) { // initialization
|
|
fNextHandler = fPrevHandler = this;
|
|
} else {
|
|
fNextHandler = nextHandler;
|
|
fPrevHandler = nextHandler->fPrevHandler;
|
|
nextHandler->fPrevHandler = this;
|
|
fPrevHandler->fNextHandler = this;
|
|
}
|
|
}
|
|
|
|
HandlerDescriptor::~HandlerDescriptor() {
|
|
// Unlink this descriptor from a doubly-linked list:
|
|
fNextHandler->fPrevHandler = fPrevHandler;
|
|
fPrevHandler->fNextHandler = fNextHandler;
|
|
}
|
|
|
|
HandlerSet::HandlerSet()
|
|
: fHandlers(&fHandlers) {
|
|
fHandlers.socketNum = -1; // shouldn't ever get looked at, but in case...
|
|
}
|
|
|
|
HandlerSet::~HandlerSet() {
|
|
// Delete each handler descriptor:
|
|
while (fHandlers.fNextHandler != &fHandlers) {
|
|
delete fHandlers.fNextHandler; // changes fHandlers->fNextHandler
|
|
}
|
|
}
|
|
|
|
void HandlerSet
|
|
::assignHandler(int socketNum, int conditionSet, TaskScheduler::BackgroundHandlerProc* handlerProc, void* clientData) {
|
|
// First, see if there's already a handler for this socket:
|
|
HandlerDescriptor* handler = lookupHandler(socketNum);
|
|
if (handler == NULL) { // No existing handler, so create a new descr:
|
|
handler = new HandlerDescriptor(fHandlers.fNextHandler);
|
|
handler->socketNum = socketNum;
|
|
}
|
|
|
|
handler->conditionSet = conditionSet;
|
|
handler->handlerProc = handlerProc;
|
|
handler->clientData = clientData;
|
|
}
|
|
|
|
void HandlerSet::clearHandler(int socketNum) {
|
|
HandlerDescriptor* handler = lookupHandler(socketNum);
|
|
delete handler;
|
|
}
|
|
|
|
void HandlerSet::moveHandler(int oldSocketNum, int newSocketNum) {
|
|
HandlerDescriptor* handler = lookupHandler(oldSocketNum);
|
|
if (handler != NULL) {
|
|
handler->socketNum = newSocketNum;
|
|
}
|
|
}
|
|
|
|
HandlerDescriptor* HandlerSet::lookupHandler(int socketNum) {
|
|
HandlerDescriptor* handler;
|
|
HandlerIterator iter(*this);
|
|
while ((handler = iter.next()) != NULL) {
|
|
if (handler->socketNum == socketNum) break;
|
|
}
|
|
return handler;
|
|
}
|
|
|
|
HandlerIterator::HandlerIterator(HandlerSet& handlerSet)
|
|
: fOurSet(handlerSet) {
|
|
reset();
|
|
}
|
|
|
|
HandlerIterator::~HandlerIterator() {
|
|
}
|
|
|
|
void HandlerIterator::reset() {
|
|
fNextPtr = fOurSet.fHandlers.fNextHandler;
|
|
}
|
|
|
|
HandlerDescriptor* HandlerIterator::next() {
|
|
HandlerDescriptor* result = fNextPtr;
|
|
if (result == &fOurSet.fHandlers) { // no more
|
|
result = NULL;
|
|
} else {
|
|
fNextPtr = fNextPtr->fNextHandler;
|
|
}
|
|
|
|
return result;
|
|
}
|