home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / modules / schedulr / src / schedulr.c < prev   
Encoding:
C/C++ Source or Header  |  1998-04-08  |  22.6 KB  |  897 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /********************************************************************/
  20.  
  21. #include <prtypes.h>
  22. #include <prmem.h>
  23. #include <prlog.h>
  24. #include <prtime.h>
  25. #include <prclist.h>
  26. #include <limits.h>
  27.  
  28. #include "schedulr.h"
  29.  
  30. #define SCHEDULER_SLOP    1000000
  31. /*
  32.     If an event is scheduled to happen within this time frame (in milliseconds)
  33.     we'll pull it off the queue rather than entering another wait loop.
  34. */
  35.  
  36. #define SCHED_RUNNING     (PR_BIT(0))
  37. #define SCHED_PAUSED      (PR_BIT(1))
  38. #define SCHED_FROZEN      (PR_BIT(2))
  39. #define SCHED_TERMINATED  (PR_BIT(31))
  40. /*
  41.     Status bits for the scheduler
  42. */
  43.  
  44. typedef struct  _SchedulerData {
  45.     PRUint32    flags;        
  46.     PRUint32    nextID;
  47.     PRUint32    nextObserver;
  48.     PRTime        nextEventTime;
  49.     PRThread    *pSchedThread;
  50.     PRLock        *pTimerLock;
  51.     PRCondVar    *pTimerCVar;
  52.     PRCList        *pEventQueue;
  53.     PRCList        *pObserverQueue;
  54.     PRCList        eventQ;
  55. } SchedulerData;
  56.  
  57. /*
  58.     SchedulerPtr resolves internally to this data type, which holds the
  59.     state for a particular scheduler.  Note that pEventQueue points to
  60.     the eventQ struct which is embedded within the data, so pEventQueue
  61.     must never be free'd (since it was never allocated)
  62. */
  63.  
  64. typedef struct    _SchedulerEvent {
  65.     PRCList                *next;
  66.     PRCList                *prev;
  67.     PRUint32            eventID;
  68.     SchedulerTime        eventTime;
  69.     PRTime                fireTime;
  70.     SchedFuncPtr        pEventProc;
  71.     SchedFuncDataPtr    pEventData;
  72.     char                *eventName;
  73. } SchedEvent;
  74.  
  75. typedef struct _EventParameters {
  76.     SchedulerPtr        pScheduler;
  77.     PRUint32            eventID;
  78.     SchedFuncPtr        pEventProc;
  79.     SchedFuncDataPtr    pEventData;
  80. } EventParams;
  81. /*
  82.     EventParams is an encapsulation of the data that's passed to the
  83.     event function.  Because thread creation only takes a single
  84.     parameter, EventParams is necessary to allow all of the data
  85.     to go across.
  86. */
  87.  
  88.  
  89. typedef struct    _SchedulerObserver {
  90.     PRCList                *next;
  91.     PRCList                *prev;
  92.     PRUint32            observerID;
  93.     SchedObsType        typeMask;
  94.     SchedObsPtr            pObserverProc;
  95.     SchedObsDataPtr        pObserverData;
  96. } SchedObserver;
  97.  
  98. /* Forward Definitions */
  99.  
  100. void            DoEvent(EventParams *pParms);
  101.  
  102. void            SchedulerRun(SchedulerData *pData);
  103. SchedulerErr    SchedulerInit(SchedulerData *pData);
  104. void            SchedulerDestroy(SchedulerData *pData);
  105.  
  106. SchedEvent *    EventFromID(SchedulerData *pData, PRUint32 eventID);
  107. void            InsertEvent(SchedulerData *pData, SchedEvent *pEvent);
  108. void            RescheduleEvent(SchedulerData *pScheduler, SchedEvent *pEvent);
  109.  
  110. void            NotifyObservers(SchedulerData *pScheduler, SchedObsType type,
  111.                                 PRUint32 eventID, char *szEventName,
  112.                                 SchedFuncDataPtr pEventData);
  113. SchedObserver*    ObserverFromID(SchedulerData *pData, PRUint32 observerID);
  114.  
  115.  
  116. /* Implementation */
  117.  
  118. void DoEvent(EventParams *pParms)
  119. {
  120.     pParms->pEventProc(pParms->pScheduler, pParms->eventID, (void *) pParms->pEventData);
  121. }
  122.  
  123. void SchedulerRun(SchedulerData    *pData)
  124. {
  125.     PRUint32            uSecDifference;
  126.     PRTime                timeDifference;
  127.     PRInt64                scratchVal;
  128.     PRIntervalTime        timeout;
  129.     SchedEvent            *pCurrentEvent;
  130.  
  131.     pData->flags |= SCHED_RUNNING;
  132.  
  133.     while (!(pData->flags & SCHED_TERMINATED)) {
  134.         if (pData->flags & SCHED_RUNNING) {
  135.             pCurrentEvent = NULL;
  136.  
  137.             /* Then, compute the next scheduler interval and wait */
  138.             PR_Lock(pData->pTimerLock);
  139.  
  140.             if ((PR_CLIST_IS_EMPTY(pData->pEventQueue)) ||
  141.                 (LL_IS_ZERO(pData->nextEventTime))) {
  142.  
  143.                 /* if there's nothing on the queue, the timeout is infinite */
  144.  
  145.                 timeout = PR_INTERVAL_NO_TIMEOUT;
  146.             } else {
  147.                 /* otherwise, it's the time between now and the next event */
  148.  
  149.                 LL_SUB(timeDifference, pData->nextEventTime, PR_Now());
  150.  
  151.                 LL_I2L(scratchVal, LONG_MAX);
  152.                 if (LL_CMP(timeDifference, <, scratchVal)) {
  153.  
  154.                     if (!(LL_GE_ZERO(timeDifference))) {
  155.                         timeout = PR_INTERVAL_NO_WAIT;
  156.  
  157.                     } else {
  158.                         LL_L2UI(uSecDifference, timeDifference);
  159.  
  160.                         timeout = PR_MicrosecondsToInterval(uSecDifference);
  161.                     }
  162.                 } else {
  163.                     timeout = ULONG_MAX;
  164.                 }
  165.             }
  166.  
  167.             PR_WaitCondVar(pData->pTimerCVar, timeout);
  168.  
  169.             /* if we've come out of the wait condition, it's because:
  170.  
  171.                 1) we've scheduled a new event before the current one
  172.                 2) the current event was deleted
  173.                 3) it's time for the current event to fire
  174.                 4) the crawler is terminating
  175.                 5) the thread was spuriously interrupted
  176.  
  177.                all of these conditions will get checked subsequent to
  178.                here, or the next time the loop is evaluated
  179.             */
  180.  
  181.             /* First, check that the stated next event time is accurate
  182.                (to check for addition/deletion cases) */
  183.  
  184.             if (PR_CLIST_IS_EMPTY(pData->pEventQueue)) {
  185.                 pData->nextEventTime = LL_ZERO;
  186.             } else {
  187.                 pData->nextEventTime = ((SchedEvent *) PR_LIST_HEAD(pData->pEventQueue))->fireTime;
  188.  
  189.                 /* Now, see if the nextEventTime is close enough to 
  190.                    pop the event off of the queue and process it */
  191.  
  192.                 LL_SUB(timeDifference, pData->nextEventTime,
  193.                     PR_Now());
  194.  
  195.                 LL_I2L(scratchVal, LONG_MAX);
  196.                 if (LL_CMP(timeDifference, <, scratchVal)) {
  197.  
  198.                     LL_L2UI(uSecDifference, timeDifference);
  199.  
  200.                     if (!(LL_GE_ZERO(timeDifference)) || (uSecDifference < SCHEDULER_SLOP)) {
  201.                         /* yup, take the first item off the queue and use it. */
  202.                         pCurrentEvent = (SchedEvent *) PR_LIST_HEAD(pData->pEventQueue);
  203.  
  204.                         PR_REMOVE_LINK(pCurrentEvent);
  205.                     }
  206.                 }
  207.             }
  208.  
  209.             PR_Unlock(pData->pTimerLock);
  210.  
  211.             /* we didn't want to actually process the event while we were in the lock */
  212.  
  213.             if (pCurrentEvent != NULL) {
  214.                 if (pData->flags & SCHED_PAUSED) {
  215.                     /* we missed an event */
  216.                     /* not really, we only missed it if expires in this time frame */
  217.                     /* if we missed it, send out a missed notification */
  218.  
  219.                 } else {
  220.                     /* process event */
  221.                     EventParams        parms;
  222.  
  223.                     parms.pScheduler = pData;
  224.                     parms.pEventProc = pCurrentEvent->pEventProc;
  225.                     parms.pEventData = pCurrentEvent->pEventData;
  226.                     parms.eventID = pCurrentEvent->eventID;
  227.  
  228.                     PR_CreateThread(PR_USER_THREAD,
  229.                         (void (*)(void *arg)) DoEvent,
  230.                         &parms,
  231.                         PR_PRIORITY_NORMAL,
  232.                         PR_LOCAL_THREAD,
  233.                         PR_UNJOINABLE_THREAD,
  234.                         0);
  235.  
  236.                     /* notify observers */
  237.                     NotifyObservers(pData, SCHED_OBSERVE_FIRE, pCurrentEvent->eventID,
  238.                         pCurrentEvent->eventName, pCurrentEvent->pEventData);
  239.                 }
  240.  
  241.                 /* if the event is a repeating event, reschedule it */
  242.                 if (pCurrentEvent->eventTime.repeating != 0) {
  243.                     RescheduleEvent(pData, pCurrentEvent);
  244.                 } else {
  245.                     if (pCurrentEvent->eventName != NULL) {
  246.                         PR_Free(pCurrentEvent->eventName);
  247.                     }
  248.  
  249.                     PR_DELETE(pCurrentEvent);
  250.                 }
  251.             }
  252.         }
  253.     }
  254.  
  255.     SchedulerDestroy(pData);
  256. }
  257.  
  258. SchedulerErr SchedulerInit(SchedulerData *pData) {
  259.  
  260.     pData->nextID = 1;
  261.     pData->nextObserver = 1000;
  262.     pData->pTimerLock = PR_NewLock();
  263.     pData->pTimerCVar = PR_NewCondVar(pData->pTimerLock);
  264.  
  265.     pData->pSchedThread = PR_CreateThread(PR_USER_THREAD,
  266.             (void (*)(void *)) SchedulerRun,
  267.             (void *) pData, 
  268.             PR_PRIORITY_NORMAL,
  269.             PR_LOCAL_THREAD,
  270.             PR_JOINABLE_THREAD,
  271.             0);
  272.  
  273.     pData->pEventQueue = &(pData->eventQ);
  274.     PR_INIT_CLIST(pData->pEventQueue);
  275.  
  276.     return SCHED_ERR_NOERR;
  277. }
  278.  
  279. void SchedulerDestroy(SchedulerData *pData) {
  280.     SchedObserver *pHead, *pCurrent, *pOld;
  281.  
  282.     PR_ASSERT(pData != NULL);
  283.  
  284.     /* First, clear out the observer list */
  285.  
  286.     if (pData->pObserverQueue != NULL) {
  287.  
  288.         PR_Lock(pData->pTimerLock);
  289.  
  290.         if (!PR_CLIST_IS_EMPTY(pData->pObserverQueue)) {
  291.             pHead = pCurrent = (SchedObserver *) PR_LIST_HEAD(pData->pObserverQueue);
  292.  
  293.             do {
  294.                 pOld = pCurrent;
  295.                 pCurrent = (SchedObserver *) PR_NEXT_LINK(pCurrent);
  296.  
  297.                 PR_REMOVE_LINK(pOld);
  298.                 PR_DELETE(pOld);
  299.             } while (!PR_CLIST_IS_EMPTY(pData->pObserverQueue));
  300.         }
  301.  
  302.         PR_Free(pData->pObserverQueue);
  303.         pData->pObserverQueue = NULL;
  304.  
  305.         PR_Unlock(pData->pTimerLock);
  306.     }
  307.  
  308.     /*    FIXME! Need to remove all events*/
  309.  
  310.  
  311.     /* Release the locks and condition variable */
  312.  
  313.     if (pData->pTimerCVar != NULL) {
  314.         PR_DestroyCondVar(pData->pTimerCVar); 
  315.         pData->pTimerCVar = NULL;
  316.     }
  317.  
  318.     if (pData->pTimerLock != NULL) {
  319.         PR_DestroyLock(pData->pTimerLock); 
  320.         pData->pTimerLock = NULL;
  321.     }
  322.  
  323.     /* Finally, delete the instance */
  324.  
  325.     PR_DELETE(pData);
  326.  
  327.     return;
  328. }
  329.  
  330. /* EventFromID should always (and only) be called from inside a lock */
  331.  
  332. SchedEvent *
  333. EventFromID(SchedulerData *pData, PRUint32 eventID)
  334. {
  335.     SchedEvent    *pReturn = NULL;
  336.     SchedEvent    *pHead, *pCurrent;
  337.  
  338.     if ((pData != NULL) && (!PR_CLIST_IS_EMPTY(pData->pEventQueue))) {
  339.  
  340.         pHead = pCurrent = (SchedEvent *) PR_LIST_HEAD(pData->pEventQueue);
  341.  
  342.         do {
  343.             if (pCurrent->eventID == eventID) {
  344.                 pReturn = pCurrent;
  345.             }
  346.  
  347.             pCurrent = (SchedEvent *) PR_NEXT_LINK(pCurrent);
  348.         } while ((pReturn == NULL) && (pCurrent != pHead));
  349.     }
  350.  
  351.     return pReturn;
  352. }
  353.  
  354. /* InsertEvent should always (and only) be called from inside a lock */
  355.  
  356. /* InsertEvent is really pretty inefficient.  It works by checking to
  357.    see if the event is before the first one, and if so, inserts it there.
  358.    Then, it checks to see if it's after the last one.  Otherwise, it
  359.    walks forward from the first element using an insertion sort.
  360. */
  361.  
  362. void
  363. InsertEvent(SchedulerData *pData, SchedEvent *pEvent)
  364. {
  365.  
  366.     PR_ASSERT(pData != NULL);
  367.     PR_ASSERT(pEvent != NULL);
  368.  
  369.     if (PR_CLIST_IS_EMPTY(pData->pEventQueue)) {
  370.         PR_INSERT_LINK((PRCList *) pEvent, pData->pEventQueue);
  371.  
  372.         /* update the scheduler's nextEventTime */
  373.         pData->nextEventTime = pEvent ->fireTime;
  374.     } else {
  375.         SchedEvent    *pCurrent;
  376.  
  377.         pCurrent = (SchedEvent *) PR_LIST_HEAD(pData->pEventQueue);
  378.  
  379.         if (LL_CMP(pEvent->fireTime, <, pCurrent->fireTime)) {
  380.             PR_INSERT_LINK((PRCList *) pEvent, pData->pEventQueue);
  381.  
  382.              /* update the scheduler's nextEventTime */
  383.             pData->nextEventTime = pEvent ->fireTime;
  384.     
  385.         } else if (!(LL_CMP(pEvent->fireTime, <, 
  386.             ((SchedEvent *) PR_LIST_TAIL(pData->pEventQueue))->fireTime))) {
  387.             PR_APPEND_LINK((PRCList *) pEvent, pData->pEventQueue);
  388.         } else {
  389.             /* it's not the first, or the last item, so do the painful
  390.                insertion thang. */
  391.             int        done = 0;
  392.  
  393.             while (!done) {
  394.                 pCurrent = (SchedEvent *) PR_NEXT_LINK(pCurrent);
  395.  
  396.                 if (LL_CMP(pEvent->fireTime, <, pCurrent->fireTime)) {
  397.                     PR_INSERT_BEFORE((PRCList *) pEvent, (PRCList *) pCurrent);
  398.                     done = 1;
  399.                 }
  400.  
  401.             } /* while */
  402.         } /* else */
  403.     } /* else (not empty list */
  404.  
  405.     return;
  406. }
  407.  
  408.  
  409. void RescheduleEvent(SchedulerData *pScheduler, SchedEvent *pEvent) {
  410.  
  411.     /* compute the new firing time */
  412.  
  413.     /* rescheduled event times are computed by taking the event's scheduled time
  414.        and adding the appropriate number of seconds to the base.  Then, a randomized
  415.        value (plus or minus) is added, and the event is inserted into the queue.
  416.        This implies that examination of the event structure after the first firing
  417.        of an event will yield the adjusted base time, not the original base time.
  418.  
  419.        If the newly scheduled event base time falls outside of the end time for the event,
  420.        the event is discarded and is not scheduled.  However, if the event's scheduled
  421.        time + its randomized value within range falls outside of the end time, the
  422.        event's firing time is pinned to the end time and still gets scheduled.
  423.      */
  424.  
  425.     PRTime                fireTime;
  426.     PRInt32                random, randomSecs;
  427.     PRInt64                randomizer, repeatValue;
  428.     PRInt64                secs64, us2s;
  429.     SchedulerTime        eventTime;
  430.  
  431.     LL_UI2L(us2s, PR_USEC_PER_SEC);
  432.     LL_I2L(secs64, pEvent->eventTime.repeating);
  433.  
  434.     LL_MUL(repeatValue, secs64, us2s);
  435.     LL_ADD(pEvent->eventTime.baseTime, pEvent->eventTime.baseTime, repeatValue);
  436.  
  437.     eventTime = pEvent->eventTime;
  438.  
  439.     if ((LL_NE(eventTime.end, LL_ZERO)) && (LL_CMP(eventTime.baseTime, >, eventTime.end))) {
  440.         /* event is out of range; delete and move on */
  441.  
  442.         if (pEvent->eventName != NULL) {
  443.             PR_Free(pEvent->eventName);
  444.         }
  445.  
  446.         PR_DELETE(pEvent);
  447.     } else {
  448.  
  449.         if (eventTime.range != 0) {
  450.  
  451.             random = (PRInt32) rand();
  452.             randomSecs = random % eventTime.range;
  453.  
  454.             LL_I2L(secs64, randomSecs);
  455.             LL_MUL(randomizer, secs64, us2s);
  456.  
  457.             if ((random % 2) == 0) {
  458.                 LL_NEG(randomizer, randomizer);
  459.             }
  460.  
  461.             LL_ADD(fireTime, eventTime.baseTime, randomizer);
  462.         } else {
  463.             fireTime = eventTime.baseTime;
  464.         }
  465.  
  466.         if ((LL_NE(LL_ZERO, eventTime.end)) && (LL_CMP(fireTime, >, eventTime.end))) {
  467.             pEvent->fireTime = eventTime.end;
  468.         } else if (LL_CMP(fireTime, <, eventTime.start)) {
  469.             pEvent->fireTime = eventTime.start;
  470.         } else {
  471.             pEvent->fireTime = fireTime;
  472.         }
  473.  
  474.  
  475.         PR_Lock(pScheduler->pTimerLock);
  476.  
  477.         InsertEvent(pScheduler, pEvent);
  478.  
  479.         PR_NotifyCondVar(pScheduler->pTimerCVar);
  480.  
  481.         PR_Unlock(pScheduler->pTimerLock);
  482.     }
  483.  
  484. }
  485.  
  486. void
  487. NotifyObservers(SchedulerData *pScheduler, SchedObsType type,
  488.                 PRUint32 eventID, char *szEventName, SchedFuncDataPtr pEventData)
  489. {
  490.  
  491.     SchedObserver    *pHead, *pCurrent;
  492.  
  493.     PR_ASSERT(pScheduler != NULL);
  494.  
  495.     if ((pScheduler->pObserverQueue != NULL) && (!PR_CLIST_IS_EMPTY(pScheduler->pObserverQueue))) {
  496.  
  497.         pHead = pCurrent = (SchedObserver *) PR_LIST_HEAD(pScheduler->pObserverQueue);
  498.  
  499.         do {
  500.             if (pCurrent->typeMask & type) {
  501.                 pCurrent->pObserverProc(pScheduler, pCurrent->observerID, pCurrent->pObserverData,
  502.                                         type, eventID, szEventName, pEventData);
  503.             }
  504.  
  505.             pCurrent = (SchedObserver *) PR_NEXT_LINK(pCurrent);
  506.         } while (pCurrent != pHead);
  507.     }
  508.  
  509.  
  510.     return;
  511. }
  512.  
  513.  
  514. SchedObserver *
  515. ObserverFromID(SchedulerData *pData, PRUint32 observerID)
  516. {
  517.     SchedObserver *pReturn = NULL;
  518.     SchedObserver *pHead, *pCurrent;
  519.  
  520.     PR_ASSERT(pData != NULL);
  521.  
  522.     if ((pData->pObserverQueue != NULL) && (!PR_CLIST_IS_EMPTY(pData->pObserverQueue))) {
  523.  
  524.         pHead = pCurrent = (SchedObserver *) PR_LIST_HEAD(pData->pObserverQueue);
  525.  
  526.         do {
  527.             if (pCurrent->observerID == observerID) {
  528.                 pReturn = pCurrent;
  529.             }
  530.  
  531.             pCurrent = (SchedObserver *) PR_NEXT_LINK(pCurrent);
  532.         } while ((pReturn == NULL) && (pCurrent != pHead));
  533.     }
  534.  
  535.     return pReturn;
  536. }
  537.  
  538. /* Implementation of publically callable functions */
  539.  
  540. PR_IMPLEMENT(SchedulerPtr)
  541. SchedulerStart(void)
  542. {
  543.     SchedulerData    *pNewScheduler = PR_NEWZAP(SchedulerData);
  544.  
  545.     if (pNewScheduler != NULL) {
  546.  
  547.         SchedulerInit(pNewScheduler);
  548.     }
  549.  
  550.     return (SchedulerPtr) pNewScheduler;
  551. }
  552.  
  553.  
  554. PR_IMPLEMENT(SchedulerErr)
  555. SchedulerStop(SchedulerPtr pArg)
  556. {
  557.     SchedulerData        *pData = (SchedulerData *) pArg;
  558.  
  559.     if (pData == NULL) {
  560.         PR_ASSERT(pData != NULL);
  561.  
  562.         return SCHED_ERR_INVALID_SCHEDULER;
  563.     }
  564.  
  565.     pData->flags |= SCHED_TERMINATED;
  566.     
  567.     PR_Lock(pData->pTimerLock);
  568.     PR_NotifyCondVar(pData->pTimerCVar);
  569.     PR_Unlock(pData->pTimerLock);
  570.  
  571.     return SCHED_ERR_NOERR;
  572. }
  573.  
  574. PR_IMPLEMENT(SchedulerErr)
  575. SchedulerPause(SchedulerPtr pArg)
  576. {
  577.     SchedulerData        *pData = (SchedulerData *) pArg;
  578.  
  579.     if (pData == NULL) {
  580.         PR_ASSERT(pData != NULL);
  581.  
  582.         return SCHED_ERR_INVALID_SCHEDULER;
  583.     }
  584.  
  585.     pData->flags |= SCHED_PAUSED;
  586.     return SCHED_ERR_NOERR;
  587. }
  588.  
  589. PR_IMPLEMENT(SchedulerErr)
  590. SchedulerResume(SchedulerPtr pArg)
  591. {
  592.     SchedulerData        *pData = (SchedulerData *) pArg;
  593.  
  594.     if (pData == NULL) {
  595.         PR_ASSERT(pData != NULL);
  596.  
  597.         return SCHED_ERR_INVALID_SCHEDULER;
  598.     }
  599.  
  600.     pData->flags = (pData->flags & ~SCHED_PAUSED);
  601.     return SCHED_ERR_NOERR;
  602. }
  603.  
  604.  
  605. PR_IMPLEMENT(SchedulerErr)
  606. SchedulerAddEvent(SchedulerPtr pArg,
  607.                                PRUint32 *pEventID,
  608.                                const char *szName,
  609.                                SchedulerTime *pTime,
  610.                                SchedFuncPtr function,
  611.                                SchedFuncDataPtr pData) 
  612. {
  613.     SchedulerData        *pScheduler = (SchedulerData *) pArg;
  614.     SchedEvent            *pNewEvent;    
  615.     SchedulerErr        err = SCHED_ERR_NOERR;
  616.  
  617.     if (pScheduler == NULL) {
  618.         PR_ASSERT(pScheduler != NULL);
  619.  
  620.         return SCHED_ERR_INVALID_SCHEDULER;
  621.     }
  622.  
  623.     if (function == NULL) {
  624.         return SCHED_ERR_BAD_PARAMETER;
  625.     }
  626.  
  627.     if ((pTime == NULL) || (pTime->range < 0) ||
  628.         (LL_CMP(pTime->baseTime, <, pTime->start)) ||
  629.         ((!LL_IS_ZERO(pTime->end)) && (LL_CMP(pTime->baseTime, >, pTime->end)))) {
  630.         return SCHED_ERR_BAD_TIME;
  631.     }
  632.  
  633.     pNewEvent = PR_NEWZAP(SchedEvent);
  634.  
  635.     if (pNewEvent == NULL) {
  636.         return SCHED_ERR_OUT_OF_MEMORY;
  637.     }
  638.  
  639.     pNewEvent->eventTime = *pTime;
  640.     pNewEvent->pEventProc = function;
  641.     pNewEvent->pEventData = pData;
  642.  
  643.     if (szName != NULL) {
  644.         pNewEvent->eventName = PR_Malloc(strlen(szName) + 1);
  645.         strcpy(pNewEvent->eventName, szName);
  646.     }
  647.  
  648.     PR_Lock(pScheduler->pTimerLock);
  649.  
  650.     pNewEvent->eventID = pScheduler->nextID++;
  651.     
  652.     if (pEventID != NULL) {
  653.         *pEventID = pNewEvent->eventID;
  654.     }
  655.  
  656.     /* compute the firing time */
  657.     /* The first firing time is computed as follows: Select a random positive integer
  658.        within the range.  Multiply the number by 60 to obtain seconds,
  659.        then add that to the base time to get a time.  If the time is after the end time,
  660.        pin to the end time.  If it's before the start time, pin to the start time.*/
  661.     {
  662.         PRTime                fireTime;
  663.         PRInt32                random, randomSecs;
  664.         PRInt64                randomizer, us2s;
  665.  
  666.         if (pTime->range != 0) {
  667.             random = (PRInt32) rand();
  668.             randomSecs = random % pTime->range;
  669.  
  670.             LL_UI2L(us2s, PR_USEC_PER_SEC);
  671.             LL_I2L(randomizer, randomSecs);
  672.             LL_MUL(randomizer, randomizer, us2s);
  673.  
  674.             if ((random % 2) == 0) {
  675.                 LL_NEG(randomizer, randomizer);
  676.             }
  677.  
  678.             LL_ADD(fireTime, pTime->baseTime, randomizer);
  679.  
  680.         } else {
  681.             fireTime = pTime->baseTime;
  682.         }
  683.  
  684.         if ((LL_NE(LL_ZERO, pTime->end)) && (LL_CMP(fireTime, >, pTime->end))) {
  685.             pNewEvent->fireTime = pTime->end;
  686.         } else if (LL_CMP(fireTime, <, pTime->start)) {
  687.             pNewEvent->fireTime = pTime->start;
  688.         } else {
  689.             pNewEvent->fireTime = fireTime;
  690.         }
  691.     }
  692.  
  693.     /* insert the item in the proper place in the list */
  694.  
  695.     InsertEvent(pScheduler, pNewEvent);
  696.  
  697.     PR_NotifyCondVar(pScheduler->pTimerCVar);
  698.  
  699.     PR_Unlock(pScheduler->pTimerLock);
  700.  
  701.     /* notify observers */
  702.     NotifyObservers(pScheduler, SCHED_OBSERVE_ADD, pNewEvent->eventID, pNewEvent->eventName, pData);
  703.  
  704.     return err;
  705. }
  706.  
  707. PR_IMPLEMENT(SchedulerErr)
  708. SchedulerRemoveEvent(SchedulerPtr pArg, PRUint32 eventID)
  709. {
  710.     SchedulerData    *pScheduler = (SchedulerData *) pArg;
  711.     SchedEvent        *pEvent;
  712.     SchedulerErr    err = SCHED_ERR_BAD_EVENT;
  713.  
  714.     if (pScheduler == NULL) {
  715.         PR_ASSERT(pScheduler != NULL);
  716.  
  717.         return SCHED_ERR_INVALID_SCHEDULER;
  718.     }
  719.  
  720.     PR_Lock(pScheduler->pTimerLock);
  721.  
  722.     pEvent = EventFromID(pScheduler, eventID);
  723.  
  724.     if (pEvent != NULL) {
  725.  
  726.         PR_REMOVE_LINK(pEvent);
  727.  
  728.         if (LL_EQ(pScheduler->nextEventTime, pEvent->fireTime)) {
  729.  
  730.             /* the item we just removed was the next item to fire,
  731.                so reinitialize the firing time */
  732.  
  733.             if (PR_CLIST_IS_EMPTY(pScheduler->pEventQueue)) {
  734.                 pScheduler->nextEventTime = LL_ZERO;
  735.             } else {
  736.                 SchedEvent        *pNextEvent = (SchedEvent *) PR_LIST_HEAD(pScheduler->pEventQueue);
  737.  
  738.                 pScheduler->nextEventTime = pNextEvent->fireTime;
  739.             }
  740.  
  741.             PR_NotifyCondVar(pScheduler->pTimerCVar);
  742.         }
  743.         
  744.         err = SCHED_ERR_NOERR; 
  745.     }
  746.  
  747.     PR_Unlock(pScheduler->pTimerLock);
  748.  
  749.     if (pEvent != NULL) {
  750.  
  751.         /* notify observers */
  752.         NotifyObservers(pScheduler, SCHED_OBSERVE_REMOVE, pEvent->eventID, pEvent->eventName, pEvent->pEventData);
  753.  
  754.         /* free the event */
  755.         if (pEvent->eventName != NULL) {
  756.             PR_Free(pEvent->eventName);
  757.         }
  758.  
  759.         PR_DELETE(pEvent);
  760.     }
  761.  
  762.  
  763.     return err;
  764. }
  765.  
  766. PR_EXTERN(SchedulerErr)
  767. SchedulerAddObserver(SchedulerPtr pArg,
  768.                      PRUint32 *pObserverID,
  769.                      SchedObsType typeMask,
  770.                      SchedObsPtr function,
  771.                      SchedObsDataPtr pData)
  772. {
  773.     SchedulerData    *pScheduler = (SchedulerData *) pArg;
  774.     SchedObserver    *pNewObserver = NULL;
  775.  
  776.     if (pScheduler == NULL) {
  777.         PR_ASSERT(pScheduler != NULL);
  778.  
  779.         return SCHED_ERR_INVALID_SCHEDULER;
  780.     }
  781.  
  782.     if (function == NULL) {
  783.         return SCHED_ERR_BAD_PARAMETER;
  784.     }
  785.  
  786.     pNewObserver = PR_NEWZAP(SchedObserver);
  787.  
  788.     if (pNewObserver == NULL) {
  789.         return SCHED_ERR_OUT_OF_MEMORY;
  790.     }
  791.  
  792.     PR_Lock(pScheduler->pTimerLock);
  793.  
  794.     if (pScheduler->pObserverQueue == NULL) {
  795.         pScheduler->pObserverQueue = PR_Malloc(sizeof(PRCList));
  796.  
  797.         if (pScheduler->pObserverQueue == NULL) {
  798.             if (pNewObserver != NULL) {
  799.                 PR_Free(pNewObserver);
  800.             }
  801.  
  802.             PR_Unlock(pScheduler->pTimerLock);
  803.  
  804.             return SCHED_ERR_OUT_OF_MEMORY;
  805.         } else {
  806.             PR_INIT_CLIST(pScheduler->pObserverQueue);
  807.         }
  808.     }
  809.  
  810.     pNewObserver->observerID = pScheduler->nextObserver++;
  811.     
  812.     if (pObserverID != NULL) {
  813.         *pObserverID = pNewObserver->observerID;
  814.     }
  815.  
  816.     pNewObserver->typeMask = typeMask;
  817.     pNewObserver->pObserverProc = function;
  818.     pNewObserver->pObserverData = pData;
  819.  
  820.     PR_APPEND_LINK((PRCList *) pNewObserver, pScheduler->pObserverQueue);
  821.  
  822.     PR_Unlock(pScheduler->pTimerLock);
  823.  
  824.     return SCHED_ERR_NOERR;
  825. }
  826.  
  827.  
  828. PR_IMPLEMENT(SchedulerErr)
  829. SchedulerRemoveObserver(SchedulerPtr pArg, PRUint32 observerID)
  830. {
  831.     SchedulerData    *pScheduler = (SchedulerData *) pArg;
  832.     SchedObserver    *pObserver;
  833.     SchedulerErr    err = SCHED_ERR_BAD_PARAMETER;
  834.  
  835.     if (pScheduler == NULL) {
  836.         PR_ASSERT(pScheduler != NULL);
  837.  
  838.         return SCHED_ERR_INVALID_SCHEDULER;
  839.     }
  840.  
  841.     PR_Lock(pScheduler->pTimerLock);
  842.  
  843.     pObserver = ObserverFromID(pScheduler, observerID);
  844.  
  845.     if (pObserver != NULL) {
  846.         PR_REMOVE_LINK(pObserver);
  847.         PR_DELETE(pObserver);
  848.  
  849.         err = SCHED_ERR_NOERR; 
  850.     }
  851.  
  852.     PR_Unlock(pScheduler->pTimerLock);
  853.  
  854.     return err;
  855. }
  856.  
  857.  
  858. #ifdef DEBUG
  859.  
  860. PR_IMPLEMENT(void)
  861. Sched_FormatEventTime(char *output, int len, SchedulerTime *pTime)
  862. {
  863.     PRExplodedTime    exp_time;
  864.     char            scratch[100];
  865.     char            returnValue[1024];
  866.  
  867.     PR_ExplodeTime(pTime->baseTime, PR_LocalTimeParameters, &exp_time);
  868.     PR_FormatTime(scratch, 100, "%X on %x", &exp_time);
  869.     sprintf(returnValue, "%s (+/- %ld seconds)", scratch, pTime->range);
  870.  
  871.     if (pTime->repeating !=0) {
  872.         sprintf(scratch, "repeating every %d seconds,\n", pTime->repeating);
  873.     } else {
  874.         strcpy(scratch, "(nonrepeating),\n");
  875.     }
  876.  
  877.     strcat(returnValue, scratch);
  878.  
  879.     PR_ExplodeTime(pTime->start, PR_LocalTimeParameters, &exp_time);
  880.     PR_FormatTime(scratch, 100, "beginning at %X on %x\n", &exp_time);
  881.     strcat(returnValue, scratch);
  882.  
  883.     if (LL_IS_ZERO(pTime->end)) {
  884.         strcpy(scratch, "and continuing until cancelled");
  885.     } else {
  886.         PR_ExplodeTime(pTime->end, PR_LocalTimeParameters, &exp_time);
  887.         PR_FormatTime(scratch, 100, "and stopping at %X on %x\n", &exp_time);
  888.     }
  889.  
  890.     strcat(returnValue, scratch);
  891.  
  892.     strncpy(output, returnValue, len);
  893. }
  894.  
  895. #endif
  896.  
  897.