home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1998 / MacHack 1998.toast / Papers / C++ Exceptions / µShell / Threads Support / Semaphores.cp < prev    next >
Encoding:
Text File  |  1998-05-25  |  22.2 KB  |  689 lines  |  [TEXT/CWIE]

  1. #include "Semaphores.h"
  2. #include "TimeUtils.h"
  3. #include <AppleEvents.h>
  4. #include <AERegistry.h>
  5. #include <Errors.h>
  6.  
  7. //========================================================================================
  8. // CLASS TThreadIDQueue
  9. //========================================================================================
  10.  
  11. const short  kThreadQueueAllocSize = 4;
  12.  
  13. //----------------------------------------------------------------------------------------
  14. // TThreadIDQueue::~TThreadIDQueue
  15. //----------------------------------------------------------------------------------------
  16. TThreadIDQueue::~TThreadIDQueue()
  17. {
  18.     if(fThreadQueue != nil)
  19.     {
  20.         DisposeHandle((Handle)fThreadQueue);
  21.         fThreadQueue = nil;
  22.     }
  23. } // TThreadIDQueue::~TThreadIDQueue
  24.  
  25. //----------------------------------------------------------------------------------------
  26. // TThreadIDQueue::Enqueue
  27. //----------------------------------------------------------------------------------------
  28. OSErr TThreadIDQueue::Enqueue(ThreadID threadToAdd)
  29. {
  30.     OSErr err = this->InsureThreadQueueHasFreeSpace();
  31.     if(err == noErr)
  32.     {
  33.         (*fThreadQueue)[fThreadCount] = threadToAdd;
  34.         ++fThreadCount;
  35.     }
  36.     return err;
  37. } // TThreadIDQueue::Enqueue
  38.  
  39. //----------------------------------------------------------------------------------------
  40. // TThreadIDQueue::Dequeue
  41. //----------------------------------------------------------------------------------------
  42. ThreadID TThreadIDQueue::Dequeue()
  43. {
  44.     ThreadID resultThreadID = kNoThreadID;
  45.  
  46.     if(fThreadCount > 0)
  47.     {
  48.         resultThreadID = (*fThreadQueue)[0];
  49.         --fThreadCount;
  50.         for(long i=0;i<fThreadCount;++i)
  51.             (*fThreadQueue)[i] = (*fThreadQueue)[i + 1];
  52.         (*fThreadQueue)[fThreadCount] = kNoThreadID;
  53.     }
  54.  
  55.     return resultThreadID;
  56. } // TThreadIDQueue::Dequeue
  57.  
  58. //----------------------------------------------------------------------------------------
  59. // TThreadIDQueue::InsureThreadQueueHasFreeSpace
  60. //----------------------------------------------------------------------------------------
  61. OSErr TThreadIDQueue::InsureThreadQueueHasFreeSpace()
  62. {
  63.     OSErr err = noErr;
  64.     
  65.     if(fThreadQueue == nil)
  66.     {
  67.         if((fThreadCount > 0) || (fReservedSpace > 0))
  68.             err = errAEEventFailed;
  69.         else
  70.         {
  71.             fThreadQueue = (ThreadID**) NewHandle( sizeof(ThreadID) * kThreadQueueAllocSize );
  72.             err = MemError();
  73.             if(err == noErr)
  74.                 fReservedSpace = kThreadQueueAllocSize;
  75.         }
  76.     }
  77.     else if(fThreadCount >= fReservedSpace)
  78.     {
  79.         if(fThreadCount > fReservedSpace)
  80.             err = errAEEventFailed;
  81.         else
  82.         {
  83.             SetHandleSize((Handle)fThreadQueue, sizeof(ThreadID) * (kThreadQueueAllocSize + fReservedSpace));
  84.             err = MemError();
  85.             if(err == noErr)
  86.                 fReservedSpace += kThreadQueueAllocSize;
  87.         }
  88.     }
  89.     
  90.     return err;
  91. } // TThreadIDQueue::InsureThreadQueueHasFreeSpace
  92.  
  93. //========================================================================================
  94. // CLASS TSemaphore
  95. //========================================================================================
  96.  
  97. TSemaphore*        TSemaphore::fgFirstSemaphore            = nil;
  98.  
  99. long            TSemaphore::fgSemaphoreIDSequence        = kAnySemaphoreID + 1;
  100. long            TSemaphore::fgSemaphoreDefaultTimeout    = kNeverTimeoutSemaphore;
  101. long            TSemaphore::fgSemaphoreDefaultMaxWait    = kNeverTimeoutSemaphore;
  102.  
  103. unsigned long    TSemaphore::fgLastTimeoutTest            = 0;
  104. unsigned long    TSemaphore::fgLastIdleTick                = 0;
  105. unsigned long    TSemaphore::fgNextTimeoutTest            = 1;
  106.  
  107. //----------------------------------------------------------------------------------------
  108. // TSemaphore::TSemaphore
  109. //
  110. // Add the semaphore to a global list so we can look up semaphores later by ID.
  111. //----------------------------------------------------------------------------------------
  112. TSemaphore::TSemaphore(long resourceCount /* = 1 */, long semaphoreID /* = kAnySemaphoreID*/)
  113. {
  114.     if (semaphoreID == kAnySemaphoreID)
  115.         semaphoreID = TSemaphore::NewUniqueSemaphoreID();
  116.     
  117.     fResourceCount = resourceCount;
  118.     fSemaphoreID = semaphoreID;
  119.  
  120.     //
  121.     // We have one reference (the person who created the semaphore)
  122.     // but no owner yet
  123.     //
  124.     fReferenceCount = 1;
  125.     fOwnerThread = kNoThreadID;
  126.     fOwnerCount = 0;
  127.     fDisposed = false;
  128.     fFailOnWakeup = noErr;
  129.     
  130.     fSemaphoreTimeoutValue = fgSemaphoreDefaultTimeout;
  131.     fSemaphoreMaxWaitTime = fgSemaphoreDefaultMaxWait;
  132.     fBlockStartTick = TickCount();
  133.     this->ResetTimeoutTimer();
  134.     
  135.     fNextSemaphore = fgFirstSemaphore;
  136.     fPreviousSemaphore = nil;
  137.     if(fNextSemaphore != nil)
  138.         fNextSemaphore->fPreviousSemaphore = this;
  139.     fgFirstSemaphore = this;
  140. }
  141.  
  142. //----------------------------------------------------------------------------------------
  143. // TSemaphore::~TSemaphore: 
  144. //
  145. // First, remove ourselves from the global linked list of all semaphores.  Then,
  146. // wake up all threads that are still blocked on us before we go away so they won’t
  147. // be stranded, asleep forever.
  148. //----------------------------------------------------------------------------------------
  149. TSemaphore::~TSemaphore()
  150. {
  151.     this->RemoveFromGlobalSemaphoreList();
  152. }
  153.  
  154. //----------------------------------------------------------------------------------------
  155. // TSemaphore::InitializeGlobals
  156. //----------------------------------------------------------------------------------------
  157. void TSemaphore::InitializeGlobals()
  158. {
  159.     fgFirstSemaphore            = nil;
  160.  
  161.     fgSemaphoreIDSequence        = kAnySemaphoreID + 1;
  162.     fgSemaphoreDefaultTimeout    = kNeverTimeoutSemaphore;
  163.     fgSemaphoreDefaultMaxWait    = kNeverTimeoutSemaphore;
  164.  
  165.     fgLastTimeoutTest            = 0;
  166.     fgLastIdleTick                = 0;
  167.     fgNextTimeoutTest            = 1;
  168. }
  169.  
  170. //----------------------------------------------------------------------------------------
  171. // TSemaphore::RemoveFromGlobalSemaphoreList
  172. //----------------------------------------------------------------------------------------
  173. void TSemaphore::RemoveFromGlobalSemaphoreList()
  174. {
  175.     if((fPreviousSemaphore != nil) || (fNextSemaphore != nil) || (fgFirstSemaphore == this))
  176.     {
  177.         //
  178.         // Either gFirstSemaphore == this or fPreviousSemaphore != nil
  179.         //
  180.         if (fgFirstSemaphore == this)
  181.             fgFirstSemaphore = fNextSemaphore;
  182.         else if (fPreviousSemaphore != nil)
  183.             fPreviousSemaphore->fNextSemaphore = fNextSemaphore;
  184.         else
  185.             DebugStr("\pRemoveFromGlobalSemaphoreList semaphore list corrupt");
  186.         
  187.         if (fNextSemaphore != nil)
  188.             fNextSemaphore->fPreviousSemaphore = fPreviousSemaphore;
  189.         
  190.         fPreviousSemaphore = nil;
  191.         fNextSemaphore = nil;
  192.     }
  193. }
  194.  
  195. //----------------------------------------------------------------------------------------
  196. // TSemaphore::ReleaseReference
  197. //
  198. // Delete this semaphore iff there are no references to it.
  199. // Usually, clients should call either 'Dispose' (the owner of the semaphore should
  200. // dispose it) or 'Release' (any thread that grabs a semaphore should release it).
  201. //----------------------------------------------------------------------------------------
  202. void TSemaphore::ReleaseReference()
  203. {
  204.     --fReferenceCount;
  205.     //
  206.     // By the time our reference count goes to zero, fDisposed should
  207.     // be true and we should no longer be in the global semaphore list.
  208.     //
  209. //    if(fReferenceCount == 0)
  210. //        delete this;
  211. } // TSemaphore::ReleaseReference
  212.  
  213. //----------------------------------------------------------------------------------------
  214. // TSemaphore::Dispose
  215. //
  216. // Delete this semaphore iff there are no threads blocked on it.  If there are
  217. // blocked threads, then wait until the threads all wake up before disposing this
  218. // semaphore.
  219. //----------------------------------------------------------------------------------------
  220. void TSemaphore::Dispose()
  221. {
  222.     this->ReleaseAllThreads();
  223.     this->RemoveFromGlobalSemaphoreList();
  224.     fDisposed = true;
  225.     this->ReleaseReference();
  226. }
  227.  
  228. //----------------------------------------------------------------------------------------
  229. // TSemaphore::NewUniqueSemaphoreID: 
  230. // A static method to generate a unique ID for a semaphore.  Unique IDs are generated 
  231. // simply by starting at zero and counting up.
  232. //----------------------------------------------------------------------------------------
  233. long TSemaphore::NewUniqueSemaphoreID()
  234. {
  235.     ++fgSemaphoreIDSequence;
  236.     return fgSemaphoreIDSequence;
  237. }
  238.  
  239.  
  240. //----------------------------------------------------------------------------------------
  241. // TSemaphore::FindSemaphore: 
  242. //----------------------------------------------------------------------------------------
  243. TSemaphore*    TSemaphore::FindSemaphore(long semaphoreID, Boolean createIfNotFound /* = false */, long resourceCount /* = 1 */)
  244. {
  245.     TSemaphore* resultSemaphore = fgFirstSemaphore;
  246.     
  247.     if(fgFirstSemaphore != nil)
  248.         if(fgFirstSemaphore->fPreviousSemaphore != nil)
  249.             DebugStr("\pFirst semaphore in list corrupt!");
  250.     
  251.     while ((resultSemaphore != nil) && (resultSemaphore->fSemaphoreID != semaphoreID))
  252.     {
  253.         if(resultSemaphore->fNextSemaphore != nil)
  254.             if(resultSemaphore->fNextSemaphore->fPreviousSemaphore != resultSemaphore)
  255.                 DebugStr("\pSemaphore linked list corrupt!");
  256.         
  257.         resultSemaphore = resultSemaphore->fNextSemaphore;
  258.     }
  259.     
  260.     if((resultSemaphore == nil) && (createIfNotFound == true))
  261.         resultSemaphore = new TSemaphore(resourceCount, semaphoreID);
  262.     
  263.     return resultSemaphore;
  264. }
  265.     
  266.  
  267. //----------------------------------------------------------------------------------------
  268. // TSemaphore::Idle
  269. //
  270. // Call Periodicly to test for blocked semaphores whose time has expired
  271. //----------------------------------------------------------------------------------------
  272. void TSemaphore::Idle()
  273. {
  274.     unsigned long currentTime = TickCount();
  275.     long semaphoreCount = 0;
  276.     long gracePeriod = currentTime - fgLastIdleTick;
  277.     
  278.     //
  279.     // If we have been away for a long time, then allow clients
  280.     // a grace period; the idea being, that the user or some
  281.     // selfish process probably locked up the machine for a long
  282.     // time, so it's likely that no one (client or server) got
  283.     // any work done.  Even if the server is on some other machine,
  284.     // we still want to add in the grace period; it would be really
  285.     // bad to time out after a long lock-out if the reply was sitting
  286.     // in the AppleEvent manager's queue, waiting for some time to
  287.     // be processed.
  288.     //
  289.     // If we weren't gone for at least half a second, though, then we'll
  290.     // assume that the machine is running just fine.
  291.     //
  292.     if((gracePeriod < 30) || (fgLastIdleTick == 0))
  293.         gracePeriod = 0;
  294.     fgLastIdleTick = currentTime;
  295.     
  296.     //
  297.     // Don't walk the semaphore list until it's time
  298.     //
  299.     if(TimeExpired(currentTime, fgLastTimeoutTest, fgNextTimeoutTest))
  300.     {
  301.         // DebugStr("\pTSemaphore::Idle decided to do work");
  302.         
  303.         //
  304.         // Reset gLastTimeoutTest to currentTime to record that this
  305.         // is the time that we did the last test.  Reset gNextTimeoutTest
  306.         // to be one tick before that, the longest time we could
  307.         // possably wait before doing another test (forever!).  In the
  308.         // "forever" case, gNextTimeoutTest will be reset when new
  309.         // semaphores are created.
  310.         //
  311.         fgLastTimeoutTest = currentTime;
  312.         fgNextTimeoutTest = currentTime - 1;
  313.         
  314.         TSemaphore* semaphore = fgFirstSemaphore;
  315.         
  316.         while(semaphore != nil)
  317.         {
  318.             ++semaphoreCount;
  319.             TSemaphore* nextSemaphore = semaphore->fNextSemaphore;
  320.             
  321.             //
  322.             // If we've been away for a while, then allow the
  323.             // semaphore to adjust itself.
  324.             //
  325.             if(gracePeriod)
  326.                 semaphore->AddGracePeriod(gracePeriod);
  327.             
  328.             if(semaphore->CheckIfTimerExpired(currentTime) == false)
  329.             {
  330.                 // DebugStr("\pFound a semaphore that has not timed out");
  331.                 
  332.                 //
  333.                 // For next time through the loop:  test to see
  334.                 // if the timeout tick for this semaphore is going
  335.                 // to happen sooner than any other timeout value
  336.                 // we've calculated yet.  Doing this test now will
  337.                 // let us avoid walking the list of semaphores until
  338.                 // it's time for one of them to expire.  We don't
  339.                 // care if the next-in-line semaphore goes away
  340.                 // before we test again; this is just a best-case
  341.                 // estimate.
  342.                 //
  343.                 fgNextTimeoutTest = CloserTick(currentTime, fgNextTimeoutTest, semaphore->TimeoutTick());
  344.             }
  345.             else
  346.             {
  347.                 //
  348.                 // If the semaphore's time has come, cancel the threads
  349.                 // that are blocked on it.
  350.                 //
  351.                 semaphore->SemaphoreTimedOut();
  352.             }
  353.             
  354.             semaphore = nextSemaphore;
  355.         }
  356.     }
  357. }
  358.  
  359. //----------------------------------------------------------------------------------------
  360. // TSemaphore::ThreadDied
  361. //
  362. // If a thread is killed from some other thread, and the killed thread might
  363. // be the owner of some semaphore, then the killer must call this routine so
  364. // that the semaphore can be released.
  365. //----------------------------------------------------------------------------------------
  366. void TSemaphore::ThreadDied(ThreadID threadID)
  367. {
  368.     TSemaphore* semaphore = fgFirstSemaphore;
  369.     while(semaphore != nil)
  370.     {
  371.         TSemaphore* nextSemaphore = semaphore->fNextSemaphore;
  372.         
  373.         //
  374.         // Release this semaphore iff it was owned by the thread that died
  375.         //
  376.         semaphore->Release(threadID);
  377.         semaphore = nextSemaphore;
  378.     }
  379. }
  380.  
  381. //----------------------------------------------------------------------------------------
  382. // TSemaphore::Grab: 
  383. //
  384. // If the specified thread does not already have ownership of this semaphore, enqueue it.
  385. // If other threads are already waiting, put the thread to sleep (to be woken up when it
  386. // gets to the head of the queue).  By default, the thread grabbing the semaphore is the
  387. // current thread.
  388. //----------------------------------------------------------------------------------------
  389. OSErr TSemaphore::Grab(ThreadID grabbingThread /* = kCurrentThreadID */)
  390. {
  391.     OSErr err = noErr;
  392.  
  393.     //
  394.     // Every call to 'Grab' should be balanced by a call to 'release'
  395.     //
  396.     ++fReferenceCount;
  397.     
  398.     //
  399.     // This semaphore isn't good for much once it's been disposed.
  400.     // Similarly, if a semaphore has timed out and is causing all
  401.     // of its blocked threads to wake up (fFailOnWakeup != noErr),
  402.     // then we shouldn't let new threads in either.
  403.     //
  404.     if(fFailOnWakeup != noErr)
  405.         err = fFailOnWakeup;
  406.     else if(fDisposed)
  407.         err = errAEEventFailed;
  408.     else
  409.     {    
  410.         //
  411.         // By default the current thread grabs the semaphore
  412.         //
  413.         if (grabbingThread == kCurrentThreadID)
  414.             GetCurrentThread(&grabbingThread);
  415.  
  416.         //
  417.         // Short-circuit if we already own the semaphore
  418.         // (Note:  if the semaphore sontrolled multiple
  419.         // resources, then it may be possible for one thread
  420.         // to grab the same resource more than once.)
  421.         //
  422.         if (grabbingThread == fOwnerThread)
  423.             return noErr;
  424.         
  425.         //
  426.         // If the semaphore is available, then we'll let this thread
  427.         // grab it.
  428.         //    
  429.         if (fOwnerCount < fResourceCount)
  430.         {
  431.             //
  432.             // There shouldn’t be an owner if we’re about to grab it;
  433.             // set the owner, and mark the semaphore as being unavailable
  434.             //
  435.             // Assert(fOwnerThread == kNoThreadID);
  436.             fOwnerThread = grabbingThread;
  437.             ++fOwnerCount;
  438.         }
  439.         //
  440.         // If the semaphore isn't available, then block the grabbing thread
  441.         //
  442.         else    
  443.         {
  444.             //
  445.             // Enqueue the thread, increment the count of threads blocked
  446.             // on this semaphore (often different than the count of threads
  447.             // queued, since at wake-up time threads will be dequeued and
  448.             // woken up, but won't fall out of 'SetThreadState' until the
  449.             // next time they're scheduled)
  450.             //
  451.             err = fBlockedThreads.Enqueue(grabbingThread);
  452.             if(err == noErr)
  453.                 err = SetThreadState(grabbingThread, kStoppedThreadState, kNoThreadID);
  454.             
  455.             //
  456.             // If someone has set our automatic-failure signal, then
  457.             // make note of it; this error should take precedence over
  458.             // any returned by 'SetThreadState'.
  459.             //
  460.             if(fFailOnWakeup != noErr)
  461.             {
  462.                 err = fFailOnWakeup;
  463.             }
  464.         }
  465.     }
  466.     
  467.     return err;
  468. }
  469.  
  470. //----------------------------------------------------------------------------------------
  471. // TSemaphore::Release: 
  472. //
  473. // Remove the reference that this thread has to this semaphore
  474. //----------------------------------------------------------------------------------------
  475. void TSemaphore::Release(ThreadID threadID /* = kCurrentThreadID */)
  476. {
  477.     //
  478.     // If no thread is specified, then assume that the current thread
  479.     // is being released.
  480.     //
  481.     if (threadID == kCurrentThreadID)
  482.         GetCurrentThread(&threadID);
  483.  
  484.     //
  485.     // If we own this semaphore, then let someone else have it
  486.     //
  487.     // ••• We should require that the thread being released is
  488.     // either the owner or in the list of blocked threads.  If
  489.     // it is in the list of blocked threads, then we should wake
  490.     // it up if possible.
  491.     //
  492.     if(fOwnerThread == threadID)
  493.     {
  494.         fOwnerThread = this->ReleaseOneThread();
  495.     }
  496.     
  497.     //
  498.     // Decrement our reference count
  499.     //
  500.     this->ReleaseReference();
  501. }
  502.  
  503. //----------------------------------------------------------------------------------------
  504. // TSemaphore::ReleaseOneThread: 
  505. //
  506. // If the thread queue is non-empty, wake up the thread that is now at the head of the
  507. // queue.  If the queue is empty, do nothing.
  508. //----------------------------------------------------------------------------------------
  509. ThreadID TSemaphore::ReleaseOneThread()
  510. {
  511.     ThreadID releasedThread = kNoThreadID;
  512.     
  513.     //
  514.     // Probably not necessary, but let's make sure that this
  515.     // semaphore is never deleted while we're working with it.
  516.     //
  517.     ++fReferenceCount;
  518.     
  519.     //
  520.     // Keep working until we find a thread to wake up,
  521.     // or we run out of threads that are blocked on us
  522.     //
  523.     while(fBlockedThreads.QueuedThreads() > 0)
  524.     {
  525.         //
  526.         // Take the first thread waiting in line and wake it up
  527.         //
  528.         releasedThread = fBlockedThreads.Dequeue();
  529.         
  530.         //
  531.         // If we have a thread, try to wake it up.  Stop working
  532.         // if we can; if we can't wake it up (maybe someone killed
  533.         // it already), then try to pop off the next thread in the list.
  534.         //
  535.         if (releasedThread != kNoThreadID)
  536.         {
  537.             //
  538.             // If the thread can't be woken up, then it will never
  539.             // fall out from TSemaphore::Grab; in that case, decrement
  540.             // the count of blocked threads.
  541.             //
  542.             if(SetThreadState(releasedThread, kReadyThreadState, kNoThreadID) == noErr)
  543.                 break;
  544.             else
  545.                 this->ReleaseReference();
  546.         }
  547.     }
  548.     
  549.     //
  550.     // Release the reference we grabbed
  551.     //
  552.     this->ReleaseReference();
  553.     
  554.     return releasedThread;
  555. }
  556.  
  557. //----------------------------------------------------------------------------------------
  558. // TSemaphore::ReleaseAllThreads: 
  559. // Release everyone who’s blocked on us by simply releasing threads until no one is left.
  560. //----------------------------------------------------------------------------------------
  561. void TSemaphore::ReleaseAllThreads()
  562. {
  563.     //
  564.     // Probably not necessary, but let's make sure that this
  565.     // semaphore is never deleted while we're working with it.
  566.     //
  567.     ++fReferenceCount;
  568. //    while (fBlockedThreads.QueuedThreads() > 0)
  569.         this->ReleaseOneThread();
  570.     
  571.     //
  572.     // Release the reference we grabbed
  573.     //
  574.     this->ReleaseReference();
  575. }
  576.  
  577. //----------------------------------------------------------------------------------------
  578. // TSemaphore::CheckIfTimerExpired
  579. //
  580. // CurrentTime is just 'TickCount', but since this routine is always called from a
  581. // loop, I didn't think there was any point in calling it over and over again.
  582. //
  583. // If the timer has expired, then set fFailOnWakeup to errAETimeout and wake up
  584. // all of the blocked threads.
  585. //----------------------------------------------------------------------------------------
  586. Boolean TSemaphore::CheckIfTimerExpired(unsigned long currentTime)
  587. {    
  588.     //
  589.     // In order for our timer to run out, one of fSemaphoreTimeoutValue
  590.     // or fSemaphoreMaxWaitTime must be something other than kNeverTimeoutSemaphore,
  591.     // AND the time must have expired for this semaphore.
  592.     //
  593.     return (    ((fSemaphoreTimeoutValue != kNeverTimeoutSemaphore) || (fSemaphoreMaxWaitTime != kNeverTimeoutSemaphore))
  594.                 && TimeExpired(fBlockStartTick, fTimeoutTick, currentTime)    );
  595. }
  596.  
  597. //----------------------------------------------------------------------------------------
  598. // TSemaphore::SemaphoreTimedOut
  599. //
  600. // If the timer expired, deal with it.
  601. //----------------------------------------------------------------------------------------
  602. void TSemaphore::SemaphoreTimedOut()
  603. {
  604.     // DebugStr("\pSemaphore timed out!  Wake up blocked threads");
  605.     
  606.     fFailOnWakeup = errAETimeout;
  607.     this->ReleaseAllThreads();
  608. }
  609.  
  610. //----------------------------------------------------------------------------------------
  611. // TSemaphore::AddGracePeriod
  612. //
  613. // Acount for the fact that the machine may have been locked up for a while by
  614. // user actions or what-have-you.
  615. //----------------------------------------------------------------------------------------
  616. void TSemaphore::AddGracePeriod(long gracePeriod)
  617. {
  618.     //
  619.     // Pretend that our timer was reset 'gracePeriod' ticks
  620.     // later than it actually was.
  621.     //
  622.     fTimeoutTick += gracePeriod;
  623. } // TSemaphore::AddGracePeriod
  624.  
  625. //----------------------------------------------------------------------------------------
  626. // TSemaphore::ResetTimeoutTimer
  627. //
  628. // Reset the timer, but don't allow it to go past 'maxTimoutValue'.
  629. //----------------------------------------------------------------------------------------
  630. void TSemaphore::ResetTimeoutTimer()
  631. {
  632.     unsigned long currentTime = TickCount();
  633.     unsigned long newTimeoutValue = fSemaphoreTimeoutValue;
  634.         
  635.     //
  636.     // Pin the timeout value rather than the timeout tick,
  637.     // so we don't get messed up by overflow problems.
  638.     //
  639.     // Note that if we have passed '(fBlockStartTick + fSemaphoreMaxWaitTime)',
  640.     // then maxTimeoutValue will be negative.  This is what we
  641.     // want:  the semaphore should time out on the next call to
  642.     // 'CheckIfTimerExpired'.
  643.     //
  644.     if(fSemaphoreMaxWaitTime != kNeverTimeoutSemaphore)
  645.     {
  646.         unsigned long maxTimeoutValue = (fBlockStartTick + fSemaphoreMaxWaitTime) - currentTime;
  647.         if(newTimeoutValue > maxTimeoutValue)
  648.             newTimeoutValue = maxTimeoutValue;
  649.     }
  650.     
  651.     fTimeoutTick = currentTime + newTimeoutValue;
  652.     
  653.     //
  654.     // Recalculate the next time that we're going to test
  655.     // for Semaphore timeouts (just in case gNextTimeoutTest
  656.     // is off on the 'forever' side of gLastTimeoutTest).
  657.     //
  658.     fgNextTimeoutTest = CloserTick(currentTime, fgNextTimeoutTest, fTimeoutTick);
  659. }
  660.  
  661. //----------------------------------------------------------------------------------------
  662. // TSemaphore::SetSemaphoreTimoutValue
  663. //
  664. // Specify some timeout value other than the default for this semaphore.
  665. //
  666. // Don't call 'SetSemaphoreTimoutValue' at arbitrary times, because it also resets the
  667. // timeout timer.
  668. //----------------------------------------------------------------------------------------
  669. void TSemaphore::SetSemaphoreTimoutValue(long timeoutValue)
  670. {
  671.     fSemaphoreTimeoutValue = timeoutValue;
  672.     this->ResetTimeoutTimer();
  673. }
  674.  
  675. //----------------------------------------------------------------------------------------
  676. // TSemaphore::SetSemaphoreMaxWaitTime
  677. //
  678. // Specify some maximum wait time other than the default for this semaphore.
  679. //
  680. // Don't call 'SetSemaphoreMaxWaitTime' at arbitrary times, because it also resets the
  681. // timeout timer.
  682. //----------------------------------------------------------------------------------------
  683. void TSemaphore::SetSemaphoreMaxWaitTime(long timeoutValue)
  684. {
  685.     fSemaphoreMaxWaitTime = timeoutValue;
  686.     this->ResetTimeoutTimer();
  687. }
  688.  
  689.