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 | // Basic Usage Environment: for a simple, non-scripted, console application |
18 | // Implementation |
19 | |
20 | |
21 | #include "BasicUsageEnvironment.hh" |
22 | #include "HandlerSet.hh" |
23 | #include <stdio.h> |
24 | #if defined(_QNX4) |
25 | #include <sys/select.h> |
26 | #include <unix.h> |
27 | #endif |
28 | |
29 | ////////// BasicTaskScheduler ////////// |
30 | |
31 | BasicTaskScheduler* BasicTaskScheduler::createNew(unsigned maxSchedulerGranularity) { |
32 | return new BasicTaskScheduler(maxSchedulerGranularity); |
33 | } |
34 | |
35 | BasicTaskScheduler::BasicTaskScheduler(unsigned maxSchedulerGranularity) |
36 | : fMaxSchedulerGranularity(maxSchedulerGranularity), fMaxNumSockets(0) |
37 | #if defined(__WIN32__) || defined(_WIN32) |
38 | , fDummySocketNum(-1) |
39 | #endif |
40 | { |
41 | FD_ZERO(&fReadSet); |
42 | FD_ZERO(&fWriteSet); |
43 | FD_ZERO(&fExceptionSet); |
44 | |
45 | if (maxSchedulerGranularity > 0) schedulerTickTask(); // ensures that we handle events frequently |
46 | } |
47 | |
48 | BasicTaskScheduler::~BasicTaskScheduler() { |
49 | #if defined(__WIN32__) || defined(_WIN32) |
50 | if (fDummySocketNum >= 0) closeSocket(fDummySocketNum); |
51 | #endif |
52 | } |
53 | |
54 | void BasicTaskScheduler::schedulerTickTask(void* clientData) { |
55 | ((BasicTaskScheduler*)clientData)->schedulerTickTask(); |
56 | } |
57 | |
58 | void BasicTaskScheduler::schedulerTickTask() { |
59 | scheduleDelayedTask(fMaxSchedulerGranularity, schedulerTickTask, this); |
60 | } |
61 | |
62 | #ifndef MILLION |
63 | #define MILLION 1000000 |
64 | #endif |
65 | |
66 | void BasicTaskScheduler::SingleStep(unsigned maxDelayTime) { |
67 | fd_set readSet = fReadSet; // make a copy for this select() call |
68 | fd_set writeSet = fWriteSet; // ditto |
69 | fd_set exceptionSet = fExceptionSet; // ditto |
70 | |
71 | DelayInterval const& timeToDelay = fDelayQueue.timeToNextAlarm(); |
72 | struct timeval tv_timeToDelay; |
73 | tv_timeToDelay.tv_sec = timeToDelay.seconds(); |
74 | tv_timeToDelay.tv_usec = timeToDelay.useconds(); |
75 | // Very large "tv_sec" values cause select() to fail. |
76 | // Don't make it any larger than 1 million seconds (11.5 days) |
77 | const long MAX_TV_SEC = MILLION; |
78 | if (tv_timeToDelay.tv_sec > MAX_TV_SEC) { |
79 | tv_timeToDelay.tv_sec = MAX_TV_SEC; |
80 | } |
81 | // Also check our "maxDelayTime" parameter (if it's > 0): |
82 | if (maxDelayTime > 0 && |
83 | (tv_timeToDelay.tv_sec > (long)maxDelayTime/MILLION || |
84 | (tv_timeToDelay.tv_sec == (long)maxDelayTime/MILLION && |
85 | tv_timeToDelay.tv_usec > (long)maxDelayTime%MILLION))) { |
86 | tv_timeToDelay.tv_sec = maxDelayTime/MILLION; |
87 | tv_timeToDelay.tv_usec = maxDelayTime%MILLION; |
88 | } |
89 | |
90 | int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay); |
91 | if (selectResult < 0) { |
92 | #if defined(__WIN32__) || defined(_WIN32) |
93 | int err = WSAGetLastError(); |
94 | // For some unknown reason, select() in Windoze sometimes fails with WSAEINVAL if |
95 | // it was called with no entries set in "readSet". If this happens, ignore it: |
96 | if (err == WSAEINVAL && readSet.fd_count == 0) { |
97 | err = EINTR; |
98 | // To stop this from happening again, create a dummy socket: |
99 | if (fDummySocketNum >= 0) closeSocket(fDummySocketNum); |
100 | fDummySocketNum = socket(AF_INET, SOCK_DGRAM, 0); |
101 | FD_SET((unsigned)fDummySocketNum, &fReadSet); |
102 | } |
103 | if (err != EINTR) { |
104 | #else |
105 | if (errno != EINTR && errno != EAGAIN) { |
106 | #endif |
107 | // Unexpected error - treat this as fatal: |
108 | #if !defined(_WIN32_WCE) |
109 | perror("BasicTaskScheduler::SingleStep(): select() fails" ); |
110 | // Because this failure is often "Bad file descriptor" - which is caused by an invalid socket number (i.e., a socket number |
111 | // that had already been closed) being used in "select()" - we print out the sockets that were being used in "select()", |
112 | // to assist in debugging: |
113 | fprintf(stderr, "socket numbers used in the select() call:" ); |
114 | for (int i = 0; i < 10000; ++i) { |
115 | if (FD_ISSET(i, &fReadSet) || FD_ISSET(i, &fWriteSet) || FD_ISSET(i, &fExceptionSet)) { |
116 | fprintf(stderr, " %d(" , i); |
117 | if (FD_ISSET(i, &fReadSet)) fprintf(stderr, "r" ); |
118 | if (FD_ISSET(i, &fWriteSet)) fprintf(stderr, "w" ); |
119 | if (FD_ISSET(i, &fExceptionSet)) fprintf(stderr, "e" ); |
120 | fprintf(stderr, ")" ); |
121 | } |
122 | } |
123 | fprintf(stderr, "\n" ); |
124 | #endif |
125 | internalError(); |
126 | } |
127 | } |
128 | |
129 | // Call the handler function for one readable socket: |
130 | HandlerIterator iter(*fHandlers); |
131 | HandlerDescriptor* handler; |
132 | // To ensure forward progress through the handlers, begin past the last |
133 | // socket number that we handled: |
134 | if (fLastHandledSocketNum >= 0) { |
135 | while ((handler = iter.next()) != NULL) { |
136 | if (handler->socketNum == fLastHandledSocketNum) break; |
137 | } |
138 | if (handler == NULL) { |
139 | fLastHandledSocketNum = -1; |
140 | iter.reset(); // start from the beginning instead |
141 | } |
142 | } |
143 | while ((handler = iter.next()) != NULL) { |
144 | int sock = handler->socketNum; // alias |
145 | int resultConditionSet = 0; |
146 | if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE; |
147 | if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE; |
148 | if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION; |
149 | if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) { |
150 | fLastHandledSocketNum = sock; |
151 | // Note: we set "fLastHandledSocketNum" before calling the handler, |
152 | // in case the handler calls "doEventLoop()" reentrantly. |
153 | (*handler->handlerProc)(handler->clientData, resultConditionSet); |
154 | break; |
155 | } |
156 | } |
157 | if (handler == NULL && fLastHandledSocketNum >= 0) { |
158 | // We didn't call a handler, but we didn't get to check all of them, |
159 | // so try again from the beginning: |
160 | iter.reset(); |
161 | while ((handler = iter.next()) != NULL) { |
162 | int sock = handler->socketNum; // alias |
163 | int resultConditionSet = 0; |
164 | if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE; |
165 | if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE; |
166 | if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION; |
167 | if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) { |
168 | fLastHandledSocketNum = sock; |
169 | // Note: we set "fLastHandledSocketNum" before calling the handler, |
170 | // in case the handler calls "doEventLoop()" reentrantly. |
171 | (*handler->handlerProc)(handler->clientData, resultConditionSet); |
172 | break; |
173 | } |
174 | } |
175 | if (handler == NULL) fLastHandledSocketNum = -1;//because we didn't call a handler |
176 | } |
177 | |
178 | // Also handle any newly-triggered event (Note that we do this *after* calling a socket handler, |
179 | // in case the triggered event handler modifies The set of readable sockets.) |
180 | if (fTriggersAwaitingHandling != 0) { |
181 | if (fTriggersAwaitingHandling == fLastUsedTriggerMask) { |
182 | // Common-case optimization for a single event trigger: |
183 | fTriggersAwaitingHandling &=~ fLastUsedTriggerMask; |
184 | if (fTriggeredEventHandlers[fLastUsedTriggerNum] != NULL) { |
185 | (*fTriggeredEventHandlers[fLastUsedTriggerNum])(fTriggeredEventClientDatas[fLastUsedTriggerNum]); |
186 | } |
187 | } else { |
188 | // Look for an event trigger that needs handling (making sure that we make forward progress through all possible triggers): |
189 | unsigned i = fLastUsedTriggerNum; |
190 | EventTriggerId mask = fLastUsedTriggerMask; |
191 | |
192 | do { |
193 | i = (i+1)%MAX_NUM_EVENT_TRIGGERS; |
194 | mask >>= 1; |
195 | if (mask == 0) mask = 0x80000000; |
196 | |
197 | if ((fTriggersAwaitingHandling&mask) != 0) { |
198 | fTriggersAwaitingHandling &=~ mask; |
199 | if (fTriggeredEventHandlers[i] != NULL) { |
200 | (*fTriggeredEventHandlers[i])(fTriggeredEventClientDatas[i]); |
201 | } |
202 | |
203 | fLastUsedTriggerMask = mask; |
204 | fLastUsedTriggerNum = i; |
205 | break; |
206 | } |
207 | } while (i != fLastUsedTriggerNum); |
208 | } |
209 | } |
210 | |
211 | // Also handle any delayed event that may have come due. |
212 | fDelayQueue.handleAlarm(); |
213 | } |
214 | |
215 | void BasicTaskScheduler |
216 | ::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) { |
217 | if (socketNum < 0) return; |
218 | #if !defined(__WIN32__) && !defined(_WIN32) && defined(FD_SETSIZE) |
219 | if (socketNum >= (int)(FD_SETSIZE)) return; |
220 | #endif |
221 | FD_CLR((unsigned)socketNum, &fReadSet); |
222 | FD_CLR((unsigned)socketNum, &fWriteSet); |
223 | FD_CLR((unsigned)socketNum, &fExceptionSet); |
224 | if (conditionSet == 0) { |
225 | fHandlers->clearHandler(socketNum); |
226 | if (socketNum+1 == fMaxNumSockets) { |
227 | --fMaxNumSockets; |
228 | } |
229 | } else { |
230 | fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData); |
231 | if (socketNum+1 > fMaxNumSockets) { |
232 | fMaxNumSockets = socketNum+1; |
233 | } |
234 | if (conditionSet&SOCKET_READABLE) FD_SET((unsigned)socketNum, &fReadSet); |
235 | if (conditionSet&SOCKET_WRITABLE) FD_SET((unsigned)socketNum, &fWriteSet); |
236 | if (conditionSet&SOCKET_EXCEPTION) FD_SET((unsigned)socketNum, &fExceptionSet); |
237 | } |
238 | } |
239 | |
240 | void BasicTaskScheduler::moveSocketHandling(int oldSocketNum, int newSocketNum) { |
241 | if (oldSocketNum < 0 || newSocketNum < 0) return; // sanity check |
242 | #if !defined(__WIN32__) && !defined(_WIN32) && defined(FD_SETSIZE) |
243 | if (oldSocketNum >= (int)(FD_SETSIZE) || newSocketNum >= (int)(FD_SETSIZE)) return; // sanity check |
244 | #endif |
245 | if (FD_ISSET(oldSocketNum, &fReadSet)) {FD_CLR((unsigned)oldSocketNum, &fReadSet); FD_SET((unsigned)newSocketNum, &fReadSet);} |
246 | if (FD_ISSET(oldSocketNum, &fWriteSet)) {FD_CLR((unsigned)oldSocketNum, &fWriteSet); FD_SET((unsigned)newSocketNum, &fWriteSet);} |
247 | if (FD_ISSET(oldSocketNum, &fExceptionSet)) {FD_CLR((unsigned)oldSocketNum, &fExceptionSet); FD_SET((unsigned)newSocketNum, &fExceptionSet);} |
248 | fHandlers->moveHandler(oldSocketNum, newSocketNum); |
249 | |
250 | if (oldSocketNum+1 == fMaxNumSockets) { |
251 | --fMaxNumSockets; |
252 | } |
253 | if (newSocketNum+1 > fMaxNumSockets) { |
254 | fMaxNumSockets = newSocketNum+1; |
255 | } |
256 | } |
257 | |