home *** CD-ROM | disk | FTP | other *** search
- /****************************************************************************************
- atomicity.c
-
- The grabbing hands
- grab all they can.
- Everything counts
- in large amounts.
-
- Copyright © 1998-1999 Red Shed Software. All rights reserved.
- by Jonathan 'Wolf' Rentzsch (jon@redshed.net)
- This code requires a 68020 or later or any PowerPC.
-
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, Oct 26, 1998 Created as AtomicStacks.h/AtomicStacks.c.
- wolf Mon, Nov 9, 1998 PowerPC code rolled in.
- wolf Wed, Nov 18, 1998 Rolled in AtomicQueue code.
-
- Renamed to AtomicLists.h/AtomicLists.c.
- wolf Mon, Nov 23, 1998 Added guarded lists code.
- wolf Wed, Nov 25, 1998 Added ___Off functions and ___Type macros.
-
- Reworked file to be more comprehensible &
- navigatible.
- wolf Mon, Nov 30, 1998 PushGuardedAtomicStack() was modified to be atomic.
- wolf Wed, Dec 2, 1998 Added requirement checking.
- wolf Thu, Dec 24, 1998 Added StealGuardedAtomicStackType() macro.
- wolf Tue, Mar 23, 1999 Added conditionally compiled 'extern "C" {}'
- declaration.
- wolf Mon, Mar 29, 1999 Added Open Transport compatibility options.
-
- Now uses size_t and offsetof() instead of long and
- my offof() macro, respectively.
-
- Renamed to atomicity.h/atomicity.c.
- wolf Wed, Mar 31, 1999 Reworked OpenTransport compatibility & documented it.
-
- Added AtomicLocks.
-
- Now can stand without require.h (just comment it out
- in atomicity.c).
-
- The new public AtomicLocks code obsoleted my private
- IncrementGuardedAtomicStack() function, so the
- function has been removed and the Guarded Stack (and
- thus the Guarded Queue) code has been moved to Atomic
- Locks.
- wolf Thu, Apr 15, 1999 Added Peek[Guarded]AtomicQueue[Off|Type]().
- wolf Thu, Apr 22, 1999 Moved the OpenTransport compatibility settings into
- their own file: atomicity.config.h.
- wolf Fri, Apr 30, 1999 Added Peek[Guarded]AtomicStack[Off|Type].
- wolf Mon, May 3, 1999 Changed name of AtomicQueue's fields from (pushed,
- toPop) to (input, output) to ease understanding.
-
- Rearranged the parameter order of the ___Off and
- ___Type functions. Before it was ( OFFSET, STACK )
- and ( STRUCTURE, FIELD, STACK ). Now it's
- ( STACK, OFFSET ) and ( STACK, STRUCTURE, FIELD ).
- This breaks existing code, but the fix is simple and
- the new way is easier to remember. I wanted to make
- this change before I distributed my code on the
- MacHack 99 CD-ROM.
- wolf Fri, May 7, 1999 The Open Transport Compatibility Options weren't
- working correctly for some reason (apparently my
- concepts about what !defined() means is wrong).
- I reworked it a little and now all is well.
-
- ************************************************************************************/
-
- #include "atomicity.h"
- #include "require.h" // Comment out this line to disable requirement checking.
-
- #ifndef GenerateRequirements
- #define Require( CODE )
- #define RequirePtr( PTR )
- #define RequirePtrAlign( PTR, ALIGN )
- #define RequirePtrAlignIfNotNil( PTR, ALIGN )
- #endif
-
- #if defined(DontUseOpenTransport)
- #elif defined(OnlyUseOpenTransport)
- #else // UseOpenTransportIfAvailable
- Boolean
- IsOpenTransportAvailable();
- #endif
-
- /****************************************************************************************
- *
- * Atomic Stack Functions
- *
- ****************************************************************************************/
- #pragma mark --Atomic Stack Functions--
-
- /****************************************************************************************
- About the next three functions (MyPushAtomicStack(), MyPopAtomicStack() &
- MyStealAtomicStack()):
-
- These functions are written in assembly language to access atomic instructions
- inaccessable from a high-level language like C. We want to compile to differing
- platforms from a single source file, so we use conditional #defines to generate
- 68K or PowerPC code depending on what the compiler is generating.
-
- In addition to conditionally generating 68K or PowerPC assembly, when generating 68K
- code, we conditionally generate Classic 68K or CFM-68K code.
-
- Classic 68K:
- Classic 68K doesn't define a standard parameter passing convention, so we use
- the Pascal calling convention. We use the 'pascal' keyword so the compiler
- generates code that uses the Pascal calling convention.
-
- The Pascal calling convention requires the caller make space for the callee's
- result (if any) and all parameters. The parameters are pushed from left to right,
- followed by the return address. The callee is responsible for popping all
- paramaters before returning to the caller. The function result (if any) is left
- on the stack.
-
- The Pascal calling convention doesn't explictly state what registers should be
- saved, so we use the rather strict CFM-68K register preservation guideline, which
- requires we preserve registers d3-d7 and a2-a7. That leaves us with d0, d1, d2
- and a0, a1.
- CFM-68K:
- CFM-68K defines a standard parameter passing convention, so the 'pascal'
- keyword (required when generating Classic 68K code) is ignored.
-
- Under CFM-68K, parameters are pushed from right to left onto the stack,
- followed by the return address. The function's result (if any) is returned
- in register D0. CFM-68K requires we preserve registers d3-d7 and a2-a7. That
- leaves us with d0, d1, d2 and a0, a1.
- PowerPC:
- PowerPC code always uses CFM parameter passing conventions, so the 'pascal'
- keyword (required when generating Classic 68K code) is ignored.
-
- Under PowerPC, parameters are passed in registers left to right, starting with
- register r3. The function's result (if any) is stored in register r3.
-
- ************************************************************************************/
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, Oct 26, 1998 Created.
- wolf Mon, Nov 9, 1998 PowerPC code rolled in.
- wolf Tue, Mar 23, 1999 Rewrote PowerPC code. After more study of the
- programming model, I learned I need not save and
- restore the Link Register (I don't modify it) and
- the Condition Register (the cr0 field is marked
- volatile). This saves 8 instructions including
- two loads and two stores.
- However, I now use a more complex double-load-with-
- sync technique to eliminate the possibility of
- livelock in a multiprocessor environment. The
- overhead of the sync instruction probably negates
- the speed gain of omitting the 8 instructions.
- wolf Thu, Apr 1, 1999 Rewrote PowerPC code to be a tad more efficient.
-
- In C:
- AtomicElement *next = stack->next;
- element->next = next;
- stack->next = element;
-
- ************************************************************************************/
-
- asm
- pascal
- void
- MyPushAtomicStack(
- AtomicElement *element,
- AtomicStack *stack )
- {
- #if powerc
- //-------------------------------------------------
- // PowerPC
- lostReservation:
- lwz r5, 0(r4) // next1 = stack->next.
- stw r5, 0(r3) // element->next = next1.
- lwarx r6, 0, r4 // next2 = stack->next.
- cmpw r6, r5 // If next1 != next2
- bne- nextChanged // Then someone else progressed, try again.
- sync // Let the store settle.
- stwcx. r3, 0, r4 // If stack->next didn't change, stack->next = element.
- bne- lostReservation // Else someone else progressed, try again.
- sync // Broadcast update.
- blr // We're outta here.
- nextChanged:
- sync // Let the store settle.
- stwcx. r6, 0, r4 // Clear reservation created above.
- b lostReservation // Try again.
- #else // powerc
- #if GENERATINGCFM
- //-------------------------------------------------
- // CFM-68K
- movea.l 8(a7), a0 // stack.
- movea.l 4(a7), a1 // element.
- move.l a1, d0 // element.
- again:
- move.l (a0), d1 // next = stack->next.
- move.l d1, (a1) // element->next = next.
- dc.l 0x0ED00001 // CWPro2 doesn't know cas. Here's the raw opcode.
- // cas.l d1, d0, (a0) // If stack->next didn't change, stack->next = element.
- bne.s again // Else someone else progressed, try again.
- movea.l (a7)+, a0 // Pop the return address.
- addq.l #8, a7 // Pop the parameters.
- jmp (a0) // We're outta here.
- #else // GENERATINGCFM
- //-------------------------------------------------
- // Classic 68K
- movea.l 4(a7), a0 // stack.
- movea.l 8(a7), a1 // element.
- move.l a1, d0 // element.
- again:
- move.l (a0), d1 // next = stack->next.
- move.l d1, (a1) // element->next = next.
- dc.l 0x0ED00001 // CWPro2 doesn't know cas. Here's the raw opcode.
- // cas.l d1, d0, (a0) // If stack->next didn't change, stack->next = element.
- bne.s again // Else someone else progressed, try again.
- movea.l (a7)+, a0 // Pop the return address.
- addq.l #8, a7 // Pop the parameters.
- jmp (a0) // We're outta here.
- #endif // GENERATINGCFM
- #endif // powerc
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, Oct 26, 1998 Created.
- wolf Mon, Nov 9, 1998 PowerPC code rolled in.
- wolf Wed, Nov 25, 1998 Fixed function result bug in Classic 68K code.
- I was returning the result in the space allocated
- for the stack parameter, not the result space right
- above it.
- wolf Tue, Mar 23, 1999 Rewrote PowerPC code. After more study of the
- programming model, I learned I need not save and
- restore the Link Register (I don't modify it) and
- the Condition Register (the cr0 field is marked
- volatile). This saves 8 instructions including
- two loads and two stores.
- wolf Thu, Apr 1, 1999 I moved the test for a nil stack out of the
- reservation code, which should help performance.
-
- In C:
- AtomicElement *next, *element = stack->next;
- if( element != nil ) {
- next = element->next;
- stack->next = next;
- }
- return( element );
-
- ************************************************************************************/
-
- asm
- pascal
- AtomicElement*
- MyPopAtomicStack(
- AtomicStack *stack )
- {
- #if powerc
- //-------------------------------------------------
- // PowerPC
- lostReservation:
- lwz r4, 0(r3) // element1 = stack->next.
- cmpwi r4, 0 // If element1 == nil
- beq emptyStack // Then the stack is empty, return nil.
-
- lwarx r5, 0, r3 // element2 = stack->next.
- cmpw r5, r4 // If element1 != element2
- bne- stackChanged // Then someone else progressed, try again.
- lwz r6, 0(r5) // next = element2->next.
- stwcx. r6, 0, r3 // If the reservation wasn't lost, stack->next = next,
- bne- lostReservation // Else someone else progressed, try again.
- sync // Broadcast update.
- mr r3, r4 // element is in r4, but the result must be in r3. Move it.
- blr // We're outta here.
- stackChanged:
- stwcx. r5, 0, r3 // Clear reservation created above.
- b lostReservation // Try again.
- emptyStack:
- mr r3, r4 // element is in r4, but the result must be in r3. Move it.
- blr // We're outta here.
- #else // powerc
- #if GENERATINGCFM
- //-------------------------------------------------
- // CFM-68K
- movea.l 4(a7), a0 // stack.
- again:
- movea.l (a0), a1 // element = stack->next.
- move.l a1, d0 // element = stack->next.
- tst.l d0 // Is element == nil?
- beq.s done // If equal, return nil.
- move.l (a1), d1 // next = element->next.
- dc.l 0xED00040 // CWPro2 doesn't know cas. Here's the raw opcode.
- // cas.l d0, d1, (a0) // If stack->next wasn't changed, stack->next = next.
- bne.s again // Else someone else progressed, try again.
- done:
- movea.l (a7)+, a0 // Pop the return address.
- addq.l #4, a7 // Pop stack parameter.
- jmp (a0) // We're outta here.
- #else // GENERATINGCFM
- //-------------------------------------------------
- // Classic 68K
- movea.l 4(a7), a0 // stack.
- again:
- movea.l (a0), a1 // element = stack->next.
- move.l a1, d0 // element = stack->next.
- tst.l d0 // Is element == nil?
- beq.s done // If equal, return nil.
- move.l (a1), d1 // next = element->next.
- dc.l 0xED00040 // CWPro2 doesn't know cas. Here's the raw opcode.
- // cas.l d0, d1, (a0) // If stack->next wasn't changed, stack->next = next.
- bne.s again // Else someone else progressed, try again.
- done:
- movea.l (a7)+, a0 // Pop the return address.
- addq.l #4, a7 // Pop stack parameter.
- move.l d0, (a7) // Return the popped element on the stack.
- jmp (a0) // We're outta here.
- #endif // GENERATINGCFM
- #endif // powerc
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, Oct 26, 1998 Created.
- wolf Mon, Nov 9, 1998 PowerPC code rolled in.
- wolf Wed, Nov 25, 1998 Fixed function result bug in Classic 68K code.
- I was returning the result in the space allocated
- for the stack parameter, not the result space right
- above it.
- wolf Thu, Mar 4, 1999 On the PowerPC side, I mistakenly moved r4 (which
- is always set to nil) into the result register, r3.
- This resulted in StealAtomicStack() always returning
- nil when executing PowerPC. Now I correctly move r5
- into r3.
- It's very strange that I didn't catch this obvious
- error in my test matrix. Perhaps I forgot to test the
- PowerPC side and only tested the 68K sides.
- wolf Thu, Apr 1, 1999 I moved the test for a nil stack out of the
- reservation code, which should help performance.
-
- In C:
- AtomicElement *result = stack->next;
- stack->next = nil;
- return( result );
-
- ************************************************************************************/
-
- asm
- pascal
- AtomicElement*
- MyStealAtomicStack(
- AtomicStack *stack )
- {
- #if powerc
- //-------------------------------------------------
- // PowerPC
- li r4, 0
- lostReservation:
- lwz r5, 0(r3) // result1 = stack->next.
- cmpwi r5, 0 // If result1 == nil
- beq emptyStack // Then the stack is empty, return nil.
-
- lwarx r6, 0, r3 // result2 = stack->next.
- cmpw r6, r5 // If result1 != result2
- bne- stackChanged // Then someone else progressed, retry.
- stwcx. r4, 0, r3 // If the reservation wasn't lost, stack->next = nil
- bne- lostReservation // Else someone else progressed, try again.
- sync // Broadcast update.
- mr r3, r5 // element is in r4, but the result must be in r3. Move it.
- blr // We're outta here.
- stackChanged:
- stwcx. r6, 0, r3 // Clear reservation created above.
- b lostReservation // Try again.
- emptyStack:
- mr r3, r4 // Return nil.
- blr // We're outta here.
- #else // powerc
- #if GENERATINGCFM
- //-------------------------------------------------
- // CFM-68K
- movea.l 4(a7), a0 // stack.
- moveq.l #0, d1
- again:
- move.l (a0), d0 // element = stack->next.
- dc.l 0xED00040 // CWPro2 doesn't know cas. Here's the raw opcode.
- // cas.l d0, d1, (a0) // If stack->next wasn't changed, stack->next = nil.
- bne.s again // Else someone else progressed, try again.
-
- movea.l (a7)+, a0 // Pop the return address.
- addq.l #4, a7 // Pop stack parameter.
- jmp (a0) // We're outta here.
- #else // GENERATINGCFM
- //-------------------------------------------------
- // Classic 68K
- movea.l 4(a7), a0 // stack.
- moveq.l #0, d1
- again:
- move.l (a0), d0 // element = stack->next.
- dc.l 0xED00040 // CWPro2 doesn't know cas. Here's the raw opcode.
- // cas.l d0, d1, (a0) // If stack->next wasn't changed, stack->next = nil.
- bne.s again // Else someone else progressed, try again.
-
- movea.l (a7)+, a0 // Pop the return address.
- addq.l #4, a7 // Pop the stack parameter.
- move.l d0, (a7) // Return the popped element on the stack.
- jmp (a0) // We're outta here.
- #endif // GENERATINGCFM
- #endif // powerc
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Fri, Apr 30, 1999 Created.
-
- ************************************************************************************/
-
- pascal
- AtomicElement*
- PeekAtomicStack(
- AtomicStack *stack )
- {
- RequirePtrAlign( stack, 4 );
-
- return( stack->next );
- }
-
- /****************************************************************************************
- *
- * Atomic Stack Offset Functions
- *
- ****************************************************************************************/
- #pragma mark -
- #pragma mark --Atomic Stack Offset Functions--
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Wed, Nov 25, 1998 Created.
-
- ************************************************************************************/
-
- pascal
- void
- PushAtomicStackOff(
- void *element,
- AtomicStack *stack,
- size_t offset )
- {
- RequirePtrAlign( element, 4 );
- Require( offset < 0xFFFF );
- RequirePtrAlign( stack, 4 );
-
- PushAtomicStack( (AtomicElement*) (((char*) element) + offset), stack );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Wed, Nov 25, 1998 Created.
-
- ************************************************************************************/
-
- pascal
- AtomicElement*
- PopAtomicStackOff(
- AtomicStack *stack,
- size_t offset )
- {
- AtomicElement *result;
-
- Require( offset < 0xFFFF );
- RequirePtrAlign( stack, 4 );
-
- result = PopAtomicStack( stack );
-
- if( result )
- result = (AtomicElement*) (((char*) result) - offset);
-
- RequirePtrAlignIfNotNil( result, 4 );
- return( result );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Wed, Nov 25, 1998 Created.
-
- ************************************************************************************/
-
- pascal
- AtomicElement*
- StealAtomicStackOff(
- AtomicStack *stack,
- size_t offset )
- {
- AtomicElement *result;
-
- Require( offset < 0xFFFF );
- RequirePtrAlign( stack, 4 );
-
- result = StealAtomicStack( stack );
-
- if( result )
- result = (AtomicElement*) (((char*) result) - offset);
-
- RequirePtrAlignIfNotNil( result, 4 );
- return( result );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Fri, Apr 30, 1999 Created.
-
- ************************************************************************************/
-
- pascal
- AtomicElement*
- PeekAtomicStackOff(
- AtomicStack *stack,
- size_t offset )
- {
- AtomicElement *result;
-
- Require( offset < 0xFFFF );
- RequirePtrAlign( stack, 4 );
-
- result = PeekAtomicStack( stack );
-
- if( result )
- result = (AtomicElement*) (((char*) result) - offset);
-
- RequirePtrAlignIfNotNil( result, 4 );
- return( result );
- }
-
- /****************************************************************************************
- *
- * Atomic Lock Functions
- *
- ****************************************************************************************/
- #pragma mark -
- #pragma mark --Atomic Lock Functions--
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Wed, Mar 31, 1999 Created.
- wolf Thu, Apr 1, 1999 I moved the test for a nonzero lock out of the
- reservation code, which should help performance.
-
- In C:
-
- if( *lock == 0 ) {
- *lock = 1;
- return( true );
- } else
- return( false );
-
- ************************************************************************************/
-
- asm
- pascal
- long
- MyGrabAtomicLock(
- AtomicLock *lock )
- {
- #if powerc
- //-------------------------------------------------
- // PowerPC
- li r4, 1
- lostReservation:
- lwz r5, 0(r3) // result1 = *lock.
- cmpwi r5, 0 // If result != 0
- bne notZero // Then return false.
-
- lwarx r6, 0, r3 // result2 = *lock.
- cmpw r5, r6 // If result1 != result2
- bne- notEqual // Then someone else progressed, try again.
- stwcx. r4, 0, r3 // If the reservation wasn't lost, *lock = 1
- bne- lostReservation // Else someone else progressed, try again.
- sync // Broadcast update.
- li r3, 1 // Return true.
- blr // We're outta here.
- notEqual:
- stwcx. r6, 0, r3 // Clear reservation created above.
- b lostReservation // Try again.
- notZero:
- li r3, 0 // Return false.
- blr // We're outta here.
- #else // powerc
- #if GENERATINGCFM
- //-------------------------------------------------
- // CFM-68K
- movea.l (a7)+, a1 // Pop the return address.
- movea.l (a7)+, a0 // Pop lock parameter.
- moveq.l #0, d0 // 0.
- moveq.l #1, d1 // 1.
- dc.l 0xED00040 // CWPro2 doesn't know cas. Here's the raw opcode.
- // cas.l d0, d1, (a0) // If *lock == 0 then *lock = 1.
- bne fail
- moveq.l #1, d0 // Return true in register d0.
- jmp (a1) // We're outta here.
- fail:
- moveq.l #0, d0 // Return false in register d0.
- jmp (a1) // We're outta here.
- #else // GENERATINGCFM
- //-------------------------------------------------
- // Classic 68K
- movea.l (a7)+, a1 // Pop the return address.
- movea.l (a7)+, a0 // Pop lock parameter.
- moveq.l #0, d0 // 0.
- moveq.l #1, d1 // 1.
- dc.l 0xED00040 // CWPro2 doesn't know cas. Here's the raw opcode.
- // cas.l d0, d1, (a0) // If *lock == 0 then *lock = 1.
- bne fail
- move.l #1, (a7) // Return true on the stack.
- jmp (a1) // We're outta here.
- fail:
- move.l #0, (a7) // Return false on the stack.
- jmp (a1) // We're outta here.
- #endif // GENERATINGCFM
- #endif // powerc
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Tue, Mar 23, 1999 Created.
-
- ************************************************************************************/
-
- void
- ReleaseAtomicLock(
- AtomicLock *lock )
- {
- RequirePtrAlign( lock, 4 );
- *lock = 0;
- }
-
- /****************************************************************************************
- *
- * Open Transport Compatibility Layer
- *
- * The the following four functions (PushAtomicStack, PopAtomicStack, StealAtomicStack
- * and IsOpenTransportAvailable) are only compiled if UseOpenTransportIfAvailable is
- * defined. Otherwise zero-runtime-overhead macros are used.
- *
- ****************************************************************************************/
- #pragma mark -
- #pragma mark --Open Transport Compatibility Layer--
-
- #if defined(DontUseOpenTransport)
- #elif defined(OnlyUseOpenTransport)
- #else // UseOpenTransportIfAvailable
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, Mar 29, 1999 Created.
-
- ************************************************************************************/
-
- void
- PushAtomicStack(
- AtomicElement *element,
- AtomicStack *stack )
- {
- RequirePtrAlign( element, 4 );
- RequirePtrAlign( stack, 4 );
-
- if( IsOpenTransportAvailable() )
- OTLIFOEnqueue( (OTLIFO*) stack, (OTLink*) element );
- else
- MyPushAtomicStack( element, stack );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, Mar 29, 1999 Created.
-
- ************************************************************************************/
-
- AtomicElement*
- PopAtomicStack(
- AtomicStack *stack )
- {
- AtomicElement *result;
-
- RequirePtrAlign( stack, 4 );
-
- if( IsOpenTransportAvailable() )
- result = (AtomicElement*) OTLIFODequeue( (OTLIFO*) stack );
- else
- result = MyPopAtomicStack( stack );
-
- RequirePtrAlignIfNotNil( result, 4 );
- return( result );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, Mar 29, 1999 Created.
-
- ************************************************************************************/
-
- AtomicElement*
- StealAtomicStack(
- AtomicStack *stack )
- {
- AtomicElement *result;
-
- RequirePtrAlign( stack, 4 );
-
- if( IsOpenTransportAvailable() )
- result = (AtomicElement*) OTLIFOStealList( (OTLIFO*) stack );
- else
- result = MyStealAtomicStack( stack );
-
- RequirePtrAlignIfNotNil( result, 4 );
- return( result );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Wed, Mar 31, 1999 Created.
-
- ************************************************************************************/
-
- Boolean
- GrabAtomicLock(
- AtomicLock *lock )
- {
- Boolean result;
-
- RequirePtrAlign( lock, 4 );
-
- if( IsOpenTransportAvailable() )
- result = OTCompareAndSwap32( 0, 1, lock );
- else
- result = MyGrabAtomicLock( lock );
-
- return( result );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, Mar 29, 1999 Created.
-
- ************************************************************************************/
-
- Boolean
- IsOpenTransportAvailable()
- {
- static OSErr err = 1;
- long ignored;
-
- if( err == 1 ) {
- err = Gestalt( gestaltOpenTpt, &ignored );
- if( !err && OTLIFOEnqueue == nil )
- err = -1;
- }
-
- return( err ? false : true );
- }
-
- #endif // UseOpenTransportIfAvailable
-
- /****************************************************************************************
- *
- * Guarded Atomic Stack Functions
- *
- ****************************************************************************************/
- #pragma mark -
- #pragma mark --Guarded Atomic Stack Functions--
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, Nov 23, 1998 Created.
- wolf Mon, Nov 30, 1998 Broke out element count incrementing code into its
- own atomic function: IncrementGuardedAtomicStack().
- It takes three instructions to increment a value in
- memory on a PowerPC, so we need to use the special
- syncronization instructions offered by the PowerPC.
- wolf Wed, Mar 31, 1999 Migrated to Atomic Locks.
-
- ************************************************************************************/
-
- pascal
- long
- PushGuardedAtomicStack(
- GuardedAtomicElement *element,
- AtomicStack *stack )
- {
- Boolean result = GrabAtomicLock( &element->lock );
-
- RequirePtrAlign( element, 4 );
- RequirePtrAlign( stack, 4 );
-
- if( result )
- PushAtomicStack( (AtomicElement*) element, stack );
- return( result );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, Nov 23, 1998 Created.
- wolf Wed, Mar 31, 1999 Migrated to Atomic Locks.
-
- ************************************************************************************/
-
- pascal
- GuardedAtomicElement*
- PopGuardedAtomicStack(
- AtomicStack *stack )
- {
- GuardedAtomicElement *result;
-
- RequirePtrAlign( stack, 4 );
-
- result = (GuardedAtomicElement*) PopAtomicStack( stack );
-
- if( result )
- ReleaseAtomicLock( &result->lock );
-
- RequirePtrAlignIfNotNil( result, 4 );
- return( result );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, May 3, 1999 Created.
-
- ************************************************************************************/
-
- pascal
- GuardedAtomicElement*
- PeekGuardedAtomicStack(
- AtomicStack *stack )
- {
- RequirePtrAlign( stack, 4 );
-
- return( (GuardedAtomicElement*) stack->next );
- }
-
- /****************************************************************************************
- *
- * Guarded Atomic Stack Offset Functions
- *
- ****************************************************************************************/
- #pragma mark -
- #pragma mark --Guarded Atomic Stack Offset Functions--
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Wed, Nov 25, 1998 Created.
-
- ************************************************************************************/
-
- pascal
- long
- PushGuardedAtomicStackOff(
- void *element,
- AtomicStack *stack,
- size_t offset )
- {
- RequirePtr( element );
- Require( offset < 0xFFFF );
- RequirePtrAlign( stack, 4 );
-
- return( PushGuardedAtomicStack( (GuardedAtomicElement*) (((char*) element) + offset),
- stack ) );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Wed, Nov 25, 1998 Created.
-
- ************************************************************************************/
-
- pascal
- GuardedAtomicElement*
- PopGuardedAtomicStackOff(
- AtomicStack *stack,
- size_t offset )
- {
- GuardedAtomicElement *result;
-
- Require( offset < 0xFFFF );
- RequirePtrAlign( stack, 4 );
-
- result = PopGuardedAtomicStack( stack );
-
- if( result )
- result = (GuardedAtomicElement*) (((char*) result) - offset);
-
- RequirePtrAlignIfNotNil( result, 4 );
- return( result );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, May 3, 1999 Created.
-
- ************************************************************************************/
-
- pascal
- GuardedAtomicElement*
- PeekGuardedAtomicStackOff(
- AtomicStack *stack,
- size_t offset )
- {
- GuardedAtomicElement *result;
-
- Require( offset < 0xFFFF );
- RequirePtrAlign( stack, 4 );
-
- result = PeekGuardedAtomicStack( stack );
-
- if( result )
- result = (GuardedAtomicElement*) (((char*) result) - offset);
-
- RequirePtrAlignIfNotNil( result, 4 );
- return( result );
- }
-
- /****************************************************************************************
- *
- * Atomic Queue Functions
- *
- ****************************************************************************************/
- #pragma mark -
- #pragma mark --Atomic Queue Functions--
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Wed, Nov 18, 1998 Created.
-
- An atomic queue is simply a queue that uses an atomic stack as its input list.
-
- ************************************************************************************/
-
- pascal
- void
- PushAtomicQueue(
- AtomicElement *element,
- AtomicQueue *queue )
- {
- RequirePtrAlign( element, 4 );
- RequirePtrAlign( queue, 4 );
-
- PushAtomicStack( element, &queue->input );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Wed, Nov 18, 1998 Created.
-
- When the client wants the next element in the queue, we try to pop the element
- off the output stack. If the output stack is empty, then we have to refill the output
- stack from the input stack.
-
- We do this by atomically "stealing" the entire stack. Once we have the stack, we
- "reverse" the stack (make a first-in-last-out list into a first-in-first-out list) by
- popping each element from the stolen stack and pushing it onto the output stack.
-
- If all this stealing, popping and pushing sounds expensive, it isn't. The reversal
- operates in linear time, which is acceptable. However, if you don't need the
- sequential access of a queue, by all means use a stack, as it operates in fast
- constant time.
-
- ************************************************************************************/
-
- pascal
- AtomicElement*
- PopAtomicQueue(
- AtomicQueue *queue )
- {
- AtomicElement *next, *current;
-
- RequirePtrAlign( queue, 4 );
-
- current = PopAtomicStack( &queue->output );
-
- if( current == nil ) {
- // Nothing to pop. Refill the queue from the input stack.
- current = StealAtomicStack( &queue->input );
- while( current ) {
- RequirePtrAlign( current, 4 );
- next = current->next;
- PushAtomicStack( current, &queue->output );
- current = next;
- }
- current = PopAtomicStack( &queue->output );
- }
-
- RequirePtrAlignIfNotNil( current, 4 );
- return( current );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Thu, Apr 15, 1999 Created.
-
- ************************************************************************************/
-
- pascal
- AtomicElement*
- PeekAtomicQueue(
- AtomicQueue *queue )
- {
- AtomicElement *result;
-
- RequirePtrAlign( queue, 4 );
-
- result = PopAtomicQueue( queue );
-
- if( result )
- PushAtomicStack( result, &queue->output );
-
- RequirePtrAlignIfNotNil( result, 4 );
- return( result );
- }
-
- /****************************************************************************************
- *
- * Atomic Queue Offset Functions
- *
- ****************************************************************************************/
- #pragma mark -
- #pragma mark --Atomic Queue Offset Functions--
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Wed, Nov 25, 1998 Created.
-
- ************************************************************************************/
-
- pascal
- void
- PushAtomicQueueOff(
- void *element,
- AtomicQueue *queue,
- size_t offset )
- {
- RequirePtr( element );
- Require( offset < 0xFFFF );
- RequirePtrAlign( queue, 4 );
-
- PushAtomicQueue( (AtomicElement*) (((char*) element) + offset), queue );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Wed, Nov 25, 1998 Created.
-
- ************************************************************************************/
-
- pascal
- AtomicElement*
- PopAtomicQueueOff(
- AtomicQueue *queue,
- size_t offset )
- {
- AtomicElement *result;
-
- Require( offset < 0xFFFF );
- RequirePtrAlign( queue, 4 );
-
- result = PopAtomicQueue( queue );
-
- if( result )
- result = (AtomicElement*) (((char*) result) - offset);
-
- RequirePtrAlignIfNotNil( result, 4 );
- return( result );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Thu, Apr 15, 1999 Created.
-
- ************************************************************************************/
-
- pascal
- AtomicElement*
- PeekAtomicQueueOff(
- AtomicQueue *queue,
- size_t offset )
- {
- AtomicElement *result;
-
- Require( offset < 0xFFFF );
- RequirePtrAlign( queue, 4 );
-
- result = PeekAtomicQueue( queue );
-
- if( result )
- result = (AtomicElement*) (((char*) result) - offset);
-
- RequirePtrAlignIfNotNil( result, 4 );
- return( result );
- }
-
- /****************************************************************************************
- *
- * Guarded Atomic Queue Functions
- *
- ****************************************************************************************/
- #pragma mark -
- #pragma mark --Guarded Atomic Queue Functions--
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, Nov 23, 1998 Created.
-
- ************************************************************************************/
-
- pascal
- long
- PushGuardedAtomicQueue(
- GuardedAtomicElement *element,
- AtomicQueue *queue )
- {
- RequirePtrAlign( element, 4 );
- RequirePtrAlign( queue, 4 );
-
- return( PushGuardedAtomicStack( element, &queue->input ) );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Mon, Nov 23, 1998 Created.
-
- ************************************************************************************/
-
- pascal
- GuardedAtomicElement*
- PopGuardedAtomicQueue(
- AtomicQueue *queue )
- {
- GuardedAtomicElement *next, *current;
-
- RequirePtrAlign( queue, 4 );
-
- current = PopGuardedAtomicStack( &queue->output );
-
- if( current == nil ) {
- // Nothing to pop. Refill the queue from the input stack.
- current = (GuardedAtomicElement*) StealAtomicStack( &queue->input );
- while( current ) {
- RequirePtrAlign( current, 4 );
- next = current->next;
- PushAtomicStack( (AtomicElement*) current, &queue->output );
- current = next;
- }
- current = PopGuardedAtomicStack( &queue->output );
- }
-
- RequirePtrAlignIfNotNil( current, 4 );
- return( current );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Thu, Apr 15, 1999 Created.
-
- ************************************************************************************/
-
- pascal
- GuardedAtomicElement*
- PeekGuardedAtomicQueue(
- AtomicQueue *queue )
- {
- GuardedAtomicElement *next, *current;
-
- RequirePtrAlign( queue, 4 );
-
- if( queue->output.next == nil ) {
- // Nothing to pop. Refill the queue from the input stack.
- current = (GuardedAtomicElement*) StealAtomicStack( &queue->input );
- while( current ) {
- RequirePtrAlign( current, 4 );
- next = current->next;
- PushAtomicStack( (AtomicElement*) current, &queue->output );
- current = next;
- }
- }
-
- return( (GuardedAtomicElement*) queue->output.next );
- }
-
- /****************************************************************************************
- *
- * Guarded Atomic Queue Offset Functions
- *
- ****************************************************************************************/
- #pragma mark -
- #pragma mark --Guarded Atomic Queue Offset Functions--
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Wed, Nov 25, 1998 Created.
-
- ************************************************************************************/
-
- pascal
- long
- PushGuardedAtomicQueueOff(
- void *element,
- AtomicQueue *queue,
- size_t offset )
- {
- RequirePtr( element );
- Require( offset < 0xFFFF );
- RequirePtrAlign( queue, 4 );
-
- return( PushGuardedAtomicQueue( (GuardedAtomicElement*) (((char*) element) + offset ),
- queue ) );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Wed, Nov 25, 1998 Created.
-
- ************************************************************************************/
-
- pascal
- GuardedAtomicElement*
- PopGuardedAtomicQueueOff(
- AtomicQueue *queue,
- size_t offset )
- {
- GuardedAtomicElement *result;
-
- Require( offset < 0xFFFF );
- RequirePtrAlign( queue, 4 );
-
- result = PopGuardedAtomicQueue( queue );
-
- if( result )
- result = (GuardedAtomicElement*) (((char*) result) - offset);
-
- RequirePtrAlignIfNotNil( result, 4 );
- return( result );
- }
-
- /****************************************************************************************
- Commenter Date Comment
- --------- ----------------- -----------------------------------------------------
- wolf Thu, Apr 15, 1999 Created.
-
- ************************************************************************************/
-
- pascal
- GuardedAtomicElement*
- PeekGuardedAtomicQueueOff(
- AtomicQueue *queue,
- size_t offset )
- {
- GuardedAtomicElement *result;
-
- Require( offset < 0xFFFF );
- RequirePtrAlign( queue, 4 );
-
- result = PeekGuardedAtomicQueue( queue );
-
- if( result )
- result = (GuardedAtomicElement*) (((char*) result) - offset);
-
- RequirePtrAlignIfNotNil( result, 4 );
- return( result );
- }