home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1998 / MacHack 1998.toast / Papers / C++ Exceptions / µShell / Threads Support / Futures.cp < prev    next >
Encoding:
Text File  |  1998-06-02  |  25.2 KB  |  734 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        Futures.c
  3.  
  4.     Contains:    Futures package
  5.  
  6.     This code goes to pains to NOT use the exception handler, and therefore also
  7.     avoids using AppleEventUtilities.cp, so that it will not be dependant on the
  8.     need to switch out gExceptionStack on every thread context switch, and therefore
  9.     should be able to run in conjunction with custom scheduler and custom thread
  10.     swap routines without need for modification.
  11.     
  12.     The futures package will spawn new threads in two places:  first, at InitFutures
  13.     time, a new thread may be spawned to call IdleFutures Periodicly.  Second,
  14.     the predispatch handler, if installed, will fork a new thread every time an
  15.     AppleEvent is dispatched.
  16. */
  17.  
  18.  
  19. #include "Futures.h"
  20. #include "Semaphores.h"
  21. #include "TimeUtils.h"
  22.  
  23. #ifndef __APPLEEVENTS__
  24. #include <AppleEvents.h>
  25. #endif
  26.  
  27. //
  28. // Keywords for special handlers not documented
  29. // in Inside Macintosh
  30. //
  31. #define keyAEBlock                    'blck'
  32. #define keyAEUnblock                'unbk'
  33.  
  34. //
  35. // Message ID of the event sent by AEResetTimer
  36. //
  37. #define kAEWaitLonger                'wait'
  38.  
  39. //
  40. // Random, arbitrarily choosen keyword 
  41. // for a parameter that probably doesn't exist.
  42. //
  43. #define keyNonexistantParameter        'nonx'
  44.  
  45. //
  46. // Structure used just to pass parameters to the "RedispatchEvent" thread
  47. //
  48. struct AsyncPredispatchParameters
  49. {
  50.     AppleEvent                        fAppleEvent;
  51.     AppleEvent                        fReply;
  52.     AEEventHandlerUPP                fEventHandler;
  53.     long                            fHandlerRefCon;
  54. };
  55.  
  56. typedef struct AsyncPredispatchParameters AsyncPredispatchParameters;
  57.  
  58. #if GENERATINGCFM
  59.  
  60. //
  61. // The block and unblock special handlers are not
  62. // documented, so they do not have ProcInfo descriptions
  63. // for their callback in the Universal headers.  Both
  64. // the block and the unblock routine take a single parameter:
  65. // the AppleEvent that is being blocked on / unblocked.
  66. //
  67. enum
  68. {
  69.     uppAEBlockUnblockProcInfo = kPascalStackBased
  70.          | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
  71.          | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(AEDesc*)))
  72. };
  73.  
  74. #endif
  75.  
  76. //
  77. // Private constants:
  78. //
  79. #define kCreateSemaphoreIfNotFound            true
  80. #define kDontCreateSemaphoreIfNotFound        false
  81.  
  82.  
  83. //
  84. // Private functions:
  85. //
  86. static OSErr NewFuturesThread(ThreadEntryProcPtr threadEntry, void *threadParam, long handlerRefCon, ThreadID *threadMade);
  87. static TSemaphore* GetFutureSemaphore(AppleEvent* reply, Boolean createIfNotFound);
  88. static pascal OSErr AEBlock(AppleEvent* reply);
  89. static pascal OSErr AEUnblock(AppleEvent* reply);
  90. static pascal OSErr WaitLongerEvent(AppleEvent* ae, AppleEvent* reply, long refCon);
  91. static pascal OSErr AsyncPreDispatchHandler(AppleEvent* ae, AppleEvent* reply, long refCon);
  92. static void RedispatchEvent(void* threadParam);
  93. static OSErr GetAEMHandlerUPPFromOneTable(AEEventClass theAEEventClass, AEEventID theAEEventID, AEEventHandlerUPP *handler, long *handlerRefcon, Boolean isSysHandler);
  94. static OSErr GetAppleEventHandlerUPP(AppleEvent* ae, AEEventHandlerUPP *handler, long *handlerRefCon);
  95. static long PrivateFuturesThread(long threadParam);
  96. static void FillInDefaultTimeoutValues(long& timeout);
  97.  
  98.  
  99. #if GENERATINGCFM
  100.  
  101. static RoutineDescriptor gAEBlockRD                        = BUILD_ROUTINE_DESCRIPTOR(uppAEBlockUnblockProcInfo, AEBlock);
  102. static RoutineDescriptor gAEUnblockRD                    = BUILD_ROUTINE_DESCRIPTOR(uppAEBlockUnblockProcInfo, AEUnblock);
  103. static RoutineDescriptor gWaitLongerEventRD                = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, WaitLongerEvent);
  104. static RoutineDescriptor gAsyncPreDispatchRD            = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, AsyncPreDispatchHandler);
  105.  
  106. #endif // GENERATINGCFM
  107.  
  108. //
  109. // Globals:
  110. //
  111. ThreadCreateProcPtr gThreadCreateProc                    = nil;
  112.  
  113. //----------------------------------------------------------------------------------------
  114. // InitFutures: 
  115. //
  116. // Install the block and unblock routines.
  117. //
  118. // If spawnHousekeepingThread is true, then a thread will be forked to idle the
  119. // Futures package.  If you pass false for this boolean, your code must call
  120. // IdleFutures Periodicly.
  121. //
  122. // If installAsyncPreDispatchHandler is true, then the futures package will
  123. // install a predispatch handler that automatically forks a new thread every
  124. // time AEProcessAppleEvents is called, so that all of your application's
  125. // event handlers may be processed asynchronously.
  126. //----------------------------------------------------------------------------------------
  127. OSErr InitFutures(ThreadCreateProcPtr threadCreateProc, long initFuturesFlags)
  128. {
  129.     ThreadID futuresHousekeepingThreadID = kNoThreadID;
  130.     OSErr err = noErr;
  131.  
  132.     TSemaphore::InitializeGlobals();
  133.     
  134.     gThreadCreateProc = threadCreateProc;
  135.     
  136. #if GENERATINGCFM
  137.     if(err == noErr)
  138.         err = AEInstallSpecialHandler(keyAEBlock, &gAEBlockRD, false);
  139.     if(err == noErr)
  140.         err = AEInstallSpecialHandler(keyAEUnblock, &gAEUnblockRD, false);
  141.     if(err == noErr)
  142.         err = AEInstallEventHandler(kCoreEventClass, kAEWaitLonger, &gWaitLongerEventRD, 0, false);
  143. #else
  144.     if(err == noErr)
  145.         err = AEInstallSpecialHandler(keyAEBlock, (UniversalProcPtr) AEBlock, false);
  146.     if(err == noErr)
  147.         err = AEInstallSpecialHandler(keyAEUnblock, (UniversalProcPtr) AEUnblock, false);
  148.     if(err == noErr)
  149.         err = AEInstallEventHandler(kCoreEventClass, kAEWaitLonger, (AEEventHandlerProcPtr) &WaitLongerEvent, 0, false);
  150. #endif
  151.     
  152.     //
  153.     // Create the housekeeping thread.
  154.     // We could probably use a very small stack for this thread.
  155.     //
  156.     if((err == noErr) && (initFuturesFlags & kSpawnHousekeepingThread))
  157.         err = NewFuturesThread((ThreadEntryProcPtr)PrivateFuturesThread, 0, 0, &futuresHousekeepingThreadID);
  158.     
  159.     //
  160.     // If an async pre-dispatch handler was requested, then install it.
  161.     //
  162.     if((err == noErr) && (initFuturesFlags & kInstallAsyncPreDispatchHandler))
  163.     {
  164. #if GENERATINGCFM
  165.         err = AEInstallSpecialHandler(keyPreDispatch, &gAsyncPreDispatchRD, false);
  166. #else
  167.         err = AEInstallSpecialHandler(keyPreDispatch, (UniversalProcPtr)AsyncPreDispatchHandler, false);
  168. #endif        
  169.     }
  170.     
  171.     return err;
  172. } // InitFutures 
  173.  
  174. //----------------------------------------------------------------------------------------
  175. // BlockUntilReal: 
  176. //
  177. // Block the current thread until the specified message becomes real.  This function
  178. // works by accessing a nonexistant parameter of the reply; the AppleEvent manager will
  179. // block any access to the reply, since (obviously) it doesn't know what the parameters
  180. // of the reply will be until the reply actually arrives.
  181. //----------------------------------------------------------------------------------------
  182. void BlockUntilReal(AppleEvent* reply)
  183. {
  184.     long            nonExistentParameterStorage;
  185.     long            actualSize;
  186.     DescType        typeCode;
  187.  
  188.     //
  189.     // Ask for the non existent parameter; this will cause the AppleEvent
  190.     // manager to block on the event
  191.     //
  192.     AEGetParamPtr(reply, keyNonexistantParameter, typeLongInteger, &typeCode, (Ptr) &nonExistentParameterStorage, sizeof(long), &actualSize);
  193. } // BlockUntilReal 
  194.  
  195. //----------------------------------------------------------------------------------------
  196. // ReplyArrived
  197. //
  198. // Return 'true' if the reply can be accessed without blocking, or false if it
  199. // is still a future
  200. //----------------------------------------------------------------------------------------
  201. Boolean ReplyArrived(AppleEvent* ae)
  202. {
  203.     UniversalProcPtr    oldBlockRoutine = nil;
  204.     long                nonExistentParameterStorage;
  205.     long                actualSize;
  206.     DescType            typeCode;
  207.     Boolean                arrived = false;
  208.  
  209.     OSErr err = AEGetSpecialHandler(keyAEBlock, &oldBlockRoutine, false);
  210.     if(err == noErr)
  211.         err = AERemoveSpecialHandler(keyAEBlock, oldBlockRoutine, false);
  212.  
  213.     //
  214.     // Ask for the non existent parameter; this will cause the AppleEvent
  215.     // manager to return errAEReplyNotArrived if the reply is still a future;
  216.     // some other error (such as paramter not found) will be returned if
  217.     // the reply has arrived
  218.     //
  219.     if(err == noErr)
  220.     {
  221.         arrived = (AEGetParamPtr(ae, keyNonexistantParameter, typeLongInteger, &typeCode, (Ptr) &nonExistentParameterStorage, sizeof(long), &actualSize) != errAEReplyNotArrived);
  222.         err = AEInstallSpecialHandler(keyAEBlock, oldBlockRoutine, false);
  223.     }
  224.  
  225.     return arrived;
  226. } // ReplyArrived
  227.  
  228. //----------------------------------------------------------------------------------------
  229. // SetReplyTimeoutValue
  230. //
  231. // Specify how long the reply should wait in between reset-timer notifications, and
  232. // how much total time the reply should wait before giving up on a server that keeps
  233. // reseting the timer but never delivers results.
  234. //----------------------------------------------------------------------------------------
  235. void SetReplyTimeoutValue(AppleEvent* reply, long timeoutValue /* = kNeverTimeoutSemaphore */, long maxWaitTime /* = kNeverTimeoutSemaphore */)
  236. {
  237.     FillInDefaultTimeoutValues(timeoutValue);
  238.     FillInDefaultTimeoutValues(maxWaitTime);
  239.     
  240.     TSemaphore* semaphore = GetFutureSemaphore(reply, kCreateSemaphoreIfNotFound);
  241.     if(semaphore)
  242.     {
  243.         // DebugStr("\pSet reply timout value");
  244.         semaphore->SetSemaphoreTimoutValue(timeoutValue);
  245.         semaphore->SetSemaphoreMaxWaitTime(maxWaitTime);        
  246.     }
  247.     else
  248.         DebugStr("\pCouldn't make semaphore to set timeout value");
  249. }
  250.  
  251. //----------------------------------------------------------------------------------------
  252. // IdleFutures
  253. //
  254. // Usually, this routine doesn't need to be called, as it is done automatically by
  255. // the private futures thread, installed by InitFutures.
  256. //----------------------------------------------------------------------------------------
  257. void IdleFutures()
  258. {
  259.     TSemaphore::Idle();
  260. }
  261.  
  262. //----------------------------------------------------------------------------------------
  263. // AskForFuture
  264. //----------------------------------------------------------------------------------------
  265. OSErr AskForFuture(AppleEvent* ae, AppleEvent* reply, long timeoutValue /* = kAEDefaultTimeout */, long maxWaitTime /* = 0x7FFFFFFF */, AESendMode sendMode, AESendPriority sendPriority /* = kAENormalPriority */)
  266. {
  267.     OSErr err = noErr;
  268.     
  269.     FillInDefaultTimeoutValues(timeoutValue);
  270.     
  271.     long resetFrequency = timeoutValue / 2;
  272.     
  273.     err = AEPutAttributePtr(ae, keyAEResetTimerFrequencyAttr, typeLongInteger, (Ptr)&resetFrequency, sizeof(long));
  274.     if(err == noErr)
  275.         err = AESend(ae, reply, sendMode | kAEWaitReply, sendPriority, 0, nil, nil);
  276.     if(err == errAETimeout)
  277.         err = noErr;
  278.     if(err == noErr)
  279.         SetReplyTimeoutValue(reply, timeoutValue, maxWaitTime);
  280.  
  281.     return err;
  282. }
  283.  
  284. //----------------------------------------------------------------------------------------
  285. // GetResetTimerFrequency
  286. //
  287. // Determines how frequently a server application is advised to call AEResetTimer
  288. //----------------------------------------------------------------------------------------
  289. long GetResetTimerFrequency(AppleEvent* ae)
  290. {
  291.     DescType typeCode;
  292.     long actualSize;
  293.     long resetTimerFrequency = 0;
  294.     
  295.     //
  296.     // First look at keyAEResetTimerFrequencyAttr; if that attribute
  297.     // does not exist, try half of keyAETimeoutAttr
  298.     //
  299.     if(AEGetAttributePtr(ae, keyAEResetTimerFrequencyAttr, typeLongInteger, &typeCode, (Ptr)&resetTimerFrequency, sizeof(long), &actualSize) != noErr)
  300.     {
  301.         if(AEGetAttributePtr(ae, keyTimeoutAttr, typeLongInteger, &typeCode, (Ptr)&resetTimerFrequency, sizeof(long), &actualSize) == noErr)
  302.             resetTimerFrequency /= 2;
  303.         else
  304.             resetTimerFrequency = 0;
  305.     }
  306.     
  307.     //
  308.     // If we couldn't find a frequency, use the default
  309.     //
  310.     if(resetTimerFrequency == 0)
  311.         resetTimerFrequency = kDefaultResetTimerFrequency;
  312.     
  313.     return resetTimerFrequency;
  314. }
  315.  
  316. //----------------------------------------------------------------------------------------
  317. // ResetTimerIfNecessary
  318. //
  319. // Calls AEResetTimer if enough time has elapsed.
  320. //----------------------------------------------------------------------------------------
  321. OSErr ResetTimerIfNecessary(AppleEvent* reply, unsigned long* lastReset, long resetFrequency)
  322. {
  323.     OSErr err = noErr;
  324.     unsigned long currentTime = TickCount();
  325.     
  326.     //
  327.     // Set up 'lastReset' the first time in
  328.     //
  329.     if(*lastReset == 0)
  330.         *lastReset = currentTime;
  331.     
  332.     //
  333.     // We won't be so extreme as to enforce a minimum reset
  334.     // frequency, but we don't allow zero!
  335.     //
  336.     if(resetFrequency == 0)
  337.         resetFrequency = kDefaultResetTimerFrequency;
  338.     
  339.     //
  340.     // TimeExpired is in Semaphores.c; it determines if 'currentTime' has
  341.     // gone beyond 'lastReset + resetFrequency', handling the unavoidable
  342.     // tickcount wraparound that happens every 771 days.
  343.     //    
  344.     if(TimeExpired(currentTime, *lastReset, *lastReset + resetFrequency))
  345.     {
  346.         // DebugStr("\pAbout to call AEResetTimer");
  347.         
  348.         *lastReset = currentTime;
  349.         err = AEResetTimer(reply);
  350.     }
  351.     
  352.     return err;
  353. }
  354.  
  355. //----------------------------------------------------------------------------------------
  356. // NewFuturesThread
  357. //
  358. // This routine calls either 'NewThread' or the application-defined thread creation
  359. // procedure.
  360. //----------------------------------------------------------------------------------------
  361. static OSErr NewFuturesThread(ThreadEntryProcPtr threadEntry, void *threadParam, long handlerRefCon, ThreadID *threadMade)
  362. {
  363.     OSErr err = noErr;
  364.     Size stackSize = 0;
  365.     ThreadOptions options = kCreateIfNeeded | kFPUNotNeeded;
  366.     
  367.     if(gThreadCreateProc != nil)
  368.         err = (*gThreadCreateProc)(threadEntry, threadParam, handlerRefCon, threadMade);
  369.     else
  370.         err = NewThread(kCooperativeThread, threadEntry, threadParam, stackSize, options, nil, threadMade);
  371.     
  372.     return err;
  373. }
  374.  
  375. //----------------------------------------------------------------------------------------
  376. // GetFutureSemaphore:
  377. //
  378. // This function returns the semaphore that this event is blocked on, or
  379. // nil if the event is not blocked
  380. //----------------------------------------------------------------------------------------
  381. static TSemaphore* GetFutureSemaphore(AppleEvent* reply, Boolean createIfNotFound)
  382. {
  383.     TSemaphore* semaphore = nil;
  384.     long semaphoreID = 0;
  385.  
  386.     //
  387.     // Use the reply's return ID as the semaphore ID.
  388.     //
  389.     // We need to set the semaphore ID when the current thread
  390.     // blocks on the future; once the reply arrives, we need to
  391.     // pull the semaphore ID out of the reply.  Therefore, we'd
  392.     // better use an attribute that exists before and after the
  393.     // reply arrives.
  394.     //
  395.     // Another important point:  we can access the return ID attribute
  396.     // without causing the block routine to be called--very important,
  397.     // as we need to get the semaphore out of the message from
  398.     // within the block routine!
  399.     //
  400.     DescType typeCode;
  401.     long actualSize;
  402.     if(AEGetAttributePtr(reply, keyReturnIDAttr, typeLongInteger, &typeCode, (Ptr)&semaphoreID, sizeof(long), &actualSize) == noErr)
  403.         semaphore = TSemaphore::FindSemaphore(semaphoreID, createIfNotFound, 0);
  404.     else
  405.         DebugStr("\pCould not get return attr from reply");
  406.     
  407.     return semaphore;
  408. } // GetFutureSemaphore 
  409.  
  410. //----------------------------------------------------------------------------------------
  411. // AEBlock: 
  412. //
  413. // This routine is installed as the special handler "block", which is called by the
  414. // AppleEvent manager whenever a routine that tries to extract information from a
  415. // future (an AppleEvent reply that has not yet arrived) is called.
  416. //----------------------------------------------------------------------------------------
  417. static pascal OSErr AEBlock(AppleEvent* reply)
  418. {
  419.     TSemaphore*            semaphore = nil;
  420.     OSErr                err = noErr;
  421.     
  422.     // DebugStr("\pAEBlock");
  423.     
  424.     //
  425.     // Look up the semaphore attached to this message.  This will
  426.     // only rarely exist; the only time that it would exist would
  427.     // be if multiple threads tried to pull information out of
  428.     // the same future.  In that case, the first thread would create
  429.     // the semaphore, and the second thread would then get and
  430.     // block on the same semaphore.
  431.     //
  432.     semaphore = GetFutureSemaphore(reply, kCreateSemaphoreIfNotFound);
  433.         
  434.     //
  435.     // If we have a semaphore, then block on it.
  436.     // 'errAETimeout' is one error that may
  437.     // come out of 'Grab' (if we go to sleep
  438.     // for a while, then give up).  Note, however,
  439.     // that the AppleEvent manager will always
  440.     // return errAEReplyNotArrived if AEBlock
  441.     // returns anything other than noErr.
  442.     //
  443.     if(semaphore != nil)
  444.     {
  445.         err = semaphore->Grab();
  446.         semaphore->Release();
  447.     }
  448.     //
  449.     // If we can't block, then return
  450.     // errAEReplyNotArrived right away
  451.     //
  452.     else
  453.         err = errAEReplyNotArrived;
  454.             
  455.     return err;
  456. } // AEBlock 
  457.  
  458. //----------------------------------------------------------------------------------------
  459. // AEUnblock: 
  460. //
  461. // This function is called by the AppleEvent manager when a future that was blocked
  462. // on becomes a real event (i.e., when the reply actually arrives).  When that happens,
  463. // we need to unblock all threads that are blocked on this future.
  464. //----------------------------------------------------------------------------------------
  465. static pascal OSErr AEUnblock(AppleEvent* reply)
  466. {
  467.     TSemaphore*            semaphore = nil;
  468.     OSErr                err = noErr;
  469.  
  470.     // DebugStr("\pAEUnblock");
  471.     
  472.     //
  473.     // The AppleEvent manager may call the unblock routine
  474.     // even if the block routine was never called, so there
  475.     // may or may not be a semaphore attached to the message
  476.     //
  477.     semaphore = GetFutureSemaphore(reply, kDontCreateSemaphoreIfNotFound);
  478.     if(semaphore != nil)
  479.     {
  480.         // DebugStr("\pAbout to release all threads");
  481.         semaphore->Dispose();
  482.     }
  483.     
  484.     return err;
  485. } // AEUnblock 
  486.  
  487. //----------------------------------------------------------------------------------------
  488. // WaitLongerEvent:
  489. //
  490. // Notify the reply that some activity was reported from the server. 
  491. //----------------------------------------------------------------------------------------
  492. static pascal OSErr WaitLongerEvent(AppleEvent* ae, AppleEvent* /*reply*/, long /*refCon*/)
  493. {
  494.     // DebugStr("\pWaitLongerEvent");
  495.     
  496.     //
  497.     // Note that the return ID of the wait longer event
  498.     // is the same as the return ID of the reply whose
  499.     // semaphore we want to look up; the reply to the
  500.     // wait longer event is not used for anything, because
  501.     // the AppleEvent manager sends the wait longer event
  502.     // "no reply".
  503.     //
  504.     TSemaphore* semaphore = GetFutureSemaphore(ae, kDontCreateSemaphoreIfNotFound);
  505.     if(semaphore)
  506.     {
  507.         // DebugStr("\pFound semaphore to reset timer on");
  508.         semaphore->ResetTimeoutTimer();
  509.     }
  510.     //else
  511.         // DebugStr("\pGot wait longer event but did not find semaphore");
  512.     
  513.     return noErr;
  514. } // WaitLongerEvent
  515.  
  516. //----------------------------------------------------------------------------------------
  517. // AsyncPreDispatchHandler: 
  518. //----------------------------------------------------------------------------------------
  519. static pascal OSErr AsyncPreDispatchHandler(AppleEvent* ae, AppleEvent* reply, long /*refCon*/)
  520. {
  521.     //
  522.     // By default, assume that we are not going to handle this event;
  523.     // If we return 'errAEEventNotHandled', the AppleEvent manager will
  524.     // continue dispatching this event.  We let the AEM handle events
  525.     // without any handler, and events that would be handled by a system
  526.     // event handler (which we know will never yield, and therefore
  527.     // do not need to run in a thread).
  528.     //
  529.     OSErr err = errAEEventNotHandled;
  530.     AsyncPredispatchParameters** dispatchParams = nil;
  531.     AEEventHandlerUPP handler = nil;
  532.     long handlerRefCon = 0;
  533.     
  534.     //
  535.     // Look up the event handler and refCon in the AppleEvent tables;
  536.     // if we can't find a handler, then we'll let the AppleEvent
  537.     // manager do the dispatching.
  538.     //
  539.     if(GetAppleEventHandlerUPP(ae, &handler, &handlerRefCon) == noErr)
  540.     {
  541.         dispatchParams = (AsyncPredispatchParameters**)NewHandle(sizeof(AsyncPredispatchParameters));
  542.         if(dispatchParams != nil)
  543.         {
  544.             //
  545.             // Save the AppleEvent message and reply.  We don't need to
  546.             // save the event handler's refCon, because the AppleEvent
  547.             // manager will look it up again when the event is redispatched.
  548.             //
  549.             (*dispatchParams)->fAppleEvent        = *ae;
  550.             (*dispatchParams)->fReply            = *reply;
  551.             (*dispatchParams)->fEventHandler    = handler;
  552.             (*dispatchParams)->fHandlerRefCon    = handlerRefCon;
  553.                 
  554.             //
  555.             // Make a new thread
  556.             //
  557.             ThreadID newThreadID;
  558.             if(NewFuturesThread((ThreadEntryProcPtr)RedispatchEvent, (void*)dispatchParams, handlerRefCon, &newThreadID) == noErr)
  559.             {
  560.                 dispatchParams = nil;
  561.  
  562.                 //
  563.                 // Always suspend the current event.  AEResumeTheCurrentEvent
  564.                 // will be called to redispatch the event after it has been
  565.                 // forked into a thread.
  566.                 //
  567.                 AESuspendTheCurrentEvent(ae);
  568.             
  569.                 //
  570.                 // Set 'err' to 'noErr', indicating that we have handled
  571.                 // the event.
  572.                 //
  573.                 err = noErr;
  574.             }
  575.         }
  576.     }
  577.  
  578.     //
  579.     // If the 'dispatchParams' were created, but we couldn't
  580.     // fork a new thread, then dispose of them here.  Otherwise,
  581.     // they will be disposed when the event is redispatched.
  582.     //
  583.     if(dispatchParams != nil)
  584.         DisposeHandle((Handle)dispatchParams);
  585.     
  586.     return err;
  587. } // AsyncPreDispatchHandler 
  588.  
  589.  
  590. //----------------------------------------------------------------------------------------
  591. // RedispatchEvent
  592. //----------------------------------------------------------------------------------------
  593. static void RedispatchEvent(void* threadParam)
  594. {
  595.     OSErr err = noErr;
  596.     
  597.     //
  598.     // Extract the AppleEvent, Reply, event handler UPP and
  599.     // message refCon from the thread parameter, which we
  600.     // know is a handle to our predispatch parameters. 
  601.     //
  602.     AsyncPredispatchParameters** dispatchParams = (AsyncPredispatchParameters**)threadParam;
  603.     AppleEvent ae = (*dispatchParams)->fAppleEvent;
  604.     AppleEvent reply = (*dispatchParams)->fReply;
  605.     AEEventHandlerUPP handler = (*dispatchParams)->fEventHandler;
  606.     long handlerRefCon = (*dispatchParams)->fHandlerRefCon;
  607.     DisposeHandle((Handle)dispatchParams);
  608.     
  609.     //
  610.     // Call the event handler directly
  611.     //
  612.     err = CallAEEventHandlerProc(handler, &ae, &reply, handlerRefCon);
  613.     
  614.     //
  615.     // If the event handler returned an error, we need to do
  616.     // exactly what the AppleEvent manager would do in the
  617.     // same situation:  jam the error into keyErrorNumber
  618.     // iff there isn't a keyErrorNumber in the reply already.
  619.     // This is necessary because there is no way to pass
  620.     // the error code into AEResumeTheCurrentEvent
  621.     //
  622.     if(err != noErr)
  623.     {
  624.         DescType actualType = typeNull;
  625.         long actualSize = 0;
  626.         long errorResult = 0;
  627.         
  628.         if(AEGetParamPtr(&reply, keyErrorNumber, typeLongInteger, &actualType, &errorResult, sizeof(long), &actualSize) != noErr)
  629.         {
  630.             errorResult = err;
  631.             AEPutParamPtr(&reply, keyErrorNumber, typeLongInteger, &errorResult, sizeof(long));
  632.         }
  633.     }
  634.     
  635.     //
  636.     // When our handler returns, call AEResumeTheCurrentEvent
  637.     // without dispatching, so that the AppleEvent manager
  638.     // will send the reply.
  639.     //
  640.     AEResumeTheCurrentEvent(&ae, &reply, (AEEventHandlerUPP)kAENoDispatch, 0);
  641. }
  642.  
  643. //----------------------------------------------------------------------------------------
  644. // GetAEMHandlerUPPFromOneTable
  645. //----------------------------------------------------------------------------------------
  646. static OSErr GetAEMHandlerUPPFromOneTable(AEEventClass theAEEventClass, AEEventID theAEEventID, AEEventHandlerUPP *handler, long *handlerRefCon, Boolean isSysHandler)
  647. {
  648.     OSErr err = AEGetEventHandler(theAEEventClass, theAEEventID, handler, handlerRefCon, isSysHandler);
  649.     if(err != noErr)
  650.         err = AEGetEventHandler(typeWildCard, theAEEventID, handler, handlerRefCon, isSysHandler);
  651.     if(err != noErr)
  652.         err = AEGetEventHandler(theAEEventClass, typeWildCard, handler, handlerRefCon, isSysHandler);
  653.     if(err != noErr)
  654.         err = AEGetEventHandler(typeWildCard, typeWildCard, handler, handlerRefCon, isSysHandler);
  655.         
  656.     return err;
  657. }
  658.  
  659. //----------------------------------------------------------------------------------------
  660. // GetAppleEventHandlerUPP
  661. //----------------------------------------------------------------------------------------
  662. static OSErr GetAppleEventHandlerUPP(AppleEvent* ae, AEEventHandlerUPP *handler, long *handlerRefCon)
  663. {
  664.     AEEventClass theAEEventClass;
  665.     AEEventID theAEEventID;
  666.     DescType actualType = typeNull;
  667.     long actualSize = 0;
  668.     
  669.     //
  670.     // Look up the event class and the event ID of the AppleEvent
  671.     // so that we can look it up in the event handler dispatch table
  672.     //
  673.     OSErr err = AEGetAttributePtr(ae, keyEventClassAttr, typeType, &actualType, &theAEEventClass, sizeof(AEEventClass), &actualSize);
  674.     if(err == noErr)
  675.         err = AEGetAttributePtr(ae, keyEventIDAttr, typeType, &actualType, &theAEEventID, sizeof(AEEventID), &actualSize);
  676.  
  677.     if(err == noErr)
  678.     {
  679.         err = GetAEMHandlerUPPFromOneTable(theAEEventClass, theAEEventID, handler, handlerRefCon, false);
  680.  
  681. #if 0
  682.     //
  683.     // We don't need to search the system event handler table, because we can
  684.     // let AEResumeTheCurrentEvent(kAEUseStandardDispatch) do that.  We know
  685.     // that system event handlers don't yield, so this is safe.
  686.     //
  687.         if(err != noErr)
  688.             err = GetAEMHandlerUPPFromOneTable(theAEEventClass, theAEEventID, handler, handlerRefCon, true);
  689. #endif
  690.     }
  691.     
  692.     return err;
  693. }
  694.  
  695.  
  696. //----------------------------------------------------------------------------------------
  697. // PrivateFuturesThread
  698. //
  699. // This thread does periodic tasks for the Futures package; currently, it only checks
  700. // to see if there are any futures that have timed out.  Note that TSemaphore::Idle
  701. // calls TickCount(), and performs no action most of the time.
  702. //
  703. // Unfortunately, there's no way for this thread to tell the thread manager that it
  704. // doesn't need to be swapped in for a number of ticks; it has to keep getting scheduled,
  705. // do nothing, get scheduled again, and so on for most of its life.
  706. //----------------------------------------------------------------------------------------
  707. static long PrivateFuturesThread(long /* threadParam */)
  708. {
  709.     for(;;)
  710.     {
  711.         IdleFutures();
  712.         YieldToAnyThread();
  713.     }
  714.     
  715.     return 0;
  716. }
  717.  
  718. //----------------------------------------------------------------------------------------
  719. // FillInDefaultTimeoutValues
  720. //
  721. // This function converts from negative default constants to the actual positive
  722. // number that the constant is translated into.
  723. //----------------------------------------------------------------------------------------
  724. static void FillInDefaultTimeoutValues(long& timeout)
  725. {
  726.     //
  727.     // ••• What should the default timeout be?
  728.     //
  729.     if(timeout == kAEDefaultTimeout)
  730.         timeout = 1200;
  731.     else if(timeout < 0)
  732.         timeout = 0x7FFFFFFF;
  733. }
  734.