Show RedShedThreads.c syntax highlighted
/****************************************************************************************
RedShedThreads.c $Revision: 1.2 $
<http://rentzsch.com/redshedthreads>
Copyright © 1998-2002 Red Shed Software. All rights reserved.
by Jonathan 'Wolf' Rentzsch (jon at redshed dot net)
************************************************************************************/
#include "RedShedThreads.h"
#include "Align.h"
#include "require.h"
#include "doubt.h"
typedef OSErr (*HoldUnholdMemoryProc)( void *address, UInt32 count );
#if TARGET_API_MAC_CARBON
HoldUnholdMemoryProc MyHoldMemory = nil;
HoldUnholdMemoryProc MyUnholdMemory = nil;
#else
#define MyHoldMemory( ADDRESS, COUNT ) HoldMemory( ADDRESS, COUNT )
#define MyUnholdMemory( ADDRESS, COUNT ) UnholdMemory( ADDRESS, COUNT )
#endif
/**************************
*
* Types
*
**************************/
#pragma mark (Types)
typedef struct {
GuardedAtomicElement element;
RedShedThreadContext *context;
} RedShedThreadContextElement;
/**************************
*
* Constants
*
**************************/
#pragma mark -
#pragma mark (Constants)
#define kThreadAlignment kAtomicAlignment
#define kMinimumStackSize 4096 // 4K
#define kMaximumStackSize 1048576 // 1MB
#define kDefaultStackSize 16384 // 16K
/**************************
*
* Funky Protos
*
**************************/
#pragma mark -
#pragma mark (Funky Protos)
#if TARGET_API_MAC_CARBON
#define LMGetTicks TickCount
#endif
Boolean
ShouldHoldStack();
void
NewRedShedThreadHelper();
AtomicMessageElement*
IncomingAtomicMessageCallback(
AtomicMessageElement *message,
AtomicMessageQueue *queue );
/****************************************************************************************
*
* Public Thread Creation/Destruction Interfaces
*
****************************************************************************************/
#pragma mark -
#pragma mark (Public Thread Creation/Destruction Interfaces)
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Wed, Jun 3, 1998 Created.
wolf Sat, Jun 27, 1998 Now we allocate the stack's memory ourself if the
user passes us nil for the stack's limit.
wolf Mon, Jun 29, 1998 Now we zero out the stack sniffer. Yes, it's
redundant to do it each time we create a thread,
but its easy, fast and safe.
wolf Wed, Jul 1, 1998 Removed stack sniffer code.
wolf Wed, Dec 23, 1998 Fixed a logic error that occured when
NewRedShedThread() did the stack allocation itself.
Fixed an error where I didn't properly initialize
thread->rescheduleList.next to nil.
Rewrote the 68K stack munging code -- it didn't work
under CFM-68K. CodeWarrior seems to expect to be able
to restore a5 from -12(a6). I couldn't find any
documentation on this. I need to rewrite the PowerPC
side as well.
wolf Thu, Sep 30, 1999 Version 3.
wolf Fri, Jan 7, 2000 Now uses SetRedShedThreadContextStack() to set up the
stack.
wolf Mon, Jan 10, 2000 Added support for the executing lock.
wolf Mon, Jan 24, 2000 Version 5. Rewrote. Now will allocate a complete
RedShedThread control block for the user.
wolf Tue, Feb 1, 2000 No longer holds context in memory.
wolf Wed, Feb 2, 2000 contextFlags parameter is now a
RedShedThreadContextFlags (used to be a long).
Moved from HasVM() to ShouldHoldStack().
wolf Fri, Feb 4, 2000 I wasn't correctly initializing the RedShedThread
when the user allocated it himself. Fixed.
wolf Sun, Apr 9, 2000 Version 6. Now uses Atomic Flags.
wolf Sat, Aug 5, 2000 Version 6.2. Updated to use version 2.0 of
RedShedThreadsContext.
wolf Sat, Nov 25, 2000 Added internal documentation about why it's bad to
call SaveRedShedThreadContext on a brand-new thread.
I also wasn't nilling out thread->param like all the
other user fields. Now I do.
************************************************************************************/
RedShedThread*
NewRedShedThread(
RedShedThread *thread,
RedShedThreadContextID contextID )
{
Boolean allocatedContext = false, allocatedStack = false;
OSErr err = noErr;
//-------------------------------------------------
// Optional Allocations.
if( thread == nil ) {
// User wants us to allocate the control block.
err = NewAlignedPtr( sizeof( RedShedThread ), false, true, kThreadAlignment,
(void**) &thread );
if( !err ) {
RequireMacPtr( GetUnalignedPtr( thread ) );
RequirePtrAlign( thread, kThreadAlignment );
SetAtomicFlag( &thread->flags, kRedShedThreadAutoDispose );
}
} else {
// The user allocated the context for us. Make sure we don't dispose of it.
RequirePtrAlign( thread, kThreadAlignment );
thread->flags = kNoAtomicFlags;
}
if( !err && thread->context == nil ) {
// User wants us to allocate the context.
RequirePtrAlign( thread, kThreadAlignment );
Require( IsValidRedShedThreadContextID( contextID ) );
thread->contextSize = GetRedShedThreadContextSize( contextID );
err = NewAlignedPtr( thread->contextSize, false, false,
kRedShedThreadContextAlignment, (void**) &thread->context );
if( !err ) {
RequireMacPtr( GetUnalignedPtr( thread->context ) );
RequirePtrAlign( thread->context, kRedShedThreadContextAlignment );
allocatedContext = true;
InitRedShedThreadContext( thread->context, contextID );
}
}
if( !err && thread->stackLimit == nil ) {
// User wants us to allocate the stack.
RequirePtrAlign( thread, kThreadAlignment );
if( thread->stackSize == 0 )
thread->stackSize = kDefaultStackSize;
Require( thread->stackSize >= kMinimumStackSize && thread->stackSize <= kMaximumStackSize );
err = NewAlignedPtr( thread->stackSize, false, false, kRedShedThreadStackAlignment,
(void**) &thread->stackLimit );
if( !err ) {
RequireMacPtr( GetUnalignedPtr( thread->stackLimit ) );
RequirePtrAlign( thread->stackLimit, kRedShedThreadStackAlignment );
allocatedStack = true;
}
}
//-------------------------------------------------
// Initialize the thread.
if( !err ) {
RequirePtrAlign( thread, kThreadAlignment );
LMSetStackLowPoint( nil );
thread->pulsar = nil;
InitializeAtomicMessageQueue( &thread->messageQueue );
thread->messageQueue.incoming = IncomingAtomicMessageCallback;
thread->messageQueue.refCon = thread;
thread->entry = thread->suspender = thread->resumer = nil;
thread->disposer = RedShedThreadDisposer;
thread->param = thread->refCon = nil;
// Alert:
// We can't call SaveRedShedThreadContext here. What happens is that we call
// SaveRedShedThreadContext, which builds a new stack frame and calls
// thread->context->saver, which saves a pointer to SaveRedShedThreadContext's
// stack frame. After we save the context, we switch in a new stack.
// Upon resuming, SaveRedShedThreadContext attempts to return to us. However,
// because the thread is in a new stack, when SaveRedShedThreadContext reloads
// the saved link register from the stack, it loads garbage and jumps to some
// unpredictable address.
//
// Calling the context's saver directly bypasses this nasty side effect.
// if( SaveRedShedThreadContext( thread->context ) ) {
if( thread->context->saver( thread->context ) ) {
// The state is saved, and we're still under the caller's stack.
SetRedShedThreadContextStack( thread->context,
thread->stackLimit + thread->stackSize,
(long) thread );
if( ShouldHoldStack() )
MyHoldMemory( thread->stackLimit, thread->stackSize );
} else {
// Now we're running under the new stack. All our local variables are gone
// (they weren't copied over from the old stack).
NewRedShedThreadHelper();
}
}
//-------------------------------------------------
// Error Cleanup.
if( err ) {
if( allocatedStack ) {
DisposePtr( GetUnalignedPtr( thread->stackLimit ) );
thread->stackLimit = nil;
}
if( allocatedContext ) {
DisposePtr( GetUnalignedPtr( thread->context ) );
thread->context = nil;
}
if( thread && GetAtomicFlag( &thread->flags, kRedShedThreadAutoDispose ) ) {
DisposePtr( GetUnalignedPtr( thread ) );
}
return( nil );
}
return( thread );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Wed, Jul 1, 1998 Created.
wolf Thu, Sep 30, 1999 Version 3.
wolf Mon, Jan 24, 2000 Version 5. Instead of maintaining a gCurrentThread
global variable to test against (to determine
whether we can immediately dispose of the given
thread), I now get the address of a variable on the
stack and test whether is it falls within the
thread's stack allocation block. Sneaky, eh?
wolf Wed, Apr 5, 2000 Version 5.2. All the above sneakiness was moved into
IsCurrentRedShedThread(), which we now use.
wolf Sun, Apr 9, 2000 Version 6. No changes.
************************************************************************************/
void
DisposeRedShedThread(
RedShedThread *thread )
{
RequireRedShedThread( thread );
if( IsCurrentRedShedThread( thread ) ) {
// We're running under the thread that is about to be disposed.
// Simply deleting it now wouldn't be very pretty (disposing the
// stack we're currently using). So instead we set a flag and immediately
// yield. PulseRedShedThread, under the caller's stack, will note the
// flag and dispose it then.
SetAtomicFlag( &thread->flags, kRedShedThreadDisposing );
YieldRedShedThread( thread );
} else {
// We're not running under the thread about to be disposed, so we can
// immediately dispose it.
RequireProcPtr( thread->disposer );
thread->disposer( thread );
}
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Thu, Sep 30, 1999 Created.
wolf Fri, Jan 7, 2000 Now only attempts to dispose of the context and stack
if their pointers are not nil.
wolf Mon, Jan 10, 2000 Added DumpAtomicMessages() to end of function.
wolf Mon, Jan 24, 2000 Version 5. Now disposes of the thread control block
itself.
wolf Tue, Feb 1, 2000 Since the context is no longer held in memory, it
is no longer unheld here.
wolf Wed, Feb 2, 2000 Moved from HasVM() to ShouldHoldStack().
wolf Fri, Mar 24, 2000 Duh! I was re-calling HoldMemory() before disposing
the stack, instead of calling UnholdMemory().
wolf Sun, Apr 9, 2000 Version 6. Now uses Atomic Flags.
************************************************************************************/
void
RedShedThreadDisposer(
RedShedThread *thread )
{
RequireRedShedThread( thread );
Require( !IsCurrentRedShedThread( thread ) );
if( thread->context ) {
DisposePtr( GetUnalignedPtr( thread->context ) );
thread->context = nil;
}
if( thread->stackLimit ) {
if( ShouldHoldStack() )
MyUnholdMemory( thread->stackLimit, thread->stackSize );
DisposePtr( GetUnalignedPtr( thread->stackLimit ) );
thread->stackLimit = nil;
}
DumpAtomicMessages( &thread->messageQueue );
if( GetAtomicFlag( &thread->flags, kRedShedThreadAutoDispose ) ) {
DisposePtr( GetUnalignedPtr( thread ) );
}
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Fri, Feb 18, 2000 Created.
wolf Wed, Apr 5, 2000 Version 5.2. Renamed from IsRedShedThreadValid() to
IsValidRedShedThread().
wolf Sun, Apr 9, 2000 Version 6. Updated for modified RedShedThread
structure.
************************************************************************************/
ValidRedShedThreadCode
IsValidRedShedThread(
RedShedThread *thread )
{
if( thread == nil )
return( kRedShedThreadNil );
if( !IsAligned( thread, kThreadAlignment ) )
return( kRedShedThreadNotAligned );
if( !IsAligned( (void*) &thread->flags, kAtomicAlignment ) )
return( kRedShedThreadFlagsNotAligned );
if( !IsAligned( &thread->messageQueue, kAtomicAlignment ) )
return( kRedShedThreadMessageQueueNotAligned );
if( IsValidRedShedThreadContext( thread->context, thread->contextSize )
!= kRedShedThreadContextValid )
return( kRedShedThreadInvalidContext );
if( !IsAligned( thread->stackLimit, kRedShedThreadStackAlignment ) )
return( kRedShedThreadStackLimitNotAligned );
if( thread->stackSize < kMinimumStackSize || thread->stackSize > kMaximumStackSize )
return( kRedShedThreadInvalidStackSize );
if( thread->pulsar && !IsAligned( thread->pulsar, kAtomicAlignment ) )
return( kRedShedThreadPulsarNotAligned );
if( thread->entry == nil )
return( kRedShedThreadEntryNil );
RequireProcPtr( thread->entry );
if( thread->disposer == nil )
return( kRedShedThreadDisposerNil );
RequireProcPtr( thread->disposer );
RequireProcPtrIfNotNil( thread->suspender );
RequireProcPtrIfNotNil( thread->resumer );
return( kRedShedThreadValid );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Wed, Apr 5, 2000 Version 5.2. Created.
wolf Sun, Apr 9, 2000 Version 6. No changes.
************************************************************************************/
Boolean
IsCurrentRedShedThread(
RedShedThread *thread )
{
char *stackBegin;
char *stackEnd;
char *stackVariableAddress;
RequireRedShedThread( thread );
stackBegin = thread->stackLimit;
stackEnd = stackBegin + thread->stackSize;
stackVariableAddress = (char*) &stackBegin;
return( stackVariableAddress > stackBegin && stackVariableAddress < stackEnd );
}
/****************************************************************************************
*
* Public Thread Scheduling Interfaces
*
****************************************************************************************/
#pragma mark -
#pragma mark (Public Thread Scheduling Interfaces)
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Thu, Sep 30, 1999 Created.
wolf Mon, Jan 24, 2000 Version 5. No longer have to mess with
gCurrentThread, which dramatically simplifies this
function.
wolf Sun, Jan 30, 2000 Now uses the new GetRedShedThreadContextFlags()
function.
wolf Thu, Feb 3, 2000 Version 5.1. Now maintains a stack of pulsars
instead of just one.
wolf Mon, Mar 20, 2000 Now detects an thread attempting to pulse itself. It
will not pulse the thread, but will set a flag that
BlockRedShedThread() will read.
wolf Fri, Mar 24, 2000 Hmm, interesting. I declare sameThread outside the
SaveRedShedThreadContext() block, but set it's value
within the block. On the PowerPC, sameThread would
be stored in a register, not on the stack. Outside
the SaveRedShedThreadContext() block, sameThread
would be restored back to it's undefined initial
value. Rarely this would be 0 (false), so it
manifested itself as a failure to call the disposer
on a freshly disposed thread. I now give sameThread
and initial false value.
wolf Mon, Apr 3, 2000 Added support for startPulseTime.
wolf Wed, Apr 5, 2000 Version 5.2. Now detects when a thread pulses itself
using IsCurrentRedShedThread().
wolf Sun, Apr 9, 2000 Version 6. Major simplification.
wolf Sat, May 20, 2000 I was clearing the thread's kRedShedThreadRunning
flag, even after the thread was disposed. Basically
I was clearing memory I didn't really own anymore.
Fixed.
wolf Sat, Aug 5, 2000 Version 6.2. Updated to use version 2.0 of
RedShedThreadsContext.
************************************************************************************/
void
PulseRedShedThread(
RedShedThread *thread )
{
RequireRedShedThread( thread );
if( SetAtomicFlag( &thread->flags, kRedShedThreadRunning ) ) {
// We've claimed ownership of this thread.
DeclareAlignedVariable( RedShedThreadContextMax, pulsar, kRedShedThreadContextAlignment );
UInt32 oldStartPulseTime = thread->startPulseTime;
RequirePtrAlign( pulsar, kRedShedThreadContextAlignment );
RequireEqual( thread->pulsar, nil );
InitRedShedThreadContext( (RedShedThreadContext*) pulsar, thread->context->id );
ClearAtomicFlag( &thread->flags, kRedShedThreadWaitingForPulse );
ClearAtomicFlag( &thread->flags, kRedShedThreadGotPulse );
thread->pulsar = (RedShedThreadContext*) pulsar;
thread->startPulseTime = LMGetTicks();
// If the thread got a pulse while it was running, switch it back in automatically
// unless the thread is dying.
do {
if( SaveRedShedThreadContext( (RedShedThreadContext*) pulsar ) ) {
// Switch in the upcoming thread.
ResumeRedShedThreadContext( thread->context );
}
} while( GetAtomicFlag( &thread->flags, kRedShedThreadGotPulse ) );
RequireRedShedThread( thread );
thread->startPulseTime = oldStartPulseTime;
thread->pulsar = nil;
// Now that the thread isn't running, see if it disposed of itself.
if( GetAtomicFlag( &thread->flags, kRedShedThreadDisposing ) ) {
RequireProcPtr( thread->disposer );
thread->disposer( thread );
} else {
ClearAtomicFlag( &thread->flags, kRedShedThreadRunning );
}
} else {
Require( GetAtomicFlag( &thread->flags, kRedShedThreadWaitingForPulse ) );
SetAtomicFlag( &thread->flags, kRedShedThreadGotPulse );
}
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Thu, Sep 30, 1999 Created.
wolf Mon, Jan 10, 2000 Added support for the executing lock.
wolf Mon, Jan 24, 2000 Version 5. Rewrote. Much simpler.
wolf Wed, Jan 26, 2000 Now calls the optional suspender/resumer callbacks.
wolf Thu, Feb 3, 2000 Version 5.1. Now maintains a stack of pulsars
instead of just one.
wolf Mon, Mar 20, 2000 Now makes sure the kRedShedThreadDontBlockThread is
not set.
wolf Sun, Apr 9, 2000 Version 6.
************************************************************************************/
void
YieldRedShedThread(
RedShedThread *thread )
{
RequireCurrentRedShedThread( thread );
if( SaveRedShedThreadContext( thread->context ) ) {
// We're about to be suspended, call the suspender function.
if( thread->suspender ) {
RequireProcPtr( thread->suspender );
thread->suspender( thread );
}
// Resume pulsar that called us.
ResumeRedShedThreadContext( thread->pulsar );
}
// Back in our own context after sleeping.
// We've been resumed, call the resumer function.
if( thread->resumer ) {
RequireProcPtr( thread->resumer );
thread->resumer( thread );
}
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Sun, Apr 9, 2000 Created for Version 6.
************************************************************************************/
void
ReadyRedShedThread(
RedShedThread *thread )
{
RequireCurrentRedShedThread( thread );
ClearAtomicFlag( &thread->flags, kRedShedThreadGotPulse );
SetAtomicFlag( &thread->flags, kRedShedThreadWaitingForPulse );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Thu, Feb 3, 2000 Created.
wolf Mon, Mar 20, 2000 Now reads the kRedShedThreadDontBlockThread flag. If
it is set, BlockRedShedThread() doesn't resume the
pulsar. In any case, it clears the flag.
wolf Sat, May 20, 2000 Forgot to clear the kRedShedThreadGotPulse flag.
************************************************************************************/
void
BlockRedShedThread(
RedShedThread *thread )
{
RequireCurrentRedShedThread( thread );
Require( GetAtomicFlag( &thread->flags, kRedShedThreadWaitingForPulse ) );
if( !GetAtomicFlag( &thread->flags, kRedShedThreadGotPulse ) ) {
// We're about to be suspended, call the suspender function.
if( thread->suspender ) {
RequireProcPtr( thread->suspender );
thread->suspender( thread );
}
if( SaveRedShedThreadContext( thread->context ) ) {
// Resume the pulsar that called us.
ResumeRedShedThreadContext( thread->pulsar );
}
// Back in our own context after sleeping.
// We've been resumed, call the resumer function.
if( thread->resumer ) {
RequireProcPtr( thread->resumer );
thread->resumer( thread );
}
}
ClearAtomicFlag( &thread->flags, kRedShedThreadGotPulse );
ClearAtomicFlag( &thread->flags, kRedShedThreadWaitingForPulse );
}
/****************************************************************************************
*
* Public Thread Messaging Interfaces
*
****************************************************************************************/
#pragma mark -
#pragma mark (Public Thread Messaging Interfaces)
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Mon, Jan 10, 2000 Created.
wolf Mon, Jan 24, 2000 Version 5. Renamed thread parameter to toThread.
wolf Sun, Apr 9, 2000 Version 6. No changes.
************************************************************************************/
Boolean
SendRedShedThreadMessage(
RedShedThread *toThread,
AtomicMessageElement *message )
{
RequireRedShedThread( toThread );
RequirePtrAlign( message, kAtomicAlignment );
return( SendAtomicMessage( &toThread->messageQueue, message ));
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Mon, Jan 10, 2000 Created.
wolf Mon, Jan 24, 2000 Version 5. Renamed thread parameter to toThread.
wolf Sun, Apr 9, 2000 Version 6. No changes.
************************************************************************************/
OSErr
NewRedShedThreadMessage(
RedShedThread *toThread,
AtomicMessage message,
long data1,
long data2,
long data3 )
{
RequireRedShedThread( toThread );
return( NewAtomicMessage( &toThread->messageQueue, message, data1, data2, data3 ));
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Mon, Jan 10, 2000 Created.
wolf Mon, Jan 24, 2000 Version 5. Added thread parameter.
wolf Sun, Apr 9, 2000 Version 6. No Changes.
wolf Thu, Apr 20, 2000 Updated to match new Atomic Messaging interfaces.
************************************************************************************/
AtomicMessage
PeekRedShedThreadMessage(
RedShedThread *thread,
AtomicMessageProc filterProc,
AtomicMessage filterValue,
long *data1,
long *data2,
long *data3 )
{
RequireRedShedThread( thread );
RequireProcPtrIfNotNil( filterProc );
RequirePtrIfNotNil( data1 );
RequirePtrIfNotNil( data2 );
RequirePtrIfNotNil( data3 );
return( PeekAtomicMessage( &thread->messageQueue, filterProc, filterValue, data1,
data2, data3 ));
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Mon, Jan 10, 2000 Created.
wolf Mon, Jan 24, 2000 Version 5. Added thread parameter.
wolf Sun, Apr 9, 2000 Version 6. No Changes.
wolf Thu, Apr 20, 2000 Updated to match new AtomicMessaging interfaces.
************************************************************************************/
AtomicMessage
ReceiveRedShedThreadMessage(
RedShedThread *thread,
AtomicMessageProc filterProc,
AtomicMessage filterValue,
long *data1,
long *data2,
long *data3 )
{
RequireRedShedThread( thread );
RequireProcPtrIfNotNil( filterProc );
RequirePtrIfNotNil( data1 );
RequirePtrIfNotNil( data2 );
RequirePtrIfNotNil( data3 );
return( ReceiveAtomicMessage( &thread->messageQueue, filterProc, filterValue, data1,
data2, data3 ));
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Mon, Jan 10, 2000 Created as WaitRedShedThreadMessage().
wolf Mon, Jan 24, 2000 Version 5. Added thread parameter.
wolf Thu, Feb 3, 2000 Version 5.1. Closed a Window of Death.
wolf Mon, Feb 14, 2000 Duh! In WaitRedShedThreadMessage(), I put the code to
clear the kRedShedThreadWaitingForMessage flag *after*
I called BlockRedShedThread() -- a call that never
returns, and thus the flag was never cleared.
Fortunately a requirement fired the first time I was
reentered and I stomped the bug by placing the flag
clearing code *outside* the SyncRedShedThread()
block.
wolf Fri, Feb 18, 2000 Added peek option to WaitRedShedThreadMessage().
wolf Mon, May 1, 2000 Version 6.1. The old function took a
parameter ("peek") to determine whether to peek or
receive the message. I broke the one
WaitRedShedThreadMessage() into two functions:
WaitPeekRedShedThreadMessage() and
WaitReceiveRedShedThreadMessage() to better reflect
the atomic messaging API.
************************************************************************************/
AtomicMessage
WaitPeekRedShedThreadMessage(
RedShedThread *thread,
AtomicMessageProc filterProc,
AtomicMessage filterValue,
long *data1,
long *data2,
long *data3 )
{
AtomicMessage message = PeekRedShedThreadMessage( thread, filterProc, filterValue,
data1, data2, data3 );
while( message == kNoAtomicMessage ) {
ReadyRedShedThread( thread );
SetAtomicFlag( &thread->flags, kRedShedThreadWaitingForMessage );
message = PeekRedShedThreadMessage( thread, filterProc, filterValue,
data1, data2, data3 );
if( message == kNoAtomicMessage )
BlockRedShedThread( thread );
ClearAtomicFlag( &thread->flags, kRedShedThreadWaitingForMessage );
if( message == kNoAtomicMessage )
message = PeekRedShedThreadMessage( thread, filterProc, filterValue,
data1, data2, data3 );
}
return( message );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Mon, Jan 10, 2000 Created as WaitRedShedThreadMessage().
wolf Mon, Jan 24, 2000 Version 5. Added thread parameter.
wolf Thu, Feb 3, 2000 Version 5.1. Closed a Window of Death.
wolf Mon, Feb 14, 2000 Duh! In WaitRedShedThreadMessage(), I put the code to
clear the kRedShedThreadWaitingForMessage flag *after*
I called BlockRedShedThread() -- a call that never
returns, and thus the flag was never cleared.
Fortunately a requirement fired the first time I was
reentered and I stomped the bug by placing the flag
clearing code *outside* the SyncRedShedThread()
block.
wolf Fri, Feb 18, 2000 Added peek option to WaitRedShedThreadMessage().
wolf Mon, May 1, 2000 Version 6.1. The old function took a
parameter ("peek") to determine whether to peek or
receive the message. I broke the one
WaitRedShedThreadMessage() into two functions:
WaitPeekRedShedThreadMessage() and
WaitReceiveRedShedThreadMessage() to better reflect
the atomic messaging API.
************************************************************************************/
AtomicMessage
WaitReceiveRedShedThreadMessage(
RedShedThread *thread,
AtomicMessageProc filterProc,
AtomicMessage filterValue,
long *data1,
long *data2,
long *data3 )
{
AtomicMessage message = ReceiveRedShedThreadMessage( thread, filterProc, filterValue,
data1, data2, data3 );
while( message == kNoAtomicMessage ) {
ReadyRedShedThread( thread );
SetAtomicFlag( &thread->flags, kRedShedThreadWaitingForMessage );
message = ReceiveRedShedThreadMessage( thread, filterProc, filterValue,
data1, data2, data3 );
if( message == kNoAtomicMessage )
BlockRedShedThread( thread );
ClearAtomicFlag( &thread->flags, kRedShedThreadWaitingForMessage );
if( message == kNoAtomicMessage )
message = ReceiveRedShedThreadMessage( thread, filterProc, filterValue,
data1, data2, data3 );
}
return( message );
}
/****************************************************************************************
*
* Private
*
****************************************************************************************/
#pragma mark -
#pragma mark (Private)
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Wed, Jun 3, 1998 Created as HasVM.
wolf Tue, Feb 1, 2000 Rewrote as ShouldHoldStack.
wolf Mon, Feb 7, 2000 Found out that Mac OS X's gestaltVMAttr Gestalt
always says VM is off. Recoded to work around this
behavior (we only look at gestaltVMAttr if we're sure
we're on Mac OS 8.1 thru 9).
None of this applies when compiling a Classic app.
wolf Sun, Apr 9, 2000 Version 6. No changes.
wolf Mon, Jun 19, 2000 Under Carbon, I wasn't calling CloseConnection after
I GetSharedLibrary()'d InterfaceLib.
wolf Sat, Jul 1, 2000 Added explicit cast to Boolean and RequireBool.
wolf Mon, Jul 17, 2000 It turns out that calling CloseConnection() on
InterfaceLib under Carbon is unnecessary when using
the kFindCFrag option of GetSharedLibrary(). Removed.
************************************************************************************/
Boolean
ShouldHoldStack()
{
static SInt8 shouldHoldStack = -1;
#if TARGET_API_MAC_CARBON
if( shouldHoldStack == -1 ) {
// We may be running under the traditional Mac OS (8.1 thru 9 with CarbonLib),
// which requires that we hold the stack if VM is on, or we may be running
// under Mac OS X, which doesn't.
// Here we try to get InterfaceLib, which isn't available under X.
CFragConnectionID lib = nil;
OSErr err = GetSharedLibrary( "\pInterfaceLib", kPowerPCCFragArch, kFindCFrag,
&lib, nil, nil );
if( err ) {
// InterfaceLib couldn't be found, we're under Mac OS X.
// Don't hold the stack.
RequireEqual( err, cfragNoLibraryErr );
shouldHoldStack = false;
} else {
// InterfaceLib was found, we're under Mac OS 8.1 thru 9.
// If VM is on, hook up our function pointers to the correct symbols and
// return true.
// Otherwise leave them nil and return false.
long response;
err = Gestalt( gestaltVMAttr, &response );
shouldHoldStack = !err && (response & (1L << gestaltVMPresent));
if( shouldHoldStack ) {
Require( lib != nil );
err = FindSymbol( lib, "\pHoldMemory", (Ptr*) &MyHoldMemory, nil );
Require( !err );
RequireProcPtr( MyHoldMemory );
err = FindSymbol( lib, "\pUnholdMemory", (Ptr*) &MyUnholdMemory, nil );
Require( !err );
RequireProcPtr( MyUnholdMemory );
}
}
}
#else
if( shouldHoldStack == -1 ) {
// We're running under the classic Mac OS, so we simply return whether VM is on.
long response;
OSErr err = Gestalt( gestaltVMAttr, &response );
shouldHoldStack = !err && (response & (1L << gestaltVMPresent));
}
#endif
RequireBool( shouldHoldStack );
return( (Boolean) shouldHoldStack );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Fri, Jan 21, 2000 Created.
wolf Mon, Jan 24, 2000 Version 5. Simplified.
wolf Sun, Apr 9, 2000 Version 6. No changes.
************************************************************************************/
void
NewRedShedThreadHelper()
{
RedShedThread *thread = (RedShedThread*) GetRedShedThreadContextCookie();
RequireCurrentRedShedThread( thread );
RequireProcPtr( thread->entry );
thread->entry( thread );
// We may never get here -- someone may call DisposeRedShedThread()
// before the entry point returns.
DisposeRedShedThread( thread );
// We should never get here.
Require( false );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Mon, Jan 10, 2000 Created.
wolf Mon, Jan 24, 2000 Version 5.
wolf Sun, Apr 9, 2000 Version 6. Now uses Atomic Flags.
wolf Thu, Apr 20, 2000 Updated to match new Atomic Messaging interfaces.
************************************************************************************/
AtomicMessageElement*
IncomingAtomicMessageCallback(
AtomicMessageElement */*message*/,
AtomicMessageQueue *queue )
{
RedShedThread *thread = (RedShedThread*) queue->refCon;
RequirePtr( queue );
RequireRedShedThread( thread );
if( GetAtomicFlag( &thread->flags, kRedShedThreadWaitingForMessage ) ) {
PulseRedShedThread( thread );
}
return( nil );
}
See more files for this project here