BasicUsageEnvironment/BasicTaskScheduler.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // Copyright (c) 1996-2014 Live Networks, Inc.  All rights reserved.
00017 // Basic Usage Environment: for a simple, non-scripted, console application
00018 // Implementation
00019 
00020 
00021 #include "BasicUsageEnvironment.hh"
00022 #include "HandlerSet.hh"
00023 #include <stdio.h>
00024 #if defined(_QNX4)
00025 #include <sys/select.h>
00026 #include <unix.h>
00027 #endif
00028 
00030 
00031 BasicTaskScheduler* BasicTaskScheduler::createNew(unsigned maxSchedulerGranularity) {
00032         return new BasicTaskScheduler(maxSchedulerGranularity);
00033 }
00034 
00035 BasicTaskScheduler::BasicTaskScheduler(unsigned maxSchedulerGranularity)
00036   : fMaxSchedulerGranularity(maxSchedulerGranularity), fMaxNumSockets(0) {
00037   FD_ZERO(&fReadSet);
00038   FD_ZERO(&fWriteSet);
00039   FD_ZERO(&fExceptionSet);
00040 
00041   if (maxSchedulerGranularity > 0) schedulerTickTask(); // ensures that we handle events frequently
00042 }
00043 
00044 BasicTaskScheduler::~BasicTaskScheduler() {
00045 }
00046 
00047 void BasicTaskScheduler::schedulerTickTask(void* clientData) {
00048   ((BasicTaskScheduler*)clientData)->schedulerTickTask();
00049 }
00050 
00051 void BasicTaskScheduler::schedulerTickTask() {
00052   scheduleDelayedTask(fMaxSchedulerGranularity, schedulerTickTask, this);
00053 }
00054 
00055 #ifndef MILLION
00056 #define MILLION 1000000
00057 #endif
00058 
00059 void BasicTaskScheduler::SingleStep(unsigned maxDelayTime) {
00060   fd_set readSet = fReadSet; // make a copy for this select() call
00061   fd_set writeSet = fWriteSet; // ditto
00062   fd_set exceptionSet = fExceptionSet; // ditto
00063 
00064   DelayInterval const& timeToDelay = fDelayQueue.timeToNextAlarm();
00065   struct timeval tv_timeToDelay;
00066   tv_timeToDelay.tv_sec = timeToDelay.seconds();
00067   tv_timeToDelay.tv_usec = timeToDelay.useconds();
00068   // Very large "tv_sec" values cause select() to fail.
00069   // Don't make it any larger than 1 million seconds (11.5 days)
00070   const long MAX_TV_SEC = MILLION;
00071   if (tv_timeToDelay.tv_sec > MAX_TV_SEC) {
00072     tv_timeToDelay.tv_sec = MAX_TV_SEC;
00073   }
00074   // Also check our "maxDelayTime" parameter (if it's > 0):
00075   if (maxDelayTime > 0 &&
00076       (tv_timeToDelay.tv_sec > (long)maxDelayTime/MILLION ||
00077        (tv_timeToDelay.tv_sec == (long)maxDelayTime/MILLION &&
00078         tv_timeToDelay.tv_usec > (long)maxDelayTime%MILLION))) {
00079     tv_timeToDelay.tv_sec = maxDelayTime/MILLION;
00080     tv_timeToDelay.tv_usec = maxDelayTime%MILLION;
00081   }
00082 
00083   int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);
00084   if (selectResult < 0) {
00085 #if defined(__WIN32__) || defined(_WIN32)
00086     int err = WSAGetLastError();
00087     // For some unknown reason, select() in Windoze sometimes fails with WSAEINVAL if
00088     // it was called with no entries set in "readSet".  If this happens, ignore it:
00089     if (err == WSAEINVAL && readSet.fd_count == 0) {
00090       err = EINTR;
00091       // To stop this from happening again, create a dummy socket:
00092       int dummySocketNum = socket(AF_INET, SOCK_DGRAM, 0);
00093       FD_SET((unsigned)dummySocketNum, &fReadSet);
00094     }
00095     if (err != EINTR) {
00096 #else
00097     if (errno != EINTR && errno != EAGAIN) {
00098 #endif
00099         // Unexpected error - treat this as fatal:
00100 #if !defined(_WIN32_WCE)
00101         perror("BasicTaskScheduler::SingleStep(): select() fails");
00102         // Because this failure is often "Bad file descriptor" - which is caused by an invalid socket number (i.e., a socket number
00103         // that had already been closed) being used in "select()" - we print out the sockets that were being used in "select()",
00104         // to assist in debugging:
00105         fprintf(stderr, "socket numbers used in the select() call:");
00106         for (int i = 0; i < 10000; ++i) {
00107           if (FD_ISSET(i, &fReadSet) || FD_ISSET(i, &fWriteSet) || FD_ISSET(i, &fExceptionSet)) {
00108             fprintf(stderr, " %d(", i);
00109             if (FD_ISSET(i, &fReadSet)) fprintf(stderr, "r");
00110             if (FD_ISSET(i, &fWriteSet)) fprintf(stderr, "w");
00111             if (FD_ISSET(i, &fExceptionSet)) fprintf(stderr, "e");
00112             fprintf(stderr, ")");
00113           }
00114         }
00115         fprintf(stderr, "\n");
00116 #endif
00117         internalError();
00118       }
00119   }
00120 
00121   // Call the handler function for one readable socket:
00122   HandlerIterator iter(*fHandlers);
00123   HandlerDescriptor* handler;
00124   // To ensure forward progress through the handlers, begin past the last
00125   // socket number that we handled:
00126   if (fLastHandledSocketNum >= 0) {
00127     while ((handler = iter.next()) != NULL) {
00128       if (handler->socketNum == fLastHandledSocketNum) break;
00129     }
00130     if (handler == NULL) {
00131       fLastHandledSocketNum = -1;
00132       iter.reset(); // start from the beginning instead
00133     }
00134   }
00135   while ((handler = iter.next()) != NULL) {
00136     int sock = handler->socketNum; // alias
00137     int resultConditionSet = 0;
00138     if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
00139     if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
00140     if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
00141     if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) {
00142       fLastHandledSocketNum = sock;
00143           // Note: we set "fLastHandledSocketNum" before calling the handler,
00144           // in case the handler calls "doEventLoop()" reentrantly.
00145       (*handler->handlerProc)(handler->clientData, resultConditionSet);
00146       break;
00147     }
00148   }
00149   if (handler == NULL && fLastHandledSocketNum >= 0) {
00150     // We didn't call a handler, but we didn't get to check all of them,
00151     // so try again from the beginning:
00152     iter.reset();
00153     while ((handler = iter.next()) != NULL) {
00154       int sock = handler->socketNum; // alias
00155       int resultConditionSet = 0;
00156       if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
00157       if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
00158       if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
00159       if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) {
00160         fLastHandledSocketNum = sock;
00161             // Note: we set "fLastHandledSocketNum" before calling the handler,
00162             // in case the handler calls "doEventLoop()" reentrantly.
00163         (*handler->handlerProc)(handler->clientData, resultConditionSet);
00164         break;
00165       }
00166     }
00167     if (handler == NULL) fLastHandledSocketNum = -1;//because we didn't call a handler
00168   }
00169 
00170   // Also handle any newly-triggered event (Note that we do this *after* calling a socket handler,
00171   // in case the triggered event handler modifies The set of readable sockets.)
00172   if (fTriggersAwaitingHandling != 0) {
00173     if (fTriggersAwaitingHandling == fLastUsedTriggerMask) {
00174       // Common-case optimization for a single event trigger:
00175       fTriggersAwaitingHandling = 0;
00176       if (fTriggeredEventHandlers[fLastUsedTriggerNum] != NULL) {
00177         (*fTriggeredEventHandlers[fLastUsedTriggerNum])(fTriggeredEventClientDatas[fLastUsedTriggerNum]);
00178       }
00179     } else {
00180       // Look for an event trigger that needs handling (making sure that we make forward progress through all possible triggers):
00181       unsigned i = fLastUsedTriggerNum;
00182       EventTriggerId mask = fLastUsedTriggerMask;
00183 
00184       do {
00185         i = (i+1)%MAX_NUM_EVENT_TRIGGERS;
00186         mask >>= 1;
00187         if (mask == 0) mask = 0x80000000;
00188 
00189         if ((fTriggersAwaitingHandling&mask) != 0) {
00190           fTriggersAwaitingHandling &=~ mask;
00191           if (fTriggeredEventHandlers[i] != NULL) {
00192             (*fTriggeredEventHandlers[i])(fTriggeredEventClientDatas[i]);
00193           }
00194 
00195           fLastUsedTriggerMask = mask;
00196           fLastUsedTriggerNum = i;
00197           break;
00198         }
00199       } while (i != fLastUsedTriggerNum);
00200     }
00201   }
00202 
00203   // Also handle any delayed event that may have come due.
00204   fDelayQueue.handleAlarm();
00205 }
00206 
00207 void BasicTaskScheduler
00208   ::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {
00209   if (socketNum < 0) return;
00210   FD_CLR((unsigned)socketNum, &fReadSet);
00211   FD_CLR((unsigned)socketNum, &fWriteSet);
00212   FD_CLR((unsigned)socketNum, &fExceptionSet);
00213   if (conditionSet == 0) {
00214     fHandlers->clearHandler(socketNum);
00215     if (socketNum+1 == fMaxNumSockets) {
00216       --fMaxNumSockets;
00217     }
00218   } else {
00219     fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);
00220     if (socketNum+1 > fMaxNumSockets) {
00221       fMaxNumSockets = socketNum+1;
00222     }
00223     if (conditionSet&SOCKET_READABLE) FD_SET((unsigned)socketNum, &fReadSet);
00224     if (conditionSet&SOCKET_WRITABLE) FD_SET((unsigned)socketNum, &fWriteSet);
00225     if (conditionSet&SOCKET_EXCEPTION) FD_SET((unsigned)socketNum, &fExceptionSet);
00226   }
00227 }
00228 
00229 void BasicTaskScheduler::moveSocketHandling(int oldSocketNum, int newSocketNum) {
00230   if (oldSocketNum < 0 || newSocketNum < 0) return; // sanity check
00231   if (FD_ISSET(oldSocketNum, &fReadSet)) {FD_CLR((unsigned)oldSocketNum, &fReadSet); FD_SET((unsigned)newSocketNum, &fReadSet);}
00232   if (FD_ISSET(oldSocketNum, &fWriteSet)) {FD_CLR((unsigned)oldSocketNum, &fWriteSet); FD_SET((unsigned)newSocketNum, &fWriteSet);}
00233   if (FD_ISSET(oldSocketNum, &fExceptionSet)) {FD_CLR((unsigned)oldSocketNum, &fExceptionSet); FD_SET((unsigned)newSocketNum, &fExceptionSet);}
00234   fHandlers->moveHandler(oldSocketNum, newSocketNum);
00235 
00236   if (oldSocketNum+1 == fMaxNumSockets) {
00237     --fMaxNumSockets;
00238   }
00239   if (newSocketNum+1 > fMaxNumSockets) {
00240     fMaxNumSockets = newSocketNum+1;
00241   }
00242 }

Generated on Wed Apr 23 16:11:58 2014 for live by  doxygen 1.5.2