home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc Development Framework / ODFDev / ODF / OS / FWThread / FWODThrd.cpp next >
Encoding:
Text File  |  1996-09-17  |  7.7 KB  |  287 lines  |  [TEXT/MPS ]

  1. //========================================================================================
  2. //
  3. //    File:                FWODThrd.cpp
  4. //    Release Version:    $ ODF 2 $
  5. //
  6. //    Copyright:    (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved.
  7. //
  8. //========================================================================================
  9.  
  10. #include "FWOS.hpp"
  11. #include "FWODThrd.h"
  12. #include "FWThrdUt.h"
  13. #include "FWThrdGd.h"
  14.  
  15. //-----------------------------------------------------------------------------
  16. //-----------------------------------------------------------------------------
  17.  
  18. const FW_PlatformError FW_xThreadKilled = FW_xLastODFError;
  19.  
  20. struct FW_SWakeupTask: public TMTask {
  21.     FW_CThread*     fSleepingThread;
  22. };
  23.  
  24. #ifdef FW_BUILD_MAC
  25.  
  26. RoutineDescriptor FW_CThread::gMacWakeupInfo = BUILD_ROUTINE_DESCRIPTOR (uppTimerProcInfo, FW_CThread::PrivMacWakeup);
  27.  
  28. #endif
  29.  
  30. //-----------------------------------------------------------------------------
  31. // FW_CThread
  32. // Wrapper for threads, allocates a SOM Environment and catches exceptions.
  33. //-----------------------------------------------------------------------------
  34.  
  35. FW_CThread::FW_CThread (void* parameters, FW_ThreadProcedure procedure)
  36. :    fPlatformThread (kNoThreadID)
  37. ,    fParameters (parameters)
  38. ,    fProcedure (procedure)
  39. #ifdef FW_BUILD_MAC
  40. ,    fResult (kODNULL)
  41. #endif
  42. ,    fWakeupTask (kODNULL)
  43. {
  44. #ifdef FW_BUILD_MAC
  45.  
  46.     // Create a default thread (don't let it run yet)
  47.     
  48.     Size defaultStackSize = 0;
  49.     
  50.     OSErr result = ::NewThread (kCooperativeThread, 
  51.         (ThreadEntryProcPtr)PrivMacEnter, this, 
  52.         defaultStackSize, kNewSuspend | kUsePremadeThread | kCreateIfNeeded, 
  53.         &fResult, &fPlatformThread);
  54.     FW_FailOnError (result);
  55.     
  56.     // Install all the switching routines
  57.     
  58.     result = ::SetThreadSwitcher (    fPlatformThread, 
  59.                                     (ThreadSwitchProcPtr) PrivMacSwitchIn, 
  60.                                     this, true);
  61.     FW_FailOnError (result);
  62.     result = ::SetThreadSwitcher (    fPlatformThread, 
  63.                                     (ThreadSwitchProcPtr) PrivMacSwitchOut, 
  64.                                     this, false);
  65.     FW_FailOnError (result);
  66.     result = ::SetThreadTerminator (fPlatformThread, 
  67.                                     (ThreadTerminationProcPtr) PrivMacTerminate, 
  68.                                     this);
  69.     FW_FailOnError (result);
  70.     
  71.     // Now let it run
  72.     
  73.     result = ::SetThreadState (fPlatformThread, kReadyThreadState, fPlatformThread);
  74.     FW_FailOnError (result);
  75.     
  76. #endif
  77.     
  78.     // ODF exception system needs to know about threads.
  79.     
  80.     FW_CThreadSafe::NoteCreation (fPlatformThread);
  81. }
  82.  
  83. FW_CThread::~FW_CThread ()
  84. {
  85.     FW_ASSERT (fWakeupTask == kODNULL);
  86.     
  87.     // Kill the system thread if it's still around.
  88.     PrivKill (true);
  89. }
  90.  
  91. //
  92. // FW_CThread API
  93. //
  94.  
  95. void FW_CThread::PrivKill (FW_Boolean beNiceAboutIt)
  96. {
  97. /*
  98.     How to Kill a Thread
  99.     
  100.     The "best" way would be to throw an exception (in the thread's
  101.     stack frame), causing the stack to be unwound all the way back
  102.     to the entry point. This destroys stack-based objects.
  103.     
  104.     The platform technique is to deallocate the thread, which will not
  105.     destroy stack-based objects.
  106.     
  107.     We have several scenarios:
  108.     
  109.     (1) A thread needs to kill itself (i.e. not return from the Kill function).
  110.     (2) A thread needs to kill another thread.
  111.     (3) A thread, dying via exception, catches the exception and continues.
  112.     
  113.     (1) Is easy; just throw an exception. Can suffer from (3).
  114.     (3) To avoid this we need a "ThreadKiller" thread. It would watch over a bunch of
  115.         threads and make sure they died (by forced termination if exceptions didn't 
  116.         work).
  117.     (2) How do we make one stack from throw an exception, from another stack frame?
  118. */
  119.     FW_ASSERT (fPlatformThread == kNoThreadID);
  120.     if (fPlatformThread != kNoThreadID) {
  121.         
  122.         ThreadID current;
  123.         ::GetCurrentThread (¤t);
  124.         FW_Boolean isCurrent = (current == fPlatformThread);
  125.         
  126.         // Killing a thread:
  127.         // 1) Throw an exception all the way up the stack
  128.         // 2) Use the platform routine
  129.         
  130.         // If the thread being killed is the current thread,
  131.         // we can only execute step 1.
  132.         // For other threads we throw an exception in that thread,
  133.         // if possible.
  134.         if (beNiceAboutIt) {
  135.             if (isCurrent)
  136.                 FW_Failure (FW_xThreadKilled);
  137.             else
  138.                 FW_ASSERT (("FW_CThread::Kill", false));
  139.         }
  140.         
  141.         // If that didn't work then kill it with extreme prejudice
  142.         if (fPlatformThread) {
  143.             OSErr result = ::DisposeThread (fPlatformThread, kODNULL, true);
  144.         }
  145.         fPlatformThread = kNoThreadID;
  146.     }
  147. }
  148.  
  149. void FW_CThread::Sleep (long ticks)
  150. {
  151.     // Use the MacOS Time Manager to wake us up in a while.
  152.     FW_ASSERT (fWakeupTask == kODNULL);
  153.     fWakeupTask = new FW_SWakeupTask;
  154.     memset (fWakeupTask, 0, sizeof(*fWakeupTask));
  155.     fWakeupTask->fSleepingThread = this;
  156.     fWakeupTask->tmAddr = &gMacWakeupInfo;
  157.     
  158.     ::InsTime ((QElemPtr)fWakeupTask);
  159.     ::PrimeTime ((QElemPtr)fWakeupTask, ticks);
  160.     Suspend();
  161. }
  162.  
  163. pascal void FW_CThread::PrivMacWakeup (TMTaskPtr tmTaskPtr)
  164. {
  165.     FW_SWakeupTask* wakeup = (FW_SWakeupTask*) tmTaskPtr;
  166.     FW_CThread* self = wakeup->fSleepingThread;
  167.     FW_ASSERT (self->fWakeupTask == wakeup);
  168.     
  169.     FW_TRY {
  170.         self->Resume ();
  171.     }
  172.     FW_CATCH_BEGIN
  173.     FW_CATCH_EVERYTHING() {
  174. #ifdef FW_DEBUG
  175. #endif    
  176.     }
  177.     FW_CATCH_END
  178.     
  179.     ::RmvTime ((QElemPtr)self->fWakeupTask);
  180.     delete self->fWakeupTask;
  181.     self->fWakeupTask = kODNULL;
  182. }
  183.  
  184. void FW_CThread::Suspend ()
  185. {
  186.     FW_CPrivThreadCriticalState critical;
  187.     
  188.     // If this thread is trying to stop itself, then it is in the
  189.     // kRunningThreadState.
  190.     // If it is stopping another thread, then that thread is either
  191.     // kReadyThreadState or kStoppedThreadState.
  192.     
  193.     ThreadState state;
  194.     OSErr result = ::GetThreadState (fPlatformThread, &state);
  195.     FW_FailOnError (result);
  196.     
  197.     if (state == kRunningThreadState || state == kReadyThreadState)
  198.         critical.SetThreadStateEndCritical (fPlatformThread, kStoppedThreadState, kNoThreadID);
  199.     // else kStoppedThreadState, do nothing
  200. }
  201.     
  202. void FW_CThread::Resume ()
  203. {
  204.     OSErr result = ::SetThreadState (fPlatformThread, kReadyThreadState, fPlatformThread);
  205.     FW_FailOnError (result);
  206. }
  207.  
  208. //
  209. // FW_CThread Behavior
  210. //
  211.  
  212. void FW_CThread::Run (Environment* ev)
  213. {
  214.     if (fProcedure)
  215.         (fProcedure) (ev, fParameters);
  216. }
  217.  
  218. /*void FW_CThread::Switch (FW_Boolean switchingOut)
  219. {
  220. }*/
  221.  
  222. //
  223. // FW_CThread Platform Support
  224. //
  225.  
  226. pascal void* FW_CThread::PrivMacEnter (void* selfThread)
  227. {
  228.     FW_SOMEnvironment ev;
  229.     
  230.     void* result = nil;
  231.     
  232.     FW_TRY {
  233.         FW_CThread* self = (FW_CThread*) selfThread; // XXX Use dynamic cast? No.
  234.         self->Run (ev);
  235.         self->fPlatformThread = kNoThreadID;
  236.         result = self->fResult;
  237.     }
  238.     FW_CATCH_BEGIN
  239.     FW_CATCH_EVERYTHING() {
  240.     }
  241.     FW_CATCH_END
  242.     
  243.     return result;
  244. }
  245.  
  246. pascal void FW_CThread::PrivMacSwitchIn (ThreadID threadBeingSwitched, void* selfThread)
  247. {
  248.     FW_CThread* self = (FW_CThread*) selfThread;
  249.     FW_ASSERT (threadBeingSwitched == self->fPlatformThread);
  250.     
  251.     FW_CThreadSafe::NoteSwitch (threadBeingSwitched, true);
  252. }
  253.  
  254. pascal void FW_CThread::PrivMacSwitchOut (ThreadID threadBeingSwitched, void* selfThread)
  255. {
  256.     FW_CThread* self = (FW_CThread*) selfThread;
  257.     FW_ASSERT (threadBeingSwitched == self->fPlatformThread);
  258.     
  259.     FW_CThreadSafe::NoteSwitch (threadBeingSwitched, false);
  260. }
  261.  
  262. pascal void FW_CThread::PrivMacTerminate (ThreadID threadTerminated, void* selfThread)
  263. {
  264.     FW_CThread* self = (FW_CThread*) selfThread;
  265.     FW_ASSERT (self->fPlatformThread == kNoThreadID ||
  266.         self->fPlatformThread == threadTerminated);
  267.     
  268.     // The system thread has been recycled for us.
  269.     
  270.     // ODF exception system needs to know about threads.
  271.     // Work-around: FW_CThreadSafe::NoteTermination will delete the current exception
  272.     // context. We must switch to the system exception context temporarily.
  273. // mlanett 7/1/96 removed skank
  274.     ThreadID id;
  275.     OSErr result = ::GetCurrentThread (&id);
  276.     if (result == noErr && id != kApplicationThreadID) {
  277.         FW_CThreadSafe::NoteSwitch (threadTerminated, false);
  278.         // XXX kApplicationThreadID actually isn't valid to pass into here!
  279.         FW_CThreadSafe::NoteSwitch (kApplicationThreadID, true);
  280.         FW_CThreadSafe::NoteTermination (threadTerminated);
  281.     }
  282.     
  283.     // We do *not* delete the thread object; that is owned by someone.
  284.     self->fPlatformThread = kNoThreadID;
  285. }
  286.  
  287.