Code Search for Developers
 
 
  

RedShedThreads.c from redshed at Krugle


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

redshed

Code for Mac+WebObjects.

Project homepage: http://sourceforge.net/projects/redshed
Programming language(s): C,Java,Objective C
License: other

  RedShedThreadContext.c
  RedShedThreadContext.h
  RedShedThreads.c
  RedShedThreads.h