// Copyright 1997-2000 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.
//
// $Header: /Network/Source/CVS/OmniGroup/Frameworks/OmniFoundation/Locks.subproj/OFSimpleLock-ppc.h,v 1.2 2000/07/20 22:28:00 bungi Exp $


#define OFSimpleLockDefined

#import <pthread.h>

typedef unsigned int OFSimpleLockBoolean;

typedef struct {
    OFSimpleLockBoolean locked;
} OFSimpleLockType;

#define OFSimpleLockIsNotLocked ((OFSimpleLockBoolean)0)
#define OFSimpleLockIsLocked ((OFSimpleLockBoolean)1)

static inline void OFSimpleLockInit(OFSimpleLockType *simpleLock)
{
    simpleLock->locked = OFSimpleLockIsNotLocked;
}

static inline void OFSimpleLockFree(OFSimpleLockType *simpleLock)
{
}

static inline OFSimpleLockBoolean
OFSimpleLockTry(OFSimpleLockType *simpleLock)
{
    OFSimpleLockBoolean result, tmp;
    OFSimpleLockBoolean *x;
    
    // We will read and write the memory attached to this pointer,
    // but will not change the pointer itself.  Thus, this is a
    // read-only argument to the asm below.  Also, we don't care
    // if people get bad results from reading the contents of
    // the lock -- they shouldn't do that.  So we don't declare
    // that we clobber "memory".
    x = &simpleLock->locked;
    
    asm volatile(
        "li     %0,1\n"      // we want to write a one (this is also our success code)
        "lwarx  %1,0,%2\n"   // load the current value in the lock
        "cmpwi  %1,0\n"      // if it is non-zero, we've failed
        "bne    $+16\n"      // branch to failure if necessary
        "stwcx. %0,0,%2\n"   // try to store our one
        "bne-   $-16\n"      // if we lost our reservation, try again
        "b      $+8\n"       // didn't loose our reservation, so we got it!
        "li     %0,0\n"      // failed!
        : "=&r" (result), "=&r" (tmp)
        : "r" (x)
        : "cc");
    
    // Require all previous instructions (but not memory accesses) to finish
    // before continuing.  I'm not exactly sure why we do this, but the PPC
    // manual suggests it.
    asm volatile ("isync");
    
    return result;
}

static inline void OFSimpleLock(OFSimpleLockType *simpleLock)
{
    // The whole reason we use this lock is because we are optimistic
    if (OFSimpleLockTry(simpleLock))
        return;
    
    do {
	while (simpleLock->locked) {
	    sched_yield();
	    continue;
	}
    } while (!OFSimpleLockTry(simpleLock));
}

static inline void OFSimpleUnlock(OFSimpleLockType *simpleLock)
{
    // Wait for all previously issued writes to complete and become
    // visible to all processors.
    asm volatile("sync");
    
    // Release the lock
    simpleLock->locked = OFSimpleLockIsNotLocked;
}
