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-2012 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() {
00032         return new BasicTaskScheduler();
00033 }
00034 
00035 #define MAX_SCHEDULER_GRANULARITY 10000 // 10 microseconds: We will return to the event loop at least this often
00036 static void schedulerTickTask(void* clientData) {
00037   ((BasicTaskScheduler*)clientData)->scheduleDelayedTask(MAX_SCHEDULER_GRANULARITY, schedulerTickTask, clientData);
00038 }
00039 
00040 BasicTaskScheduler::BasicTaskScheduler()
00041   : fMaxNumSockets(0) {
00042   FD_ZERO(&fReadSet);
00043   FD_ZERO(&fWriteSet);
00044   FD_ZERO(&fExceptionSet);
00045 
00046   schedulerTickTask(this); // ensures that we handle events frequently
00047 }
00048 
00049 BasicTaskScheduler::~BasicTaskScheduler() {
00050 }
00051 
00052 #ifndef MILLION
00053 #define MILLION 1000000
00054 #endif
00055 
00056 void BasicTaskScheduler::SingleStep(unsigned maxDelayTime) {
00057   fd_set readSet = fReadSet; // make a copy for this select() call
00058   fd_set writeSet = fWriteSet; // ditto
00059   fd_set exceptionSet = fExceptionSet; // ditto
00060 
00061   DelayInterval const& timeToDelay = fDelayQueue.timeToNextAlarm();
00062   struct timeval tv_timeToDelay;
00063   tv_timeToDelay.tv_sec = timeToDelay.seconds();
00064   tv_timeToDelay.tv_usec = timeToDelay.useconds();
00065   // Very large "tv_sec" values cause select() to fail.
00066   // Don't make it any larger than 1 million seconds (11.5 days)
00067   const long MAX_TV_SEC = MILLION;
00068   if (tv_timeToDelay.tv_sec > MAX_TV_SEC) {
00069     tv_timeToDelay.tv_sec = MAX_TV_SEC;
00070   }
00071   // Also check our "maxDelayTime" parameter (if it's > 0):
00072   if (maxDelayTime > 0 &&
00073       (tv_timeToDelay.tv_sec > (long)maxDelayTime/MILLION ||
00074        (tv_timeToDelay.tv_sec == (long)maxDelayTime/MILLION &&
00075         tv_timeToDelay.tv_usec > (long)maxDelayTime%MILLION))) {
00076     tv_timeToDelay.tv_sec = maxDelayTime/MILLION;
00077     tv_timeToDelay.tv_usec = maxDelayTime%MILLION;
00078   }
00079 
00080   int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);
00081   if (selectResult < 0) {
00082 #if defined(__WIN32__) || defined(_WIN32)
00083     int err = WSAGetLastError();
00084     // For some unknown reason, select() in Windoze sometimes fails with WSAEINVAL if
00085     // it was called with no entries set in "readSet".  If this happens, ignore it:
00086     if (err == WSAEINVAL && readSet.fd_count == 0) {
00087       err = EINTR;
00088       // To stop this from happening again, create a dummy socket:
00089       int dummySocketNum = socket(AF_INET, SOCK_DGRAM, 0);
00090       FD_SET((unsigned)dummySocketNum, &fReadSet);
00091     }
00092     if (err != EINTR) {
00093 #else
00094     if (errno != EINTR && errno != EAGAIN) {
00095 #endif
00096         // Unexpected error - treat this as fatal:
00097 #if !defined(_WIN32_WCE)
00098         perror("BasicTaskScheduler::SingleStep(): select() fails");
00099 #endif
00100         internalError();
00101       }
00102   }
00103 
00104   // Call the handler function for one readable socket:
00105   HandlerIterator iter(*fHandlers);
00106   HandlerDescriptor* handler;
00107   // To ensure forward progress through the handlers, begin past the last
00108   // socket number that we handled:
00109   if (fLastHandledSocketNum >= 0) {
00110     while ((handler = iter.next()) != NULL) {
00111       if (handler->socketNum == fLastHandledSocketNum) break;
00112     }
00113     if (handler == NULL) {
00114       fLastHandledSocketNum = -1;
00115       iter.reset(); // start from the beginning instead
00116     }
00117   }
00118   while ((handler = iter.next()) != NULL) {
00119     int sock = handler->socketNum; // alias
00120     int resultConditionSet = 0;
00121     if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
00122     if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
00123     if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
00124     if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) {
00125       fLastHandledSocketNum = sock;
00126           // Note: we set "fLastHandledSocketNum" before calling the handler,
00127           // in case the handler calls "doEventLoop()" reentrantly.
00128       (*handler->handlerProc)(handler->clientData, resultConditionSet);
00129       break;
00130     }
00131   }
00132   if (handler == NULL && fLastHandledSocketNum >= 0) {
00133     // We didn't call a handler, but we didn't get to check all of them,
00134     // so try again from the beginning:
00135     iter.reset();
00136     while ((handler = iter.next()) != NULL) {
00137       int sock = handler->socketNum; // alias
00138       int resultConditionSet = 0;
00139       if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
00140       if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
00141       if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
00142       if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) {
00143         fLastHandledSocketNum = sock;
00144             // Note: we set "fLastHandledSocketNum" before calling the handler,
00145             // in case the handler calls "doEventLoop()" reentrantly.
00146         (*handler->handlerProc)(handler->clientData, resultConditionSet);
00147         break;
00148       }
00149     }
00150     if (handler == NULL) fLastHandledSocketNum = -1;//because we didn't call a handler
00151   }
00152 
00153   // Also handle any newly-triggered event (Note that we do this *after* calling a socket handler,
00154   // in case the triggered event handler modifies The set of readable sockets.)
00155   if (fTriggersAwaitingHandling != 0) {
00156     if (fTriggersAwaitingHandling == fLastUsedTriggerMask) {
00157       // Common-case optimization for a single event trigger:
00158       fTriggersAwaitingHandling = 0;
00159       if (fTriggeredEventHandlers[fLastUsedTriggerNum] != NULL) {
00160         (*fTriggeredEventHandlers[fLastUsedTriggerNum])(fTriggeredEventClientDatas[fLastUsedTriggerNum]);
00161       }
00162     } else {
00163       // Look for an event trigger that needs handling (making sure that we make forward progress through all possible triggers):
00164       unsigned i = fLastUsedTriggerNum;
00165       EventTriggerId mask = fLastUsedTriggerMask;
00166 
00167       do {
00168         i = (i+1)%MAX_NUM_EVENT_TRIGGERS;
00169         mask >>= 1;
00170         if (mask == 0) mask = 0x80000000;
00171 
00172         if ((fTriggersAwaitingHandling&mask) != 0) {
00173           fTriggersAwaitingHandling &=~ mask;
00174           if (fTriggeredEventHandlers[i] != NULL) {
00175             (*fTriggeredEventHandlers[i])(fTriggeredEventClientDatas[i]);
00176           }
00177 
00178           fLastUsedTriggerMask = mask;
00179           fLastUsedTriggerNum = i;
00180           break;
00181         }
00182       } while (i != fLastUsedTriggerNum);
00183     }
00184   }
00185 
00186   // Also handle any delayed event that may have come due.
00187   fDelayQueue.handleAlarm();
00188 }
00189 
00190 void BasicTaskScheduler
00191   ::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {
00192   if (socketNum < 0) return;
00193   FD_CLR((unsigned)socketNum, &fReadSet);
00194   FD_CLR((unsigned)socketNum, &fWriteSet);
00195   FD_CLR((unsigned)socketNum, &fExceptionSet);
00196   if (conditionSet == 0) {
00197     fHandlers->clearHandler(socketNum);
00198     if (socketNum+1 == fMaxNumSockets) {
00199       --fMaxNumSockets;
00200     }
00201   } else {
00202     fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);
00203     if (socketNum+1 > fMaxNumSockets) {
00204       fMaxNumSockets = socketNum+1;
00205     }
00206     if (conditionSet&SOCKET_READABLE) FD_SET((unsigned)socketNum, &fReadSet);
00207     if (conditionSet&SOCKET_WRITABLE) FD_SET((unsigned)socketNum, &fWriteSet);
00208     if (conditionSet&SOCKET_EXCEPTION) FD_SET((unsigned)socketNum, &fExceptionSet);
00209   }
00210 }
00211 
00212 void BasicTaskScheduler::moveSocketHandling(int oldSocketNum, int newSocketNum) {
00213   if (oldSocketNum < 0 || newSocketNum < 0) return; // sanity check
00214   if (FD_ISSET(oldSocketNum, &fReadSet)) {FD_CLR((unsigned)oldSocketNum, &fReadSet); FD_SET((unsigned)newSocketNum, &fReadSet);}
00215   if (FD_ISSET(oldSocketNum, &fWriteSet)) {FD_CLR((unsigned)oldSocketNum, &fWriteSet); FD_SET((unsigned)newSocketNum, &fWriteSet);}
00216   if (FD_ISSET(oldSocketNum, &fExceptionSet)) {FD_CLR((unsigned)oldSocketNum, &fExceptionSet); FD_SET((unsigned)newSocketNum, &fExceptionSet);}
00217   fHandlers->moveHandler(oldSocketNum, newSocketNum);
00218 
00219   if (oldSocketNum+1 == fMaxNumSockets) {
00220     --fMaxNumSockets;
00221   }
00222   if (newSocketNum+1 > fMaxNumSockets) {
00223     fMaxNumSockets = newSocketNum+1;
00224   }
00225 }

Generated on Thu Feb 2 23:51:29 2012 for live by  doxygen 1.5.2