libs-base/Source/NSLock.m
rfm d9749d603d Documentation generation improvments
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@29047 72102866-910b-0410-8b05-ffd578937521
2009-11-23 09:42:18 +00:00

427 lines
8.7 KiB
Objective-C

/** Control of executable units within a shared virtual memory space
Copyright (C) 1996-2000 Free Software Foundation, Inc.
Original Author: David Chisnall <csdavec@swan.ac.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
<title>NSLock class reference</title>
<ignore> All autogsdoc markup is in the header
*/
// This file uses some SUS'98 extensions, so we need to tell glibc not to hide
// them. Other platforms have more sensible libcs, which just default to being
// standards-compliant.
#define _XOPEN_SOURCE 500
#include <pthread.h>
#import "GNUstepBase/GSConfig.h"
#define gs_cond_t pthread_cond_t
#define gs_mutex_t pthread_mutex_t
#import "Foundation/NSLock.h"
#include <math.h>
#include <errno.h>
#import "Foundation/NSException.h"
/*
* Methods shared between NSLock, NSRecursiveLock, and NSCondition
*
* Note: These methods currently throw exceptions when locks are incorrectly
* acquired. This is compatible with earlier GNUstep behaviour. In OS X 10.5
* and later, these will just NSLog a warning instead. Throwing an exception
* is probably better behaviour, because it encourages developer to fix their
* code.
*/
#define MDEALLOC \
- (void) dealloc\
{\
[self finalize];\
[_name release];\
[super dealloc];\
}
#define MDESCRIPTION \
- (NSString*) description\
{\
if (_name == nil)\
{\
return [super description];\
}\
return [NSString stringWithFormat: @"%@ '%@'",\
[super description], _name];\
}
#define MFINALIZE \
- (void) finalize\
{\
pthread_mutex_destroy(&_mutex);\
}
#define MNAME \
- (void) setName: (NSString*)newName\
{\
ASSIGNCOPY(_name, newName);\
}\
- (NSString*) name\
{\
return _name;\
}
#define MLOCK \
- (void) lock\
{\
int err = pthread_mutex_lock(&_mutex);\
if (EINVAL == err)\
{\
[NSException raise: NSLockException\
format: @"failed to lock mutex"];\
}\
if (EDEADLK == err)\
{\
_NSLockError(self, _cmd);\
}\
}
#define MLOCKBEFOREDATE \
- (BOOL) lockBeforeDate: (NSDate*)limit\
{\
do\
{\
int err = pthread_mutex_trylock(&_mutex);\
if (0 == err)\
{\
return YES;\
}\
sched_yield();\
} while([limit timeIntervalSinceNow] < 0);\
return NO;\
}
#define MTRYLOCK \
- (BOOL) tryLock\
{\
int err = pthread_mutex_trylock(&_mutex);\
return (0 == err) ? YES : NO;\
}
#define MUNLOCK \
- (void) unlock\
{\
if (0 != pthread_mutex_unlock(&_mutex))\
{\
[NSException raise: NSLockException\
format: @"failed to unlock mutex"];\
}\
}
static pthread_mutex_t deadlock;
static pthread_mutexattr_t attr_normal;
static pthread_mutexattr_t attr_reporting;
static pthread_mutexattr_t attr_recursive;
/*
* OS X 10.5 compatibility function to allow debugging deadlock conditions.
*/
void _NSLockError(id obj, SEL _cmd)
{
NSLog(@"*** -[%@ %@]: deadlock (%@)", [obj class],
NSStringFromSelector(_cmd), obj);
NSLog(@"*** Break on _NSLockError() to debug.");
pthread_mutex_lock(&deadlock);
}
// Exceptions
NSString *NSLockException = @"NSLockException";
@implementation NSLock
+ (void) initialize
{
static BOOL beenHere = NO;
if (beenHere == NO)
{
beenHere = YES;
/* Initialise attributes for the different types of mutex.
* We do it once, since attributes can be shared between multiple
* mutexes.
* If we had a pthread_mutexattr_t instance for each mutex, we would
* either have to store it as an ivar of our NSLock (or similar), or
* we would potentially leak instances as we couldn't destroy them
* when destroying the NSLock. I don't know if any implementation
* of pthreads actually allocates memory when you call the
* pthread_mutexattr_init function, but they are allowed to do so
* (and deallocate the memory in pthread_mutexattr_destroy).
*/
pthread_mutexattr_init(&attr_normal);
pthread_mutexattr_settype(&attr_normal, PTHREAD_MUTEX_NORMAL);
pthread_mutexattr_init(&attr_reporting);
pthread_mutexattr_settype(&attr_reporting, PTHREAD_MUTEX_ERRORCHECK);
pthread_mutexattr_init(&attr_recursive);
pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE);
/* To emulate OSX behavior, we need to be able both to detect deadlocks
* (so we can log them), and also hang the thread when one occurs.
* the simple way to do that is to set up a locked mutex we can
* force a deadlock on.
*/
pthread_mutex_init(&deadlock, &attr_normal);
pthread_mutex_lock(&deadlock);
}
}
MDEALLOC
MDESCRIPTION
MFINALIZE
/* Use an error-checking lock. This is marginally slower, but lets us throw
* exceptions when incorrect locking occurs.
*/
- (id) init
{
if (nil != (self = [super init]))
{
if (0 != pthread_mutex_init(&_mutex, &attr_reporting))
{
[self release];
self = nil;
}
}
return self;
}
MLOCK
MLOCKBEFOREDATE
MNAME
MTRYLOCK
MUNLOCK
@end
@implementation NSRecursiveLock
+ (void) initialize
{
[NSLock class]; // Ensure mutex attributes are set up.
}
MDEALLOC
MDESCRIPTION
MFINALIZE
- (id) init
{
if (nil != (self = [super init]))
{
if (0 != pthread_mutex_init(&_mutex, &attr_recursive))
{
[self release];
self = nil;
}
}
return self;
}
MLOCK
MLOCKBEFOREDATE
MNAME
MTRYLOCK
MUNLOCK
@end
@implementation NSCondition
+ (void) initialize
{
[NSLock class]; // Ensure mutex attributes are set up.
}
- (void) broadcast
{
pthread_cond_broadcast(&_condition);
}
MDEALLOC
MDESCRIPTION
- (void) finalize
{
pthread_cond_destroy(&_condition);
pthread_mutex_destroy(&_mutex);
}
- (id) init
{
if (nil == (self = [super init]))
{
return nil;
}
if (0 != pthread_cond_init(&_condition, NULL))
{
[self release];
return nil;
}
if (0 != pthread_mutex_init(&_mutex, &attr_reporting))
{
pthread_cond_destroy(&_condition);
[self release];
return nil;
}
return self;
}
MLOCK
MLOCKBEFOREDATE
MNAME
- (void) signal
{
pthread_cond_signal(&_condition);
}
MTRYLOCK
MUNLOCK
- (void) wait
{
pthread_cond_wait(&_condition, &_mutex);
}
- (BOOL) waitUntilDate: (NSDate*)limit
{
NSTimeInterval t = [limit timeIntervalSinceReferenceDate];
double secs, subsecs;
struct timespec timeout;
// Split the float into seconds and fractions of a second
subsecs = modf(t, &secs);
timeout.tv_sec = secs;
// Convert fractions of a second to nanoseconds
timeout.tv_nsec = subsecs * 1e9;
if (0 == pthread_cond_timedwait(&_condition, &_mutex, &timeout))
{
return YES;
}
else
{
return NO;
}
}
@end
@implementation NSConditionLock
+ (void) initialize
{
[NSLock class]; // Ensure mutex attributes are set up.
}
- (NSInteger) condition
{
return _condition_value;
}
- (void) dealloc
{
[_name release];
[_condition release];
[super dealloc];
}
- (id) init
{
return [self initWithCondition: 0];
}
- (id) initWithCondition: (NSInteger)value
{
if (nil == (self = [super init]))
{
return nil;
}
if (nil == (_condition = [NSCondition new]))
{
[self release];
return nil;
}
_condition_value = value;
return self;
}
- (void) lock
{
[_condition lock];
}
- (BOOL) lockBeforeDate: (NSDate*)limit
{
return [_condition lockBeforeDate: limit];
}
- (void) lockWhenCondition: (NSInteger)value
{
[_condition lock];
while (value != _condition_value)
{
[_condition wait];
}
}
- (BOOL) lockWhenCondition: (NSInteger)condition_to_meet
beforeDate: (NSDate*)limitDate
{
BOOL ret;
[_condition lock];
if (condition_to_meet == _condition_value)
{
return YES;
}
if ([_condition waitUntilDate: limitDate]
&& (condition_to_meet == _condition_value))
{
ret = YES;
}
else
{
ret = NO;
}
[_condition unlock];
return ret;
}
MNAME
- (BOOL) tryLock
{
return [_condition tryLock];
}
- (BOOL) tryLockWhenCondition: (NSInteger)value
{
return [self lockWhenCondition: value
beforeDate: [NSDate date]];
}
- (void) unlock
{
[_condition unlock];
}
- (void) unlockWithCondition: (NSInteger)value
{
_condition_value = value;
[_condition broadcast];
[_condition unlock];
}
@end