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// Basic Usage Environment: for a simple, non-scripted, console application
18// Implementation
19
20#include "BasicUsageEnvironment0.hh"
21#include "HandlerSet.hh"
22
23////////// A subclass of DelayQueueEntry,
24////////// used to implement BasicTaskScheduler0::scheduleDelayedTask()
25
26class AlarmHandler: public DelayQueueEntry {
27public:
28 AlarmHandler(TaskFunc* proc, void* clientData, DelayInterval timeToDelay)
29 : DelayQueueEntry(timeToDelay), fProc(proc), fClientData(clientData) {
30 }
31
32private: // redefined virtual functions
33 virtual void handleTimeout() {
34 (*fProc)(fClientData);
35 DelayQueueEntry::handleTimeout();
36 }
37
38private:
39 TaskFunc* fProc;
40 void* fClientData;
41};
42
43
44////////// BasicTaskScheduler0 //////////
45
46BasicTaskScheduler0::BasicTaskScheduler0()
47 : fLastHandledSocketNum(-1), fTriggersAwaitingHandling(0), fLastUsedTriggerMask(1), fLastUsedTriggerNum(MAX_NUM_EVENT_TRIGGERS-1) {
48 fHandlers = new HandlerSet;
49 for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
50 fTriggeredEventHandlers[i] = NULL;
51 fTriggeredEventClientDatas[i] = NULL;
52 }
53}
54
55BasicTaskScheduler0::~BasicTaskScheduler0() {
56 delete fHandlers;
57}
58
59TaskToken BasicTaskScheduler0::scheduleDelayedTask(int64_t microseconds,
60 TaskFunc* proc,
61 void* clientData) {
62 if (microseconds < 0) microseconds = 0;
63 DelayInterval timeToDelay((long)(microseconds/1000000), (long)(microseconds%1000000));
64 AlarmHandler* alarmHandler = new AlarmHandler(proc, clientData, timeToDelay);
65 fDelayQueue.addEntry(alarmHandler);
66
67 return (void*)(alarmHandler->token());
68}
69
70void BasicTaskScheduler0::unscheduleDelayedTask(TaskToken& prevTask) {
71 DelayQueueEntry* alarmHandler = fDelayQueue.removeEntry((intptr_t)prevTask);
72 prevTask = NULL;
73 delete alarmHandler;
74}
75
76void BasicTaskScheduler0::doEventLoop(char volatile* watchVariable) {
77 // Repeatedly loop, handling readble sockets and timed events:
78 while (1) {
79 if (watchVariable != NULL && *watchVariable != 0) break;
80 SingleStep();
81 }
82}
83
84EventTriggerId BasicTaskScheduler0::createEventTrigger(TaskFunc* eventHandlerProc) {
85 unsigned i = fLastUsedTriggerNum;
86 EventTriggerId mask = fLastUsedTriggerMask;
87
88 do {
89 i = (i+1)%MAX_NUM_EVENT_TRIGGERS;
90 mask >>= 1;
91 if (mask == 0) mask = 0x80000000;
92
93 if (fTriggeredEventHandlers[i] == NULL) {
94 // This trigger number is free; use it:
95 fTriggeredEventHandlers[i] = eventHandlerProc;
96 fTriggeredEventClientDatas[i] = NULL; // sanity
97
98 fLastUsedTriggerMask = mask;
99 fLastUsedTriggerNum = i;
100
101 return mask;
102 }
103 } while (i != fLastUsedTriggerNum);
104
105 // All available event triggers are allocated; return 0 instead:
106 return 0;
107}
108
109void BasicTaskScheduler0::deleteEventTrigger(EventTriggerId eventTriggerId) {
110 fTriggersAwaitingHandling &=~ eventTriggerId;
111
112 if (eventTriggerId == fLastUsedTriggerMask) { // common-case optimization:
113 fTriggeredEventHandlers[fLastUsedTriggerNum] = NULL;
114 fTriggeredEventClientDatas[fLastUsedTriggerNum] = NULL;
115 } else {
116 // "eventTriggerId" should have just one bit set.
117 // However, we do the reasonable thing if the user happened to 'or' together two or more "EventTriggerId"s:
118 EventTriggerId mask = 0x80000000;
119 for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
120 if ((eventTriggerId&mask) != 0) {
121 fTriggeredEventHandlers[i] = NULL;
122 fTriggeredEventClientDatas[i] = NULL;
123 }
124 mask >>= 1;
125 }
126 }
127}
128
129void BasicTaskScheduler0::triggerEvent(EventTriggerId eventTriggerId, void* clientData) {
130 // First, record the "clientData". (Note that we allow "eventTriggerId" to be a combination of bits for multiple events.)
131 EventTriggerId mask = 0x80000000;
132 for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
133 if ((eventTriggerId&mask) != 0) {
134 fTriggeredEventClientDatas[i] = clientData;
135 }
136 mask >>= 1;
137 }
138
139 // Then, note this event as being ready to be handled.
140 // (Note that because this function (unlike others in the library) can be called from an external thread, we do this last, to
141 // reduce the risk of a race condition.)
142 fTriggersAwaitingHandling |= eventTriggerId;
143}
144
145
146////////// HandlerSet (etc.) implementation //////////
147
148HandlerDescriptor::HandlerDescriptor(HandlerDescriptor* nextHandler)
149 : conditionSet(0), handlerProc(NULL) {
150 // Link this descriptor into a doubly-linked list:
151 if (nextHandler == this) { // initialization
152 fNextHandler = fPrevHandler = this;
153 } else {
154 fNextHandler = nextHandler;
155 fPrevHandler = nextHandler->fPrevHandler;
156 nextHandler->fPrevHandler = this;
157 fPrevHandler->fNextHandler = this;
158 }
159}
160
161HandlerDescriptor::~HandlerDescriptor() {
162 // Unlink this descriptor from a doubly-linked list:
163 fNextHandler->fPrevHandler = fPrevHandler;
164 fPrevHandler->fNextHandler = fNextHandler;
165}
166
167HandlerSet::HandlerSet()
168 : fHandlers(&fHandlers) {
169 fHandlers.socketNum = -1; // shouldn't ever get looked at, but in case...
170}
171
172HandlerSet::~HandlerSet() {
173 // Delete each handler descriptor:
174 while (fHandlers.fNextHandler != &fHandlers) {
175 delete fHandlers.fNextHandler; // changes fHandlers->fNextHandler
176 }
177}
178
179void HandlerSet
180::assignHandler(int socketNum, int conditionSet, TaskScheduler::BackgroundHandlerProc* handlerProc, void* clientData) {
181 // First, see if there's already a handler for this socket:
182 HandlerDescriptor* handler = lookupHandler(socketNum);
183 if (handler == NULL) { // No existing handler, so create a new descr:
184 handler = new HandlerDescriptor(fHandlers.fNextHandler);
185 handler->socketNum = socketNum;
186 }
187
188 handler->conditionSet = conditionSet;
189 handler->handlerProc = handlerProc;
190 handler->clientData = clientData;
191}
192
193void HandlerSet::clearHandler(int socketNum) {
194 HandlerDescriptor* handler = lookupHandler(socketNum);
195 delete handler;
196}
197
198void HandlerSet::moveHandler(int oldSocketNum, int newSocketNum) {
199 HandlerDescriptor* handler = lookupHandler(oldSocketNum);
200 if (handler != NULL) {
201 handler->socketNum = newSocketNum;
202 }
203}
204
205HandlerDescriptor* HandlerSet::lookupHandler(int socketNum) {
206 HandlerDescriptor* handler;
207 HandlerIterator iter(*this);
208 while ((handler = iter.next()) != NULL) {
209 if (handler->socketNum == socketNum) break;
210 }
211 return handler;
212}
213
214HandlerIterator::HandlerIterator(HandlerSet& handlerSet)
215 : fOurSet(handlerSet) {
216 reset();
217}
218
219HandlerIterator::~HandlerIterator() {
220}
221
222void HandlerIterator::reset() {
223 fNextPtr = fOurSet.fHandlers.fNextHandler;
224}
225
226HandlerDescriptor* HandlerIterator::next() {
227 HandlerDescriptor* result = fNextPtr;
228 if (result == &fOurSet.fHandlers) { // no more
229 result = NULL;
230 } else {
231 fNextPtr = fNextPtr->fNextHandler;
232 }
233
234 return result;
235}
236