home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-09-17 | 7.7 KB | 287 lines | [TEXT/MPS ] |
- //========================================================================================
- //
- // File: FWODThrd.cpp
- // Release Version: $ ODF 2 $
- //
- // Copyright: (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved.
- //
- //========================================================================================
-
- #include "FWOS.hpp"
- #include "FWODThrd.h"
- #include "FWThrdUt.h"
- #include "FWThrdGd.h"
-
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
-
- const FW_PlatformError FW_xThreadKilled = FW_xLastODFError;
-
- struct FW_SWakeupTask: public TMTask {
- FW_CThread* fSleepingThread;
- };
-
- #ifdef FW_BUILD_MAC
-
- RoutineDescriptor FW_CThread::gMacWakeupInfo = BUILD_ROUTINE_DESCRIPTOR (uppTimerProcInfo, FW_CThread::PrivMacWakeup);
-
- #endif
-
- //-----------------------------------------------------------------------------
- // FW_CThread
- // Wrapper for threads, allocates a SOM Environment and catches exceptions.
- //-----------------------------------------------------------------------------
-
- FW_CThread::FW_CThread (void* parameters, FW_ThreadProcedure procedure)
- : fPlatformThread (kNoThreadID)
- , fParameters (parameters)
- , fProcedure (procedure)
- #ifdef FW_BUILD_MAC
- , fResult (kODNULL)
- #endif
- , fWakeupTask (kODNULL)
- {
- #ifdef FW_BUILD_MAC
-
- // Create a default thread (don't let it run yet)
-
- Size defaultStackSize = 0;
-
- OSErr result = ::NewThread (kCooperativeThread,
- (ThreadEntryProcPtr)PrivMacEnter, this,
- defaultStackSize, kNewSuspend | kUsePremadeThread | kCreateIfNeeded,
- &fResult, &fPlatformThread);
- FW_FailOnError (result);
-
- // Install all the switching routines
-
- result = ::SetThreadSwitcher ( fPlatformThread,
- (ThreadSwitchProcPtr) PrivMacSwitchIn,
- this, true);
- FW_FailOnError (result);
- result = ::SetThreadSwitcher ( fPlatformThread,
- (ThreadSwitchProcPtr) PrivMacSwitchOut,
- this, false);
- FW_FailOnError (result);
- result = ::SetThreadTerminator (fPlatformThread,
- (ThreadTerminationProcPtr) PrivMacTerminate,
- this);
- FW_FailOnError (result);
-
- // Now let it run
-
- result = ::SetThreadState (fPlatformThread, kReadyThreadState, fPlatformThread);
- FW_FailOnError (result);
-
- #endif
-
- // ODF exception system needs to know about threads.
-
- FW_CThreadSafe::NoteCreation (fPlatformThread);
- }
-
- FW_CThread::~FW_CThread ()
- {
- FW_ASSERT (fWakeupTask == kODNULL);
-
- // Kill the system thread if it's still around.
- PrivKill (true);
- }
-
- //
- // FW_CThread API
- //
-
- void FW_CThread::PrivKill (FW_Boolean beNiceAboutIt)
- {
- /*
- How to Kill a Thread
-
- The "best" way would be to throw an exception (in the thread's
- stack frame), causing the stack to be unwound all the way back
- to the entry point. This destroys stack-based objects.
-
- The platform technique is to deallocate the thread, which will not
- destroy stack-based objects.
-
- We have several scenarios:
-
- (1) A thread needs to kill itself (i.e. not return from the Kill function).
- (2) A thread needs to kill another thread.
- (3) A thread, dying via exception, catches the exception and continues.
-
- (1) Is easy; just throw an exception. Can suffer from (3).
- (3) To avoid this we need a "ThreadKiller" thread. It would watch over a bunch of
- threads and make sure they died (by forced termination if exceptions didn't
- work).
- (2) How do we make one stack from throw an exception, from another stack frame?
- */
- FW_ASSERT (fPlatformThread == kNoThreadID);
- if (fPlatformThread != kNoThreadID) {
-
- ThreadID current;
- ::GetCurrentThread (¤t);
- FW_Boolean isCurrent = (current == fPlatformThread);
-
- // Killing a thread:
- // 1) Throw an exception all the way up the stack
- // 2) Use the platform routine
-
- // If the thread being killed is the current thread,
- // we can only execute step 1.
- // For other threads we throw an exception in that thread,
- // if possible.
- if (beNiceAboutIt) {
- if (isCurrent)
- FW_Failure (FW_xThreadKilled);
- else
- FW_ASSERT (("FW_CThread::Kill", false));
- }
-
- // If that didn't work then kill it with extreme prejudice
- if (fPlatformThread) {
- OSErr result = ::DisposeThread (fPlatformThread, kODNULL, true);
- }
- fPlatformThread = kNoThreadID;
- }
- }
-
- void FW_CThread::Sleep (long ticks)
- {
- // Use the MacOS Time Manager to wake us up in a while.
- FW_ASSERT (fWakeupTask == kODNULL);
- fWakeupTask = new FW_SWakeupTask;
- memset (fWakeupTask, 0, sizeof(*fWakeupTask));
- fWakeupTask->fSleepingThread = this;
- fWakeupTask->tmAddr = &gMacWakeupInfo;
-
- ::InsTime ((QElemPtr)fWakeupTask);
- ::PrimeTime ((QElemPtr)fWakeupTask, ticks);
- Suspend();
- }
-
- pascal void FW_CThread::PrivMacWakeup (TMTaskPtr tmTaskPtr)
- {
- FW_SWakeupTask* wakeup = (FW_SWakeupTask*) tmTaskPtr;
- FW_CThread* self = wakeup->fSleepingThread;
- FW_ASSERT (self->fWakeupTask == wakeup);
-
- FW_TRY {
- self->Resume ();
- }
- FW_CATCH_BEGIN
- FW_CATCH_EVERYTHING() {
- #ifdef FW_DEBUG
- #endif
- }
- FW_CATCH_END
-
- ::RmvTime ((QElemPtr)self->fWakeupTask);
- delete self->fWakeupTask;
- self->fWakeupTask = kODNULL;
- }
-
- void FW_CThread::Suspend ()
- {
- FW_CPrivThreadCriticalState critical;
-
- // If this thread is trying to stop itself, then it is in the
- // kRunningThreadState.
- // If it is stopping another thread, then that thread is either
- // kReadyThreadState or kStoppedThreadState.
-
- ThreadState state;
- OSErr result = ::GetThreadState (fPlatformThread, &state);
- FW_FailOnError (result);
-
- if (state == kRunningThreadState || state == kReadyThreadState)
- critical.SetThreadStateEndCritical (fPlatformThread, kStoppedThreadState, kNoThreadID);
- // else kStoppedThreadState, do nothing
- }
-
- void FW_CThread::Resume ()
- {
- OSErr result = ::SetThreadState (fPlatformThread, kReadyThreadState, fPlatformThread);
- FW_FailOnError (result);
- }
-
- //
- // FW_CThread Behavior
- //
-
- void FW_CThread::Run (Environment* ev)
- {
- if (fProcedure)
- (fProcedure) (ev, fParameters);
- }
-
- /*void FW_CThread::Switch (FW_Boolean switchingOut)
- {
- }*/
-
- //
- // FW_CThread Platform Support
- //
-
- pascal void* FW_CThread::PrivMacEnter (void* selfThread)
- {
- FW_SOMEnvironment ev;
-
- void* result = nil;
-
- FW_TRY {
- FW_CThread* self = (FW_CThread*) selfThread; // XXX Use dynamic cast? No.
- self->Run (ev);
- self->fPlatformThread = kNoThreadID;
- result = self->fResult;
- }
- FW_CATCH_BEGIN
- FW_CATCH_EVERYTHING() {
- }
- FW_CATCH_END
-
- return result;
- }
-
- pascal void FW_CThread::PrivMacSwitchIn (ThreadID threadBeingSwitched, void* selfThread)
- {
- FW_CThread* self = (FW_CThread*) selfThread;
- FW_ASSERT (threadBeingSwitched == self->fPlatformThread);
-
- FW_CThreadSafe::NoteSwitch (threadBeingSwitched, true);
- }
-
- pascal void FW_CThread::PrivMacSwitchOut (ThreadID threadBeingSwitched, void* selfThread)
- {
- FW_CThread* self = (FW_CThread*) selfThread;
- FW_ASSERT (threadBeingSwitched == self->fPlatformThread);
-
- FW_CThreadSafe::NoteSwitch (threadBeingSwitched, false);
- }
-
- pascal void FW_CThread::PrivMacTerminate (ThreadID threadTerminated, void* selfThread)
- {
- FW_CThread* self = (FW_CThread*) selfThread;
- FW_ASSERT (self->fPlatformThread == kNoThreadID ||
- self->fPlatformThread == threadTerminated);
-
- // The system thread has been recycled for us.
-
- // ODF exception system needs to know about threads.
- // Work-around: FW_CThreadSafe::NoteTermination will delete the current exception
- // context. We must switch to the system exception context temporarily.
- // mlanett 7/1/96 removed skank
- ThreadID id;
- OSErr result = ::GetCurrentThread (&id);
- if (result == noErr && id != kApplicationThreadID) {
- FW_CThreadSafe::NoteSwitch (threadTerminated, false);
- // XXX kApplicationThreadID actually isn't valid to pass into here!
- FW_CThreadSafe::NoteSwitch (kApplicationThreadID, true);
- FW_CThreadSafe::NoteTermination (threadTerminated);
- }
-
- // We do *not* delete the thread object; that is owned by someone.
- self->fPlatformThread = kNoThreadID;
- }
-
-