// 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.


//#define PATCH_NSCONDITIONLOCK

#ifdef PATCH_NSCONDITIONLOCK

#import <OmniFoundation/NSLock-OFExtensions.h>

#import <Foundation/NSDate.h>
#import <Foundation/NSException.h>
#import <Foundation/NSString.h>
#import <OmniBase/OmniBase.h>
#import <pthread.h>
#import <string.h>
#import <stdio.h>
#import <math.h>

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OmniFoundation/OpenStepExtensions.subproj/NSLock-OFExtensions.m,v 1.9 2000/10/13 23:04:01 bungi Exp $")


#ifdef DEBUG
#warning DEBUG defined
#endif


//#define CLOBBER_NSCONDITION_LOCK_1
//#define CLOBBER_NSCONDITION_LOCK_2
#define CLOBBER_NSCONDITION_LOCK_3



#ifdef CLOBBER_NSCONDITION_LOCK_1

#warning Using _isLocked rewrite of NSConditionLock

//###############################################################
// Private pthreads stuff
//###############################################################


#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <mach/thread_switch.h>
#include <mach/mach.h>
#include <mach/sync.h>
#include <mach/sync_policy.h>

typedef long pthread_lock_t;

/*
 * Mutex variables
 */
typedef struct my_pthread_mutex
{
        volatile pthread_lock_t lock;         /* Used for internal mutex on structure */
        volatile pthread_t      owner;        /* Which thread has this mutex locked */
        volatile int           waiters:31,            /* Count of threads waiting for this mutex */
                        cond_lock:1;    /* Is there a condition locking me? */
        mach_port_t    sem;           /* Semaphore used for waiting */
} my_pthread_mutex_t;

typedef struct _pthread_cond
{
        volatile pthread_lock_t lock;        /* Used for internal mutex on structure */
        mach_port_t    sem;          /* Kernel semaphore */
        volatile int           waiters;      /* Number of threads waiting */
} my_pthread_cond_t;


#define LOCK_INIT(l)    ((l) = 0)
#define LOCK(v) \
        if (__is_threaded) { \
                while (!_spin_lock_try((pthread_lock_t *)&v)) { \
                    syscall_thread_switch(THREAD_NULL, SWITCH_OPTION_WAIT,1); \
                } \
        }
#define UNLOCK(v) if (__is_threaded) _spin_unlock((pthread_lock_t *)&v)
#ifndef ESUCCESS
#define ESUCCESS 0
#endif

extern int __is_threaded;
extern int  _spin_lock_try(pthread_lock_t *lockp);
extern void _spin_unlock(pthread_lock_t *lockp);
extern kern_return_t syscall_thread_switch(thread_t, int, int);

//#define LOG
#if defined(LOG)
#warning Lock logging enabled

typedef struct _log
{
        int lock;
        const char *op;
        int tbr;
} log_t;

static volatile int logcount = 0;
static log_t *tracelog = NULL;
static const size_t logsize = 4 * 1024 * 1024;
pthread_lock_t loglock;

static inline int getTBR()
{
    register int result;
    
    asm volatile ("mftb %0,268" : "=r" (result));
    return result;
}

static void _log(int self, const char *op)
{
        LOCK(loglock);
        if (logcount >= logsize) logcount = 0;
        tracelog[logcount].lock = self;
        tracelog[logcount].op = op;
        tracelog[logcount++].tbr = getTBR();
        UNLOCK(loglock);
}

static void _printLogs()
{
    int i;
    
    for (i = logcount; i != (logcount - 1) % logsize; i = (i + 1) % logsize) {
        if (tracelog[i].tbr) {
            fprintf(stderr, "i=0x%08x lock=0x%08x tbr=0x%08x op=%s\n",
                    i, tracelog[i].lock, tracelog[i].tbr, tracelog[i].op);
        }
    }
}

#else
#define _log(a, b)
#endif

//###############################################################

static int
my_pthread_mutex_init(my_pthread_mutex_t *mutex)
{
        kern_return_t kern_res;
        
#if defined(LOG)
        if (!tracelog) {
            tracelog = (log_t *)calloc(logsize, sizeof(log_t));
            LOCK_INIT(loglock);
        }
#endif

        LOCK_INIT(mutex->lock);
        mutex->owner = (pthread_t)NULL;
        mutex->waiters = 0;
        mutex->cond_lock = 0;
        kern_res = semaphore_create(mach_task_self(),
                                &mutex->sem,
                                SYNC_POLICY_FIFO,
                                0);
        if (kern_res != KERN_SUCCESS)
        {
                return (ENOMEM);
        } else
        {
                return (ESUCCESS);
        }
}

static int
my_pthread_mutex_destroy(my_pthread_mutex_t *mutex)
{
        kern_return_t kern_res;
        kern_res = semaphore_destroy(mach_task_self(), mutex->sem);
        if (kern_res != KERN_SUCCESS)
            return EPERM;
        return ESUCCESS;
}


static int
my_pthread_mutex_lock(my_pthread_mutex_t *mutex, int self)
{
        kern_return_t kern_res;

        LOCK(mutex->lock);
#if 0
        if (mutex->waiters || mutex->owner != (pthread_t)NULL)
#else
        while (mutex->owner != (pthread_t)NULL)
#endif
        {
                mutex->waiters++;
                _log(self, "going in to sem_wait");
                UNLOCK(mutex->lock);
                kern_res = semaphore_wait(mutex->sem);
                LOCK(mutex->lock);
                mutex->waiters--;
                _log(self, "woke up from sem_wait");
                if (mutex->cond_lock) {
                        _log(self, "clearing cond_lock");
                        mutex->cond_lock = 0;
#if 0
#else
                        break;
#endif
                }
        }
        _log(self, "got lock");
        mutex->owner = (pthread_t)0x12141968;
        UNLOCK(mutex->lock);
        return (ESUCCESS);
}

static int
my_pthread_mutex_unlock(my_pthread_mutex_t *mutex, int self)
{
        kern_return_t kern_res;
        int waiters;
        
        LOCK(mutex->lock);
        mutex->owner = (pthread_t)NULL;
        waiters = mutex->waiters;
        UNLOCK(mutex->lock);
        if (waiters)
        {
            _log(self, "calling sem_signal");
            kern_res = semaphore_signal(mutex->sem);
        } else {
            _log(self, "no waiters for signal");
        }
        return (ESUCCESS);
}

/*
 * Initialize a condition variable.  Note: 'attr' is ignored.
 */
static int       
my_pthread_cond_init(my_pthread_cond_t *cond)
{
        kern_return_t kern_res;
        LOCK_INIT(cond->lock);
        cond->waiters = 0;
        kern_res = semaphore_create(mach_task_self(),
                                   &cond->sem,
                                   SYNC_POLICY_FIFO,
                                   0);
        if (kern_res != KERN_SUCCESS)
        {
                return (ENOMEM);
        }
        return (ESUCCESS);
}

static int
my_pthread_cond_destroy(my_pthread_cond_t *cond)
{
        kern_return_t kern_res;
        kern_res = semaphore_destroy(mach_task_self(), cond->sem);
        if (kern_res != KERN_SUCCESS)
            return EPERM;
        return ESUCCESS;
}

/*
 * Signal a condition variable, waking up all threads waiting for it.
 */
static int       
my_pthread_cond_broadcast(my_pthread_cond_t *cond, int self)
{
        kern_return_t kern_res;
        int waiters;
        
        LOCK(cond->lock);
        waiters = cond->waiters;
        if (cond->waiters == 0)
        { /* Avoid kernel call since there are no waiters... */
                UNLOCK(cond->lock);
                _log(self, "no waiters for broadcast");
                return (ESUCCESS);
        }
        UNLOCK(cond->lock);
        _log(self, "sending broadcast");
        kern_res = semaphore_signal_all(cond->sem);
        if (kern_res == KERN_SUCCESS)
        {
                return (ESUCCESS);
        } else
        {
                return (EINVAL);
        }
}

static int       
my_pthread_cond_wait(my_pthread_cond_t *cond, my_pthread_mutex_t *mutex, int self)
{
        int res;
        kern_return_t kern_res;

        LOCK(cond->lock);
        cond->waiters++;
        UNLOCK(cond->lock);
        LOCK(mutex->lock);
        mutex->cond_lock = 1;
        _log(self, "going in to sem_wait_signal");
        UNLOCK(mutex->lock);
        kern_res = semaphore_wait_signal(cond->sem, mutex->sem);
        LOCK(cond->lock);
        cond->waiters--;
        _log(self, "woke up from sem_wait_signal");
        UNLOCK(cond->lock);
        if ((res = my_pthread_mutex_lock(mutex, self)) != ESUCCESS)
        {
                return (res);
        }
        if (kern_res == KERN_SUCCESS)
        {
                return (ESUCCESS);
        } else
        {
                return (EINVAL);
        }
}






static inline int _compute_relative_sleep_time(const struct timespec *abstime, mach_timespec_t *relative)
{
        struct timespec now;
        struct timeval tv;

        gettimeofday(&tv, NULL);
        TIMEVAL_TO_TIMESPEC(&tv, &now);

        /* Compute relative time to sleep */
        relative->tv_nsec = abstime->tv_nsec - now.tv_nsec;
        relative->tv_sec = abstime->tv_sec - now.tv_sec;
        if (relative->tv_nsec < 0)
        {
                relative->tv_nsec += 1000000000;  /* nsec/sec */
                relative->tv_sec--;
        }
        if (((int)relative->tv_sec < 0) ||
            ((relative->tv_sec == 0) && (relative->tv_nsec == 0)))
        {
                return ETIMEDOUT;
        }
        
        return 0;
}


static int       
my_pthread_cond_timedwait(my_pthread_cond_t *cond, my_pthread_mutex_t *mutex, const struct timespec *abstime, int self)
{
        int res;
        kern_return_t kern_res;
        mach_timespec_t waitTime;

        LOCK(cond->lock);
        cond->waiters++;
        UNLOCK(cond->lock);
        LOCK(mutex->lock);
        mutex->cond_lock = 1;
        _log(self, "going in to sem_wait_signal");
        UNLOCK(mutex->lock);
        
        res = _compute_relative_sleep_time(abstime, &waitTime);
        if (res) {
            // Timed out already -- don't wait
            kern_res = KERN_FAILURE;
        } else {
            kern_res = semaphore_timedwait_signal(cond->sem, mutex->sem, waitTime);
        }
        
        LOCK(cond->lock);
        cond->waiters--;
        _log(self, "woke up from sem_wait_signal");
        UNLOCK(cond->lock);
        if ((res = my_pthread_mutex_lock(mutex, self)) != ESUCCESS)
        {
                return (res);
        }
        if (kern_res == KERN_SUCCESS)
        {
                return (ESUCCESS);
        } else
        {
                return (EINVAL);
        }
}



@interface OFConditionLock : NSObject
{
@public
    my_pthread_mutex_t _mutex;
    my_pthread_cond_t  _condition;
#ifdef DEBUG
    pthread_t       _lockingThread;
#endif
    volatile BOOL   _isLocked;
    volatile int    _state;
}

- (id)initWithCondition:(int)condition;

- (int)condition;
- (void)lockWhenCondition:(int)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(int)condition;
- (void)unlockWithCondition:(int)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(int)condition beforeDate:(NSDate *)limit;

@end

static inline void _LOCK(my_pthread_mutex_t *m, int self)
{
    int rc;
    
    if ((rc = my_pthread_mutex_lock(m, self)))
        [NSException raise: NSInternalInconsistencyException
                    format: @"my_pthread_mutex_lock() returned %d, %s", rc, strerror(rc)];
}

static inline void _WAIT(my_pthread_cond_t *c, my_pthread_mutex_t *m, int self)
{
    int rc;
    
    if ((rc = my_pthread_cond_wait(c, m, self)))
        [NSException raise: NSInternalInconsistencyException
                    format: @"my_pthread_cond_wait() returned %d, %s", rc, strerror(rc)];
}

static inline void _UNLOCK(my_pthread_cond_t *c, my_pthread_mutex_t *m, int self)
{
    int rc;
    
    if (c) {
        if ((rc = my_pthread_cond_broadcast(c, self)))
            [NSException raise: NSInternalInconsistencyException
                        format: @"my_pthread_cond_broadcast() returned %d, %s", rc, strerror(rc)];
    }
    if ((rc = my_pthread_mutex_unlock(m, self)))
        [NSException raise: NSInternalInconsistencyException
                    format: @"my_pthread_mutex_unlock() returned %d, %s", rc, strerror(rc)];
}

#ifdef DEBUG
#define _SET_LOCKER() _lockingThread = pthread_self()
#define _CHECK_AND_CLEAR_LOCKER() \
do { \
    if (_lockingThread != pthread_self()) \
        [NSException raise: NSInternalInconsistencyException \
                    format: @"Attempted to unlock a lock not locked by the current thread."]; \
    _lockingThread = THREAD_NULL; \
} while(0)
#else
#define _SET_LOCKER()
#define _CHECK_AND_CLEAR_LOCKER()
#endif


@implementation OFConditionLock

#ifdef DEBUG
+ (void) initialize;
{
    [super initialize];
    fprintf(stderr, "*** Using _isLocked rewrite of NSConditionLock -- remove as soon as pthreads bug is fixed in MacOS X\n");
}
#endif

- init;
{
    return [self initWithCondition: 0];
}

- (id)initWithCondition:(int)condition;
{
    int rc;

    if ((rc = my_pthread_mutex_init(&_mutex)))
        [NSException raise: @"OFConditionLockInitializationFailed"
                    format: @"my_pthread_mutex_init returned %d, %s", rc, strerror(rc)];
    
    if ((rc = my_pthread_cond_init(&_condition)))
        [NSException raise: @"OFConditionLockInitializationFailed"
                    format: @"my_pthread_cond_init returned %d, %s", rc, strerror(rc)];
    
    _isLocked = NO;
    _state = condition;

    return self;
}

- (void) dealloc;
{
    int rc;
    
    if (_isLocked)
        [NSException raise: NSInternalInconsistencyException
                    format: @"Attempted to deallocate an OFConditionLock while it is locked"];
                
    if ((rc = my_pthread_mutex_destroy(&_mutex)))
        NSLog(@"-[OFCondition dealloc] -- my_pthread_mutex_destroy returned %d, %s", rc, strerror(rc));
    if ((rc = my_pthread_cond_destroy(&_condition)))
        NSLog(@"-[OFCondition dealloc] -- my_pthread_cond_destroy returned %d, %s", rc, strerror(rc));
    [super dealloc];
}

//
// NSLock API
//

- (void) lock;
{
    //fprintf(stderr, "thread:%d/lock:0x%08x  lock\n", pthread_self(), (unsigned int)self);

    _LOCK(&_mutex, (int)self);
    while (_isLocked) {
        _WAIT(&_condition, &_mutex, (int)self);
    }
    
    _isLocked = YES;
    _SET_LOCKER();

    _UNLOCK(NULL, &_mutex, (int)self);

    //fprintf(stderr, "thread:%d/lock:0x%08x  got lock\n", pthread_self(), (unsigned int)self);
}

- (void) unlock;
{
    //fprintf(stderr, "thread:%d/lock:0x%08x  unlock\n", pthread_self(), (unsigned int)self);

    _LOCK(&_mutex, (int)self);
    _CHECK_AND_CLEAR_LOCKER();
    _isLocked = NO;
    _UNLOCK(&_condition, &_mutex, (int)self);
}

//
// NSConditionLock API
//

- (int)condition;
{
    return _state;
}

- (void)lockWhenCondition:(int)condition;
{
    //fprintf(stderr, "thread:%d/lock:0x%08x  lockWhenCondition: %d\n", pthread_self(), (unsigned int)self, condition);

    _LOCK(&_mutex, (int)self);

    while (_isLocked || _state != condition) {
        //fprintf(stderr, "thread:%d/lock:0x%08x  condition was %d, waiters now %d\n", pthread_self(), (unsigned int)self, _state, _waiters);
        _WAIT(&_condition, &_mutex, (int)self);
    }

    //fprintf(stderr, "thread:%d/lock:0x%08x  condition is right\n", pthread_self(), (unsigned int)self);
    
    _isLocked = YES;
    _SET_LOCKER();

    _UNLOCK(NULL, &_mutex, (int)self);
}

- (BOOL)tryLock;
{
    _LOCK(&_mutex, (int)self);
    
    if (_isLocked) {
        _UNLOCK(NULL, &_mutex, (int)self);
        return NO;
    }

    _isLocked = YES;
    _SET_LOCKER();
    
    _UNLOCK(NULL, &_mutex, (int)self);
    return YES;
}

- (BOOL)tryLockWhenCondition:(int)condition;
{
    _LOCK(&_mutex, (int)self);
    
    if (_isLocked || _state != condition) {
        _UNLOCK(NULL, &_mutex, (int)self);
        return NO;
    }

    _isLocked = YES;
    _SET_LOCKER();
    
    _UNLOCK(NULL, &_mutex, (int)self);
    return YES;
}

- (void)unlockWithCondition:(int)condition;
{
    _LOCK(&_mutex, (int)self);
    
    _CHECK_AND_CLEAR_LOCKER();

    //fprintf(stderr, "thread:%d/lock:0x%08x  unlockWithCondition: %d, waiters now %d\n", pthread_self(), (unsigned int)self, condition, _waiters);

    _isLocked = NO;
    _state = condition;
    
    _UNLOCK(&_condition, &_mutex, (int)self);
}

- (BOOL)lockBeforeDate:(NSDate *)limit;
{
    NSTimeInterval ti, tiNow, seconds;
    struct timespec ts;
    
    tiNow = [limit timeIntervalSinceNow];
    
    //fprintf(stderr, "thread:%d/lock:0x%08x  lockBeforeDate: %f\n", pthread_self(), (unsigned int)self, tiNow);

    _LOCK(&_mutex, (int)self);

    // If the specified time is in the past, we should still try at least once.
    // This also solves overflow problems with massively negative time intervals
    // (like +[NSDate distantPast]).  The code below should handle small negative
    // times, but this will catch the huge ones.
    if (tiNow < 0.0) {
        if (_isLocked) {
            _UNLOCK(NULL, &_mutex, (int)self);
            return NO;
        } else {
            _UNLOCK(NULL, &_mutex, (int)self);
            return YES;
        }
    }
    
    // Unix time (assuming a signed number of seconds) allows for about 68.04 years
    // (2147483648/(365.25 * 24 * 60 * 60)).  Since it started in 1970, we can only
    // handle times up to about 2038 before we overflow the Unix date struct.
    // This handles huge positive time intervals from dates like +[NSDate distantFuture].
    ti = [limit timeIntervalSinceReferenceDate] + NSTimeIntervalSince1970;
    if (ti > 2147483647.0) {
        ti = 2147483647.0;
    }
     
    //fprintf(stderr, "thread:%d/lock:0x%08x  ti: %f\n", pthread_self(), (unsigned int)self, ti);

    seconds = floor(ti);
    ts.tv_sec = seconds;
    ts.tv_nsec = (ti - seconds) * 1.0e9;

    while (_isLocked) {
        if (my_pthread_cond_timedwait(&_condition, &_mutex, &ts, (int)self)) {
            // Expired
            _UNLOCK(NULL, &_mutex, (int)self);
            return NO;
        }
    }
    
    _SET_LOCKER();
    _UNLOCK(NULL, &_mutex, (int)self);
    
    return YES;
}

- (BOOL)lockWhenCondition:(int)condition beforeDate:(NSDate *)limit;
{
    NSTimeInterval ti, tiNow, seconds;
    struct timespec ts;

    tiNow = [limit timeIntervalSinceNow];
    
    //fprintf(stderr, "thread:%d/lock:0x%08x  lockBeforeDate: %f\n", pthread_self(), (unsigned int)self, tiNow);

    _LOCK(&_mutex, (int)self);

    // If the specified time is in the past, we should still try at least once.
    // This also solves overflow problems with massively negative time intervals
    // (like +[NSDate distantPast]).  The code below should handle small negative
    // times, but this will catch the huge ones.
    if (tiNow < 0.0) {
        if (_isLocked || _state != condition) {
            _UNLOCK(NULL, &_mutex, (int)self);
            return NO;
        } else {
            _UNLOCK(NULL, &_mutex, (int)self);
            return YES;
        }
    }
    
    // Unix time (assuming a signed number of seconds) allows for about 68.04 years
    // (2147483648/(365.25 * 24 * 60 * 60)).  Since it started in 1970, we can only
    // handle times up to about 2038 before we overflow the Unix date struct.
    // This handles huge positive time intervals from dates like +[NSDate distantFuture].
    ti = [limit timeIntervalSinceReferenceDate] + NSTimeIntervalSince1970;
    if (ti > 2147483647.0) {
        ti = 2147483647.0;
    }
     
    //fprintf(stderr, "thread:%d/lock:0x%08x  ti: %f\n", pthread_self(), (unsigned int)self, ti);

    seconds = floor(ti);
    ts.tv_sec = seconds;
    ts.tv_nsec = (ti - seconds) * 1.0e9;

    while (_isLocked || _state != condition) {
        if (my_pthread_cond_timedwait(&_condition, &_mutex, &ts, (int)self)) {
            // Expired
            _UNLOCK(NULL, &_mutex, (int)self);
            return NO;
        }
    }
    
    _SET_LOCKER();
    _UNLOCK(NULL, &_mutex, (int)self);
    
    return YES;
}


@end


@interface NSConditionLock (Hack)
+ (id) allocWithZone: (NSZone *) zone;
@end

@implementation NSConditionLock (Hack)
+ (id) allocWithZone: (NSZone *) zone;
{
    return NSAllocateObject([OFConditionLock class], 0, zone);
}
@end


#endif // CLOBBER_NSCONDITION_LOCK_1

//############################################################################
//############################################################################
//############################################################################

#ifdef CLOBBER_NSCONDITION_LOCK_2

// This version has a subtle bug in probing locks with tryLockWhenCondition: and lockWhenCondition:beforeDate: (when the date is in the past).  Two threads can probe the lock and since we actually take the mutex and hold it, we can only do a try_lock in each probe.  If one thread gets the lock and doesnt' like the condition, the other thread may have failed on try_lock even though it would have liked the condition.  Thus, neither thread would get the lock.


#warning Using pthread_mutex_lock_timeout_np rewrite of NSConditionLock

static int pthread_mutex_lock_timeout_np(pthread_mutex_t *m, const struct timespec *abstime);

@interface OFConditionLock : NSObject
{
@public
    pthread_mutex_t _mutex;
    pthread_cond_t  _condition;
#ifdef DEBUG
    pthread_t       _lockingThread;
#endif
    volatile int    _waiters;
    volatile int    _state;
}

- (id)initWithCondition:(int)condition;

- (int)condition;
- (void)lockWhenCondition:(int)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(int)condition;
- (void)unlockWithCondition:(int)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(int)condition beforeDate:(NSDate *)limit;

@end


@implementation OFConditionLock

#ifdef DEBUG
+ (void) initialize;
{
    [super initialize];
    fprintf(stderr, "*** Using pthread_mutex_lock_timeout_np rewrite of NSConditionLock -- remove as soon as pthreads bug is fixed in MacOS X\n");
}
#endif

- init;
{
    return [self initWithCondition: 0];
}

- (id)initWithCondition:(int)condition;
{
    int rc;

    if ((rc = pthread_mutex_init(&_mutex, NULL)))
        [NSException raise: @"OFConditionLockInitializationFailed"
                    format: @"pthread_mutex_init returned %d, %s", rc, strerror(rc)];
    
    if ((rc = pthread_cond_init(&_condition, NULL)))
        [NSException raise: @"OFConditionLockInitializationFailed"
                    format: @"pthread_cond_init returned %d, %s", rc, strerror(rc)];
    
    _state = condition;
    _waiters = 0;

    return self;
}

- (void) dealloc;
{
    int rc;
    
    if (_waiters)
        [NSException raise: NSInternalInconsistencyException
                    format: @"Attempted to deallocate an OFConditionLock while there were waiters"];
                
    if ((rc = pthread_mutex_destroy(&_mutex)))
        NSLog(@"-[OFCondition dealloc] -- pthread_mutex_destroy returned %d, %s", rc, strerror(rc));
    if ((rc = pthread_cond_destroy(&_condition)))
        NSLog(@"-[OFCondition dealloc] -- pthread_cond_destroy returned %d, %s", rc, strerror(rc));
    [super dealloc];
}

//
// NSLock API
//

- (void) lock;
{
    int rc;

    //fprintf(stderr, "thread:%d/lock:0x%08x  lock\n", pthread_self(), (unsigned int)self);

    if ((rc = pthread_mutex_lock(&_mutex)))
        [NSException raise: NSInternalInconsistencyException
                    format: @"pthread_mutex_lock() returned %d, %s", rc, strerror(rc)];
#ifdef DEBUG
    _lockingThread = pthread_self();
#endif

    //fprintf(stderr, "thread:%d/lock:0x%08x  got lock\n", pthread_self(), (unsigned int)self);
}

- (void) unlock;
{
    int rc;

    //fprintf(stderr, "thread:%d/lock:0x%08x  unlock\n", pthread_self(), (unsigned int)self);

#ifdef DEBUG
    if (_lockingThread != pthread_self())
        // This will leak the lock, of course
        [NSException raise: NSInternalInconsistencyException
                    format: @"Attempted to unlock a lock not locked by the current thread."];
    _lockingThread = THREAD_NULL;
#endif

    if ((rc = pthread_mutex_unlock(&_mutex)))
        [NSException raise: NSInternalInconsistencyException
                    format: @"pthread_mutex_unlock() returned %d, %s", rc, strerror(rc)];
}

//
// NSConditionLock API
//

- (int)condition;
{
    return _state;
}

- (void)lockWhenCondition:(int)condition;
{
    int rc;

    //fprintf(stderr, "thread:%d/lock:0x%08x  lockWhenCondition: %d\n", pthread_self(), (unsigned int)self, condition);

    if ((rc = pthread_mutex_lock(&_mutex)))
        [NSException raise: NSInternalInconsistencyException
                    format: @"pthread_mutex_lock() returned %d, %s", rc, strerror(rc)];

    //fprintf(stderr, "thread:%d/lock:0x%08x  got lock\n", pthread_self(), (unsigned int)self);

    while (_state != condition) {
        _waiters++;
        //fprintf(stderr, "thread:%d/lock:0x%08x  condition was %d, waiters now %d\n", pthread_self(), (unsigned int)self, _state, _waiters);
        if ((rc = pthread_cond_wait(&_condition, &_mutex))) {
            // This will leak the lock
            [NSException raise: NSInternalInconsistencyException
                        format: @"pthread_cond_wait() returned %d, %s", rc, strerror(rc)];
        }
        _waiters--;
    }

    //fprintf(stderr, "thread:%d/lock:0x%08x  condition is right\n", pthread_self(), (unsigned int)self);
    
#ifdef DEBUG
    _lockingThread = pthread_self();
#endif
}

- (BOOL)tryLock;
{
    if (pthread_mutex_trylock(&_mutex))
        return NO;

#ifdef DEBUG
    _lockingThread = pthread_self();
#endif
    
    return YES;
}

- (BOOL)tryLockWhenCondition:(int)condition;
{
    BOOL success;
    
    if (pthread_mutex_trylock(&_mutex))
        return NO;
        
    if (_state == condition) {
#ifdef DEBUG
        _lockingThread = pthread_self();
#endif
        success = YES;
    } else
        success = NO;
    
    pthread_mutex_unlock(&_mutex);
    return success;
}

- (void)unlockWithCondition:(int)condition;
{
    int rc;

#ifdef DEBUG
    if (_lockingThread != pthread_self())
        // This will leak the lock, of course
        [NSException raise: NSInternalInconsistencyException
                    format: @"Attempted to unlock a lock not locked by the current thread."];
    _lockingThread = THREAD_NULL;
#endif

    //fprintf(stderr, "thread:%d/lock:0x%08x  unlockWithCondition: %d, waiters now %d\n", pthread_self(), (unsigned int)self, condition, _waiters);

    _state = condition;
    if (_waiters) {
        if ((rc = pthread_cond_broadcast(&_condition))) {
            // This probably means some thread will hang when it shouldn't
            [NSException raise: NSInternalInconsistencyException
                        format: @"pthread_cond_broadcast() returned %d, %s", rc, strerror(rc)];
        }
    }

    if ((rc = pthread_mutex_unlock(&_mutex)))
        // This probably means the lock will get leaked
        [NSException raise: NSInternalInconsistencyException
                    format: @"pthread_mutex_unlock() returned %d, %s", rc, strerror(rc)];
}

- (BOOL)lockBeforeDate:(NSDate *)limit;
{
    NSTimeInterval ti, tiNow, seconds;
    struct timespec ts;
    int rc;

    tiNow = [limit timeIntervalSinceNow];
    
    //fprintf(stderr, "thread:%d/lock:0x%08x  lockBeforeDate: %f\n", pthread_self(), (unsigned int)self, tiNow);

    // If the specified time is in the past, we should still try at least once.
    // This also solves overflow problems with massively negative time intervals
    // (like +[NSDate distantPast]).  The code below should handle small negative
    // times, but this will catch the huge ones.
    if (tiNow < 0.0) {
        if (!pthread_mutex_trylock(&_mutex))
            return YES;
    }
    
    // Unix time (assuming a signed number of seconds) allows for about 68.04 years
    // (2147483648/(365.25 * 24 * 60 * 60)).  Since it started in 1970, we can only
    // handle times up to about 2038 before we overflow the Unix date struct.
    // This handles huge positive time intervals from dates like +[NSDate distantFuture].
    ti = [limit timeIntervalSinceReferenceDate] + NSTimeIntervalSince1970;
    if (ti > 2147483647.0) {
        ti = 2147483647.0;
    }
     
    //fprintf(stderr, "thread:%d/lock:0x%08x  ti: %f\n", pthread_self(), (unsigned int)self, ti);

    seconds = floor(ti);
    ts.tv_sec = seconds;
    ts.tv_nsec = (ti - seconds) * 1.0e9;

    rc = pthread_mutex_lock_timeout_np(&_mutex, &ts);
    if (rc == ETIMEDOUT)
        return NO;
    if (rc) {
        fprintf(stderr, "-[OFConditionLock lockWhenCondition:beforeDate:] -- pthread_mutex_lock_timeout_np returned %d, %s\n", rc, strerror(rc));
        return NO;
    }

    //fprintf(stderr, "thread:%d/lock:0x%08x  got lock\n", pthread_self(), (unsigned int)self);
    
#ifdef DEBUG
    _lockingThread = pthread_self();
#endif
    return YES;
}

- (BOOL)lockWhenCondition:(int)condition beforeDate:(NSDate *)limit;
{
    NSTimeInterval ti, tiNow, seconds;
    struct timespec ts;
    int rc;

    tiNow = [limit timeIntervalSinceNow];

    //fprintf(stderr, "thread:%d/lock:0x%08x  lockWhenCondition: %d beforeDate: %f\n", pthread_self(), (unsigned int)self, condition, tiNow);
    
    // If the specified time is in the past, we should still try at least once.
    // This also solves overflow problems with massively negative time intervals
    // (like +[NSDate distantPast]).  The code below should handle small negative
    // times, but this will catch the huge ones.
    if (tiNow < 0.0) {
        fprintf(stderr, "tiNow = %f\n", tiNow);
        if (pthread_mutex_trylock(&_mutex))
            return NO;
        if (_state != condition) {
            pthread_mutex_unlock(&_mutex);
            return NO;
        }
        return YES;
    }

    // Unix time (assuming a signed number of seconds) allows for about 68.04 years
    // (2147483648/(365.25 * 24 * 60 * 60)).  Since it started in 1970, we can only
    // handle times up to about 2038 before we overflow the Unix date struct.
    // This handles huge positive time intervals from dates like +[NSDate distantFuture].
    ti = [limit timeIntervalSinceReferenceDate] + NSTimeIntervalSince1970;
    if (ti > 2147483647.0) {
        ti = 2147483647.0;
    }

    //fprintf(stderr, "thread:%d/lock:0x%08x  ti: %f\n", pthread_self(), (unsigned int)self, ti);

    seconds = floor(ti);
    ts.tv_sec = seconds;
    ts.tv_nsec = (ti - seconds) * 1.0e9;

    rc = pthread_mutex_lock_timeout_np(&_mutex, &ts);
    if (rc == ETIMEDOUT)
        return NO;
    if (rc) {
        fprintf(stderr, "-[OFConditionLock lockWhenCondition:beforeDate:] -- pthread_mutex_lock_timeout_np returned %d, %s\n", rc, strerror(rc));
        return NO;
    }

    //fprintf(stderr, "thread:%d/lock:0x%08x  got lock\n", pthread_self(), (unsigned int)self);
    
    while (_state != condition) {
        _waiters++;
        //fprintf(stderr, "thread:%d/lock:0x%08x  condition was %d -- waiters = %d\n", pthread_self(), (unsigned int)self, _state, _waiters);
        rc = pthread_cond_timedwait(&_condition, &_mutex, &ts);
        _waiters--;
        if (rc == ETIMEDOUT) {
            //fprintf(stderr, "thread:%d/lock:0x%08x  timed out\n", pthread_self(), (unsigned int)self);
            pthread_mutex_unlock(&_mutex);
            return NO;
        } else if (rc) {
            fprintf(stderr, "-[OFConditionLock lockWhenCondition:beforeDate:] -- pthread_cond_timedwait returned %d, %s\n", rc, strerror(rc));
            pthread_mutex_unlock(&_mutex);
            return NO;
        }
    }

    //fprintf(stderr, "thread:%d/lock:0x%08x  condition is right!\n", pthread_self(), (unsigned int)self);
    
#ifdef DEBUG
    _lockingThread = pthread_self();
#endif
    return YES;
}


@end


@interface NSConditionLock (Hack)
+ (id) allocWithZone: (NSZone *) zone;
@end

@implementation NSConditionLock (Hack)
+ (id) allocWithZone: (NSZone *) zone;
{
    return NSAllocateObject([OFConditionLock class], 0, zone);
}
@end




static inline int _compute_relative_sleep_time(const struct timespec *abstime, mach_timespec_t *relative)
{
        struct timespec now;
        struct timeval tv;

        gettimeofday(&tv, NULL);
        TIMEVAL_TO_TIMESPEC(&tv, &now);

        /* Compute relative time to sleep */
        relative->tv_nsec = abstime->tv_nsec - now.tv_nsec;
        relative->tv_sec = abstime->tv_sec - now.tv_sec;
        if (relative->tv_nsec < 0)
        {
                relative->tv_nsec += 1000000000;  /* nsec/sec */
                relative->tv_sec--;
        }
        if (((int)relative->tv_sec < 0) ||
            ((relative->tv_sec == 0) && (relative->tv_nsec == 0)))
        {
                return ETIMEDOUT;
        }
        
        return 0;
}

static int pthread_mutex_lock_timeout_np(pthread_mutex_t *m,
                                         const struct timespec *abstime)
{
        kern_return_t kern_res;
        mach_timespec_t then;
        private_mutex_t *mutex = (private_mutex_t *)m;
        int rc;
        
        if (mutex->sig == _PTHREAD_MUTEX_SIG_init)
        {
                int res;
                if ((res = pthread_mutex_init(m, NULL)))
                        return (res);
        }
        if (mutex->sig != _PTHREAD_MUTEX_SIG) {
                return (EINVAL);        /* Not a mutex variable */
        }

        rc = _compute_relative_sleep_time(abstime, &then);
        if (rc) {
            // If we would time out immediately, we should at least try once.
            return pthread_mutex_trylock(m);
        }
            
        LOCK(mutex->lock);
        while (mutex->owner != (pthread_t)NULL)
        {
                mutex->waiters++;
                UNLOCK(mutex->lock);
                kern_res = semaphore_timedwait(mutex->sem, then);
                LOCK(mutex->lock);
                mutex->waiters--;
                if (kern_res == KERN_OPERATION_TIMED_OUT) {
                        UNLOCK(mutex->lock);
                        return ETIMEDOUT;
                }
                if (mutex->cond_lock) {
                        mutex->cond_lock = 0;
                        break;
                } else {
                        // recompute the relative time based on the new current time
                        if ((rc = _compute_relative_sleep_time(abstime, &then))) {
                            UNLOCK(mutex->lock);
                            return rc;
                        }
                }
        }
        mutex->owner = (pthread_t)0x12141968;
        UNLOCK(mutex->lock);
        return 0;
}

#endif


//############################################################################
//############################################################################
//############################################################################

#ifdef CLOBBER_NSCONDITION_LOCK_3

#warning Using _isLocked rewrite of NSConditionLock (w/o my_pthreads goo)

@interface OFConditionLock : NSObject
{
@public
    pthread_mutex_t _mutex;
    pthread_cond_t  _condition;
#ifdef DEBUG
    pthread_t       _lockingThread;
#endif
    volatile BOOL   _isLocked;
    volatile int    _state;
}

- (id)initWithCondition:(int)condition;

- (int)condition;
- (void)lockWhenCondition:(int)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(int)condition;
- (void)unlockWithCondition:(int)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(int)condition beforeDate:(NSDate *)limit;

@end

static inline void _LOCK(pthread_mutex_t *m, int self)
{
    int rc;
    
    if ((rc = pthread_mutex_lock(m)))
        [NSException raise: NSInternalInconsistencyException
                    format: @"pthread_mutex_lock() returned %d, %s", rc, strerror(rc)];
}

static inline void _WAIT(pthread_cond_t *c, pthread_mutex_t *m, int self)
{
    int rc;
    
    if ((rc = pthread_cond_wait(c, m)))
        [NSException raise: NSInternalInconsistencyException
                    format: @"pthread_cond_wait() returned %d, %s", rc, strerror(rc)];
}

static inline void _UNLOCK(pthread_cond_t *c, pthread_mutex_t *m, int self)
{
    int rc;
    
    if (c) {
        if ((rc = pthread_cond_broadcast(c)))
            [NSException raise: NSInternalInconsistencyException
                        format: @"pthread_cond_broadcast() returned %d, %s", rc, strerror(rc)];
    }
    if ((rc = pthread_mutex_unlock(m)))
        [NSException raise: NSInternalInconsistencyException
                    format: @"pthread_mutex_unlock() returned %d, %s", rc, strerror(rc)];
}

#ifdef DEBUG
#define _SET_LOCKER() _lockingThread = pthread_self()
#define _CHECK_AND_CLEAR_LOCKER() \
do { \
    if (_lockingThread != pthread_self()) \
        [NSException raise: NSInternalInconsistencyException \
                    format: @"Attempted to unlock a lock not locked by the current thread."]; \
    _lockingThread = THREAD_NULL; \
} while(0)
#else
#define _SET_LOCKER()
#define _CHECK_AND_CLEAR_LOCKER()
#endif


@implementation OFConditionLock

#ifdef DEBUG
+ (void) initialize;
{
    [super initialize];
    fprintf(stderr, "*** Using _isLocked rewrite of NSConditionLock -- remove as soon as pthreads bug is fixed in MacOS X\n");
}
#endif

- init;
{
    return [self initWithCondition: 0];
}

- (id)initWithCondition:(int)condition;
{
    int rc;

    if ((rc = pthread_mutex_init(&_mutex, NULL)))
        [NSException raise: @"OFConditionLockInitializationFailed"
                    format: @"pthread_mutex_init returned %d, %s", rc, strerror(rc)];
    
    if ((rc = pthread_cond_init(&_condition, NULL)))
        [NSException raise: @"OFConditionLockInitializationFailed"
                    format: @"pthread_cond_init returned %d, %s", rc, strerror(rc)];
    
    _isLocked = NO;
    _state = condition;

    return self;
}

- (void) dealloc;
{
    int rc;
    
    if (_isLocked)
        [NSException raise: NSInternalInconsistencyException
                    format: @"Attempted to deallocate an OFConditionLock while it is locked"];
                
    if ((rc = pthread_mutex_destroy(&_mutex)))
        NSLog(@"-[OFCondition dealloc] -- pthread_mutex_destroy returned %d, %s", rc, strerror(rc));
    if ((rc = pthread_cond_destroy(&_condition)))
        NSLog(@"-[OFCondition dealloc] -- pthread_cond_destroy returned %d, %s", rc, strerror(rc));
    [super dealloc];
}

//
// NSLock API
//

- (void) lock;
{
    //fprintf(stderr, "thread:%d/lock:0x%08x  lock\n", pthread_self(), (unsigned int)self);

    _LOCK(&_mutex, (int)self);
    while (_isLocked) {
        _WAIT(&_condition, &_mutex, (int)self);
    }
    
    _isLocked = YES;
    _SET_LOCKER();

    _UNLOCK(NULL, &_mutex, (int)self);

    //fprintf(stderr, "thread:%d/lock:0x%08x  got lock\n", pthread_self(), (unsigned int)self);
}

- (void) unlock;
{
    //fprintf(stderr, "thread:%d/lock:0x%08x  unlock\n", pthread_self(), (unsigned int)self);

    _LOCK(&_mutex, (int)self);
    _CHECK_AND_CLEAR_LOCKER();
    _isLocked = NO;
    _UNLOCK(&_condition, &_mutex, (int)self);
}

//
// NSConditionLock API
//

- (int)condition;
{
    return _state;
}

- (void)lockWhenCondition:(int)condition;
{
    //fprintf(stderr, "thread:%d/lock:0x%08x  lockWhenCondition: %d\n", pthread_self(), (unsigned int)self, condition);

    _LOCK(&_mutex, (int)self);

    while (_isLocked || _state != condition) {
        //fprintf(stderr, "thread:%d/lock:0x%08x  condition was %d, waiters now %d\n", pthread_self(), (unsigned int)self, _state, _waiters);
        _WAIT(&_condition, &_mutex, (int)self);
    }

    //fprintf(stderr, "thread:%d/lock:0x%08x  condition is right\n", pthread_self(), (unsigned int)self);
    
    _isLocked = YES;
    _SET_LOCKER();

    _UNLOCK(NULL, &_mutex, (int)self);
}

- (BOOL)tryLock;
{
    _LOCK(&_mutex, (int)self);
    
    if (_isLocked) {
        _UNLOCK(NULL, &_mutex, (int)self);
        return NO;
    }

    _isLocked = YES;
    _SET_LOCKER();
    
    _UNLOCK(NULL, &_mutex, (int)self);
    return YES;
}

- (BOOL)tryLockWhenCondition:(int)condition;
{
    _LOCK(&_mutex, (int)self);
    
    if (_isLocked || _state != condition) {
        _UNLOCK(NULL, &_mutex, (int)self);
        return NO;
    }

    _isLocked = YES;
    _SET_LOCKER();
    
    _UNLOCK(NULL, &_mutex, (int)self);
    return YES;
}

- (void)unlockWithCondition:(int)condition;
{
    _LOCK(&_mutex, (int)self);
    
    _CHECK_AND_CLEAR_LOCKER();

    //fprintf(stderr, "thread:%d/lock:0x%08x  unlockWithCondition: %d, waiters now %d\n", pthread_self(), (unsigned int)self, condition, _waiters);

    _isLocked = NO;
    _state = condition;
    
    _UNLOCK(&_condition, &_mutex, (int)self);
}

- (BOOL)lockBeforeDate:(NSDate *)limit;
{
    NSTimeInterval ti, tiNow, seconds;
    struct timespec ts;
    
    tiNow = [limit timeIntervalSinceNow];
    
    //fprintf(stderr, "thread:%d/lock:0x%08x  lockBeforeDate: %f\n", pthread_self(), (unsigned int)self, tiNow);

    _LOCK(&_mutex, (int)self);

    // If the specified time is in the past, we should still try at least once.
    // This also solves overflow problems with massively negative time intervals
    // (like +[NSDate distantPast]).  The code below should handle small negative
    // times, but this will catch the huge ones.
    if (tiNow < 0.0) {
        if (_isLocked) {
            _UNLOCK(NULL, &_mutex, (int)self);
            return NO;
        } else {
            _UNLOCK(NULL, &_mutex, (int)self);
            return YES;
        }
    }
    
    // Unix time (assuming a signed number of seconds) allows for about 68.04 years
    // (2147483648/(365.25 * 24 * 60 * 60)).  Since it started in 1970, we can only
    // handle times up to about 2038 before we overflow the Unix date struct.
    // This handles huge positive time intervals from dates like +[NSDate distantFuture].
    ti = [limit timeIntervalSinceReferenceDate] + NSTimeIntervalSince1970;
    if (ti > 2147483647.0) {
        ti = 2147483647.0;
    }
     
    //fprintf(stderr, "thread:%d/lock:0x%08x  ti: %f\n", pthread_self(), (unsigned int)self, ti);

    seconds = floor(ti);
    ts.tv_sec = seconds;
    ts.tv_nsec = (ti - seconds) * 1.0e9;

    while (_isLocked) {
        if (pthread_cond_timedwait(&_condition, &_mutex, &ts)) {
            // Expired
            _UNLOCK(NULL, &_mutex, (int)self);
            return NO;
        }
    }
    
    _SET_LOCKER();
    _UNLOCK(NULL, &_mutex, (int)self);
    
    return YES;
}

- (BOOL)lockWhenCondition:(int)condition beforeDate:(NSDate *)limit;
{
    NSTimeInterval ti, tiNow, seconds;
    struct timespec ts;

    tiNow = [limit timeIntervalSinceNow];
    
    //fprintf(stderr, "thread:%d/lock:0x%08x  lockBeforeDate: %f\n", pthread_self(), (unsigned int)self, tiNow);

    _LOCK(&_mutex, (int)self);

    // If the specified time is in the past, we should still try at least once.
    // This also solves overflow problems with massively negative time intervals
    // (like +[NSDate distantPast]).  The code below should handle small negative
    // times, but this will catch the huge ones.
    if (tiNow < 0.0) {
        if (_isLocked || _state != condition) {
            _UNLOCK(NULL, &_mutex, (int)self);
            return NO;
        } else {
            _UNLOCK(NULL, &_mutex, (int)self);
            return YES;
        }
    }
    
    // Unix time (assuming a signed number of seconds) allows for about 68.04 years
    // (2147483648/(365.25 * 24 * 60 * 60)).  Since it started in 1970, we can only
    // handle times up to about 2038 before we overflow the Unix date struct.
    // This handles huge positive time intervals from dates like +[NSDate distantFuture].
    ti = [limit timeIntervalSinceReferenceDate] + NSTimeIntervalSince1970;
    if (ti > 2147483647.0) {
        ti = 2147483647.0;
    }
     
    //fprintf(stderr, "thread:%d/lock:0x%08x  ti: %f\n", pthread_self(), (unsigned int)self, ti);

    seconds = floor(ti);
    ts.tv_sec = seconds;
    ts.tv_nsec = (ti - seconds) * 1.0e9;

    while (_isLocked || _state != condition) {
        if (pthread_cond_timedwait(&_condition, &_mutex, &ts)) {
            // Expired
            _UNLOCK(NULL, &_mutex, (int)self);
            return NO;
        }
    }
    
    _SET_LOCKER();
    _UNLOCK(NULL, &_mutex, (int)self);
    
    return YES;
}


@end


@interface NSConditionLock (Hack)
+ (id) allocWithZone: (NSZone *) zone;
@end

@implementation NSConditionLock (Hack)
+ (id) allocWithZone: (NSZone *) zone;
{
    return NSAllocateObject([OFConditionLock class], 0, zone);
}
@end



#endif


#endif // PATCH_NSCONDITIONLOCK
