mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
indicate trace by use of subclass rather than flag
This commit is contained in:
parent
efb4ec5f22
commit
603c3b1103
4 changed files with 417 additions and 226 deletions
|
@ -64,18 +64,49 @@ static inline void GSPThreadInitRecursiveMutex(pthread_mutex_t *x)
|
|||
NSArray *addresses;
|
||||
@public
|
||||
NSUInteger recursion; // Recursion count for lock trace
|
||||
void **returns; // The return addresses on the stack
|
||||
NSUInteger *returns; // The return addresses on the stack
|
||||
int numReturns; // Number of return addresses
|
||||
}
|
||||
- (NSArray*) addresses;
|
||||
- (NSArray*) symbols;
|
||||
- (NSArray*) addresses; // Return addresses from last trace
|
||||
- (NSArray*) symbols; // Return symbols from last trace
|
||||
- (void) trace; // Populate with new stack trace
|
||||
@end
|
||||
|
||||
/* Versions of the lock classes where the locking is never traced
|
||||
*/
|
||||
@interface GSUntracedCondition : NSCondition
|
||||
@end
|
||||
@interface GSUntracedConditionLock : NSConditionLock
|
||||
@end
|
||||
@interface GSUntracedLock : NSLock
|
||||
@end
|
||||
@interface GSUntracedRecursiveLock : NSRecursiveLock
|
||||
@end
|
||||
|
||||
/* Versions of the lock classes where the locking is traced
|
||||
*/
|
||||
@interface GSTracedCondition : NSCondition
|
||||
{
|
||||
GSStackTrace *stack;
|
||||
}
|
||||
- (GSStackTrace*) stack;
|
||||
@end
|
||||
|
||||
@interface GSTracedConditionLock : NSConditionLock
|
||||
@end
|
||||
|
||||
@interface GSTracedLock : NSLock
|
||||
{
|
||||
GSStackTrace *stack;
|
||||
}
|
||||
- (GSStackTrace*) stack;
|
||||
@end
|
||||
|
||||
@interface GSTracedRecursiveLock : NSRecursiveLock
|
||||
{
|
||||
GSStackTrace *stack;
|
||||
}
|
||||
- (GSStackTrace*) stack;
|
||||
@end
|
||||
|
||||
#endif // _GSPThread_h_
|
||||
|
|
|
@ -842,91 +842,19 @@ NSReturnAddress(NSUInteger offset)
|
|||
return env->addr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@implementation GSStackTrace : NSObject
|
||||
|
||||
/** Offset from the top of the stack (when we generate a trace) to the
|
||||
* first frame likely to be of interest for debugging.
|
||||
*/
|
||||
#define FrameOffset 4
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
#if defined(_WIN32) && !defined(USE_BFD)
|
||||
GS_INIT_RECURSIVE_MUTEX(traceLock);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSArray*) addresses
|
||||
{
|
||||
if (nil == addresses && numReturns > FrameOffset)
|
||||
{
|
||||
CREATE_AUTORELEASE_POOL(pool);
|
||||
NSInteger count = numReturns - FrameOffset;
|
||||
NSValue *objects[count];
|
||||
NSUInteger index;
|
||||
const void **ptrs = (const void **)returns;
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
objects[index] = [NSValue valueWithPointer: ptrs[FrameOffset+index]];
|
||||
}
|
||||
addresses = [[NSArray alloc] initWithObjects: objects count: count];
|
||||
DESTROY(pool);
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
- (oneway void) dealloc
|
||||
{
|
||||
DESTROY(addresses);
|
||||
DESTROY(symbols);
|
||||
if (returns != NULL)
|
||||
{
|
||||
free(returns);
|
||||
returns = NULL;
|
||||
}
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSString*) description
|
||||
{
|
||||
NSMutableString *result;
|
||||
NSArray *s;
|
||||
int i;
|
||||
int n;
|
||||
|
||||
result = [NSMutableString string];
|
||||
s = [self symbols];
|
||||
n = [s count];
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
NSString *line = [s objectAtIndex: i];
|
||||
|
||||
[result appendFormat: @"%3d: %@\n", i, line];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
return [self initWithOffset: 3];
|
||||
}
|
||||
|
||||
// grab the current stack
|
||||
- (id) initWithOffset: (unsigned)o
|
||||
unsigned
|
||||
GSPrivateReturnAddresses(NSUInteger **returns)
|
||||
{
|
||||
unsigned numReturns;
|
||||
#if HAVE_BACKTRACE
|
||||
void *addr[MAXFRAMES*sizeof(void*)];
|
||||
void *addr[MAXFRAMES*sizeof(void*)];
|
||||
|
||||
numReturns = backtrace(addr, MAXFRAMES);
|
||||
if (numReturns > 0)
|
||||
{
|
||||
returns = malloc(numReturns * sizeof(void*));
|
||||
memcpy(returns, addr, numReturns * sizeof(void*));
|
||||
}
|
||||
*returns = malloc(numReturns * sizeof(void*));
|
||||
memcpy(*returns, addr, numReturns * sizeof(void*));
|
||||
}
|
||||
#elif defined(_WIN32) && !defined(USE_BFD)
|
||||
NSUInteger addr[MAXFRAMES];
|
||||
|
||||
|
@ -945,7 +873,7 @@ NSReturnAddress(NSUInteger offset)
|
|||
fprintf(stderr, "Failed to load kernel32.dll with error: %d\n",
|
||||
(int)GetLastError());
|
||||
(void)pthread_mutex_unlock(&traceLock);
|
||||
return self;
|
||||
return 0;
|
||||
}
|
||||
capture = (CaptureStackBackTraceType)GetProcAddress(
|
||||
hModule, "RtlCaptureStackBackTrace");
|
||||
|
@ -954,7 +882,7 @@ NSReturnAddress(NSUInteger offset)
|
|||
fprintf(stderr, "Failed to find RtlCaptureStackBackTrace: %d\n",
|
||||
(int)GetLastError());
|
||||
(void)pthread_mutex_unlock(&traceLock);
|
||||
return self;
|
||||
return 0;
|
||||
}
|
||||
hModule = LoadLibrary("dbghelp.dll");
|
||||
if (0 == hModule)
|
||||
|
@ -962,7 +890,7 @@ NSReturnAddress(NSUInteger offset)
|
|||
fprintf(stderr, "Failed to load dbghelp.dll with error: %d\n",
|
||||
(int)GetLastError());
|
||||
(void)pthread_mutex_unlock(&traceLock);
|
||||
return self;
|
||||
return 0;
|
||||
}
|
||||
optSym = (SymSetOptionsType)GetProcAddress(
|
||||
hModule, "SymSetOptions");
|
||||
|
@ -971,7 +899,7 @@ NSReturnAddress(NSUInteger offset)
|
|||
fprintf(stderr, "Failed to find SymSetOptions: %d\n",
|
||||
(int)GetLastError());
|
||||
(void)pthread_mutex_unlock(&traceLock);
|
||||
return self;
|
||||
return 0;
|
||||
}
|
||||
initSym = (SymInitializeType)GetProcAddress(
|
||||
hModule, "SymInitialize");
|
||||
|
@ -980,7 +908,7 @@ NSReturnAddress(NSUInteger offset)
|
|||
fprintf(stderr, "Failed to find SymInitialize: %d\n",
|
||||
(int)GetLastError());
|
||||
(void)pthread_mutex_unlock(&traceLock);
|
||||
return self;
|
||||
return 0;
|
||||
}
|
||||
fromSym = (SymFromAddrType)GetProcAddress(
|
||||
hModule, "SymFromAddr");
|
||||
|
@ -989,7 +917,7 @@ NSReturnAddress(NSUInteger offset)
|
|||
fprintf(stderr, "Failed to find SymFromAddr: %d\n",
|
||||
(int)GetLastError());
|
||||
(void)pthread_mutex_unlock(&traceLock);
|
||||
return self;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1001,20 +929,20 @@ NSReturnAddress(NSUInteger offset)
|
|||
(int)GetLastError());
|
||||
fromSym = 0;
|
||||
(void)pthread_mutex_unlock(&traceLock);
|
||||
return self;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (0 == capture)
|
||||
{
|
||||
(void)pthread_mutex_unlock(&traceLock);
|
||||
return self;
|
||||
return 0;
|
||||
}
|
||||
|
||||
numReturns = (capture)(0, MAXFRAMES, (void**)addr, NULL);
|
||||
if (numReturns > 0)
|
||||
{
|
||||
returns = malloc(numReturns * sizeof(void*));
|
||||
memcpy(returns, addr, numReturns * sizeof(void*));
|
||||
*returns = malloc(numReturns * sizeof(void*));
|
||||
memcpy(*returns, addr, numReturns * sizeof(void*));
|
||||
}
|
||||
|
||||
(void)pthread_mutex_unlock(&traceLock);
|
||||
|
@ -1092,7 +1020,7 @@ NSReturnAddress(NSUInteger offset)
|
|||
{
|
||||
break;
|
||||
}
|
||||
memcpy(&returns[i], env->addr, sizeof(void*));
|
||||
memcpy(&(*returns)[i], env->addr, sizeof(void*));
|
||||
}
|
||||
signal(SIGSEGV, env->segv);
|
||||
signal(SIGBUS, env->bus);
|
||||
|
@ -1105,6 +1033,80 @@ NSReturnAddress(NSUInteger offset)
|
|||
}
|
||||
}
|
||||
#endif
|
||||
return numReturns;
|
||||
}
|
||||
|
||||
|
||||
@implementation GSStackTrace : NSObject
|
||||
|
||||
/** Offset from the top of the stack (when we generate a trace) to the
|
||||
* first frame likely to be of interest for debugging.
|
||||
*/
|
||||
#define FrameOffset 4
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
#if defined(_WIN32) && !defined(USE_BFD)
|
||||
GS_INIT_RECURSIVE_MUTEX(traceLock);
|
||||
#endif
|
||||
#if defined(USE_BFD)
|
||||
GS_INIT_RECURSIVE_MUTEX(modLock);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSArray*) addresses
|
||||
{
|
||||
if (nil == addresses && numReturns > FrameOffset)
|
||||
{
|
||||
CREATE_AUTORELEASE_POOL(pool);
|
||||
NSInteger count = numReturns - FrameOffset;
|
||||
NSValue *objects[count];
|
||||
NSUInteger index;
|
||||
const void **ptrs = (const void **)returns;
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
objects[index] = [NSValue valueWithPointer: ptrs[FrameOffset+index]];
|
||||
}
|
||||
addresses = [[NSArray alloc] initWithObjects: objects count: count];
|
||||
DESTROY(pool);
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
- (oneway void) dealloc
|
||||
{
|
||||
DESTROY(addresses);
|
||||
DESTROY(symbols);
|
||||
if (returns != NULL)
|
||||
{
|
||||
free(returns);
|
||||
returns = NULL;
|
||||
}
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSString*) description
|
||||
{
|
||||
NSMutableString *result;
|
||||
NSArray *s;
|
||||
int i;
|
||||
int n;
|
||||
|
||||
result = [NSMutableString string];
|
||||
s = [self symbols];
|
||||
n = [s count];
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
NSString *line = [s objectAtIndex: i];
|
||||
|
||||
[result appendFormat: @"%3d: %@\n", i, line];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -1235,6 +1237,18 @@ NSReturnAddress(NSUInteger offset)
|
|||
return symbols;
|
||||
}
|
||||
|
||||
- (void) trace
|
||||
{
|
||||
DESTROY(addresses);
|
||||
DESTROY(symbols);
|
||||
if (returns != NULL)
|
||||
{
|
||||
free(returns);
|
||||
returns = NULL;
|
||||
}
|
||||
numReturns = GSPrivateReturnAddresses(&returns);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
@ -1327,9 +1341,6 @@ callUncaughtHandler(id value)
|
|||
{
|
||||
if (self == [NSException class])
|
||||
{
|
||||
#if defined(USE_BFD)
|
||||
GS_INIT_RECURSIVE_MUTEX(modLock);
|
||||
#endif /* USE_BFD */
|
||||
#if defined(_NATIVE_OBJC_EXCEPTIONS)
|
||||
# ifdef HAVE_SET_UNCAUGHT_EXCEPTION_HANDLER
|
||||
objc_setUncaughtExceptionHandler(callUncaughtHandler);
|
||||
|
@ -1487,6 +1498,7 @@ callUncaughtHandler(id value)
|
|||
{
|
||||
// Only set the stack when first raised
|
||||
_e_stack = [GSStackTrace new];
|
||||
[_e_stack trace];
|
||||
}
|
||||
|
||||
#if defined(_NATIVE_OBJC_EXCEPTIONS)
|
||||
|
@ -1635,9 +1647,9 @@ callUncaughtHandler(id value)
|
|||
|
||||
+ (NSArray *) callStackSymbols
|
||||
{
|
||||
GSStackTrace *stackTrace = [[[GSStackTrace alloc] init] autorelease];
|
||||
NSArray *symbols = [stackTrace symbols];
|
||||
return symbols;
|
||||
GSStackTrace *stackTrace = AUTORELEASE([GSStackTrace new]);
|
||||
[stackTrace trace];
|
||||
return [stackTrace symbols];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
378
Source/NSLock.m
378
Source/NSLock.m
|
@ -43,6 +43,21 @@
|
|||
|
||||
#import "GSPThread.h"
|
||||
|
||||
static Class baseConditionClass = Nil;
|
||||
static Class baseConditionLockClass = Nil;
|
||||
static Class baseLockClass = Nil;
|
||||
static Class baseRecursiveLockClass = Nil;
|
||||
|
||||
static Class tracedConditionClass = Nil;
|
||||
static Class tracedConditionLockClass = Nil;
|
||||
static Class tracedLockClass = Nil;
|
||||
static Class tracedRecursiveLockClass = Nil;
|
||||
|
||||
static Class untracedConditionClass = Nil;
|
||||
static Class untracedConditionLockClass = Nil;
|
||||
static Class untracedLockClass = Nil;
|
||||
static Class untracedRecursiveLockClass = Nil;
|
||||
|
||||
static BOOL traceLocks = NO;
|
||||
|
||||
void
|
||||
|
@ -51,17 +66,11 @@ GSPrivateTraceLocks(BOOL aFlag)
|
|||
traceLocks = (aFlag ? YES : NO);
|
||||
}
|
||||
|
||||
|
||||
#define CHKT(T,X) \
|
||||
if (traceLocks) { \
|
||||
NSString *msg = [T mutex ## X: self]; \
|
||||
if (nil != msg) \
|
||||
{ \
|
||||
(*_NSLock_error_handler)(self, _cmd, YES, msg); \
|
||||
} \
|
||||
};
|
||||
|
||||
#define CHK(X) CHKT(GSCurrentThread(), X)
|
||||
/* In untraced operations these macros do nothing.
|
||||
* When tracing they are defined to perform the trace methods of the thread.
|
||||
*/
|
||||
#define CHKT(T,X)
|
||||
#define CHK(X)
|
||||
|
||||
/*
|
||||
* Methods shared between NSLock, NSRecursiveLock, and NSCondition
|
||||
|
@ -144,56 +153,18 @@ if (traceLocks) { \
|
|||
pthread_mutex_destroy(&_mutex);\
|
||||
}
|
||||
|
||||
#define MNAME \
|
||||
- (void) setName: (NSString*)newName\
|
||||
{\
|
||||
ASSIGNCOPY(_name, newName);\
|
||||
}\
|
||||
- (NSString*) name\
|
||||
{\
|
||||
return _name;\
|
||||
}
|
||||
|
||||
#define MLOCK_TRACED \
|
||||
{ \
|
||||
NSThread *t = GSCurrentThread(); \
|
||||
CHKT(t,Wait) \
|
||||
int err = pthread_mutex_lock(&_mutex);\
|
||||
if (EDEADLK == err)\
|
||||
{\
|
||||
CHKT(t,Drop) \
|
||||
(*_NSLock_error_handler)(self, _cmd, YES, @"deadlock");\
|
||||
}\
|
||||
else if (err != 0)\
|
||||
{\
|
||||
CHKT(t,Drop) \
|
||||
[NSException raise: NSLockException\
|
||||
format: @"failed to lock mutex"];\
|
||||
}\
|
||||
CHKT(t,Hold) \
|
||||
}
|
||||
|
||||
#define MLOCK_UNTRACED \
|
||||
{ \
|
||||
int err = pthread_mutex_lock(&_mutex);\
|
||||
if (EDEADLK == err)\
|
||||
{\
|
||||
(*_NSLock_error_handler)(self, _cmd, YES, @"deadlock");\
|
||||
}\
|
||||
else if (err != 0)\
|
||||
{\
|
||||
[NSException raise: NSLockException\
|
||||
format: @"failed to lock mutex"];\
|
||||
}\
|
||||
}
|
||||
|
||||
#define MLOCK \
|
||||
- (void) lock\
|
||||
{\
|
||||
if (traceLocks) \
|
||||
MLOCK_TRACED \
|
||||
else \
|
||||
MLOCK_UNTRACED \
|
||||
int err = pthread_mutex_lock(&_mutex);\
|
||||
if (EDEADLK == err)\
|
||||
{\
|
||||
(*_NSLock_error_handler)(self, _cmd, YES, @"deadlock");\
|
||||
}\
|
||||
else if (err != 0)\
|
||||
{\
|
||||
[NSException raise: NSLockException format: @"failed to lock mutex"];\
|
||||
}\
|
||||
}
|
||||
|
||||
#define MLOCKBEFOREDATE \
|
||||
|
@ -212,6 +183,22 @@ if (traceLocks) { \
|
|||
return NO;\
|
||||
}
|
||||
|
||||
#define MNAME \
|
||||
- (void) setName: (NSString*)newName\
|
||||
{\
|
||||
ASSIGNCOPY(_name, newName);\
|
||||
}\
|
||||
- (NSString*) name\
|
||||
{\
|
||||
return _name;\
|
||||
}
|
||||
|
||||
#define MSTACK \
|
||||
- (GSStackTrace*) stack \
|
||||
{ \
|
||||
return nil; \
|
||||
}
|
||||
|
||||
#define MTRYLOCK \
|
||||
- (BOOL) tryLock\
|
||||
{\
|
||||
|
@ -263,6 +250,15 @@ NSString *NSLockException = @"NSLockException";
|
|||
|
||||
@implementation NSLock
|
||||
|
||||
+ (id) allocWithZone: (NSZone*)z
|
||||
{
|
||||
if (self == baseLockClass && YES == traceLocks)
|
||||
{
|
||||
return class_createInstance(tracedLockClass, 0);
|
||||
}
|
||||
return class_createInstance(self, 0);
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
static BOOL beenHere = NO;
|
||||
|
@ -296,6 +292,21 @@ NSString *NSLockException = @"NSLockException";
|
|||
*/
|
||||
pthread_mutex_init(&deadlock, &attr_normal);
|
||||
pthread_mutex_lock(&deadlock);
|
||||
|
||||
baseConditionClass = [NSCondition class];
|
||||
baseConditionLockClass = [NSConditionLock class];
|
||||
baseLockClass = [NSLock class];
|
||||
baseRecursiveLockClass = [NSRecursiveLock class];
|
||||
|
||||
tracedConditionClass = [GSTracedCondition class];
|
||||
tracedConditionLockClass = [GSTracedConditionLock class];
|
||||
tracedLockClass = [GSTracedLock class];
|
||||
tracedRecursiveLockClass = [GSTracedRecursiveLock class];
|
||||
|
||||
untracedConditionClass = [GSUntracedCondition class];
|
||||
untracedConditionLockClass = [GSUntracedConditionLock class];
|
||||
untracedLockClass = [GSUntracedLock class];
|
||||
untracedRecursiveLockClass = [GSUntracedRecursiveLock class];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -328,7 +339,7 @@ MLOCK
|
|||
int err = pthread_mutex_trylock(&_mutex);
|
||||
if (0 == err)
|
||||
{
|
||||
if (traceLocks) CHK(Hold)
|
||||
CHK(Hold)
|
||||
return YES;
|
||||
}
|
||||
if (EDEADLK == err)
|
||||
|
@ -341,12 +352,23 @@ MLOCK
|
|||
}
|
||||
|
||||
MNAME
|
||||
MSTACK
|
||||
MTRYLOCK
|
||||
MUNLOCK
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSRecursiveLock
|
||||
|
||||
+ (id) allocWithZone: (NSZone*)z
|
||||
{
|
||||
if (self == baseRecursiveLockClass && YES == traceLocks)
|
||||
{
|
||||
return class_createInstance(tracedRecursiveLockClass, 0);
|
||||
}
|
||||
return class_createInstance(self, 0);
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
[NSLock class]; // Ensure mutex attributes are set up.
|
||||
|
@ -372,12 +394,22 @@ MISLOCKED
|
|||
MLOCK
|
||||
MLOCKBEFOREDATE
|
||||
MNAME
|
||||
MSTACK
|
||||
MTRYLOCK
|
||||
MUNLOCK
|
||||
@end
|
||||
|
||||
@implementation NSCondition
|
||||
|
||||
+ (id) allocWithZone: (NSZone*)z
|
||||
{
|
||||
if (self == baseConditionClass && YES == traceLocks)
|
||||
{
|
||||
return class_createInstance(tracedConditionClass, 0);
|
||||
}
|
||||
return class_createInstance(self, 0);
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
[NSLock class]; // Ensure mutex attributes are set up.
|
||||
|
@ -424,23 +456,13 @@ MNAME
|
|||
pthread_cond_signal(&_condition);
|
||||
}
|
||||
|
||||
MSTACK
|
||||
MTRYLOCK
|
||||
MUNLOCK
|
||||
|
||||
- (void) wait
|
||||
{
|
||||
if (traceLocks)
|
||||
{
|
||||
NSThread *t = GSCurrentThread();
|
||||
CHKT(t,Drop)
|
||||
CHKT(t,Wait)
|
||||
pthread_cond_wait(&_condition, &_mutex);
|
||||
CHKT(t,Hold)
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_cond_wait(&_condition, &_mutex);
|
||||
}
|
||||
pthread_cond_wait(&_condition, &_mutex);
|
||||
}
|
||||
|
||||
- (BOOL) waitUntilDate: (NSDate*)limit
|
||||
|
@ -459,36 +481,15 @@ MUNLOCK
|
|||
/* NB. On timeout the lock is still held even through condition is not met
|
||||
*/
|
||||
|
||||
if (traceLocks)
|
||||
retVal = pthread_cond_timedwait(&_condition, &_mutex, &timeout);
|
||||
if (retVal == 0)
|
||||
{
|
||||
NSThread *t = GSCurrentThread();
|
||||
|
||||
CHKT(t,Drop)
|
||||
retVal = pthread_cond_timedwait(&_condition, &_mutex, &timeout);
|
||||
if (retVal == 0)
|
||||
{
|
||||
CHKT(t,Hold)
|
||||
return YES;
|
||||
}
|
||||
if (retVal == ETIMEDOUT)
|
||||
{
|
||||
CHKT(t,Hold)
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
if (retVal == ETIMEDOUT)
|
||||
{
|
||||
retVal = pthread_cond_timedwait(&_condition, &_mutex, &timeout);
|
||||
if (retVal == 0)
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
if (retVal == ETIMEDOUT)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (retVal == EINVAL)
|
||||
{
|
||||
NSLog(@"Invalid arguments to pthread_cond_timedwait");
|
||||
|
@ -500,6 +501,15 @@ MUNLOCK
|
|||
|
||||
@implementation NSConditionLock
|
||||
|
||||
+ (id) allocWithZone: (NSZone*)z
|
||||
{
|
||||
if (self == baseConditionLockClass && YES == traceLocks)
|
||||
{
|
||||
return class_createInstance(tracedConditionLockClass, 0);
|
||||
}
|
||||
return class_createInstance(self, 0);
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
[NSLock class]; // Ensure mutex attributes are set up.
|
||||
|
@ -587,6 +597,7 @@ MUNLOCK
|
|||
}
|
||||
|
||||
MNAME
|
||||
MSTACK
|
||||
|
||||
- (BOOL) tryLock
|
||||
{
|
||||
|
@ -627,10 +638,6 @@ MNAME
|
|||
|
||||
/* Versions of the lock classes where the locking is unconditionally traced
|
||||
*/
|
||||
@interface GSTracedRecursiveLock : NSRecursiveLock
|
||||
@end
|
||||
@interface GSTracedLock : NSLock
|
||||
@end
|
||||
|
||||
#undef CHKT
|
||||
#define CHKT(T,X) \
|
||||
|
@ -641,20 +648,151 @@ MNAME
|
|||
(*_NSLock_error_handler)(self, _cmd, YES, msg); \
|
||||
} \
|
||||
}
|
||||
#undef CHK
|
||||
#define CHK(X) CHKT(GSCurrentThread(), X)
|
||||
|
||||
#undef MDEALLOC
|
||||
#define MDEALLOC \
|
||||
- (void) dealloc \
|
||||
{ \
|
||||
DESTROY(stack); \
|
||||
[super dealloc]; \
|
||||
}
|
||||
|
||||
#undef MLOCK
|
||||
#define MLOCK \
|
||||
- (void) lock\
|
||||
MLOCK_TRACED
|
||||
{ \
|
||||
NSThread *t = GSCurrentThread(); \
|
||||
CHKT(t,Wait) \
|
||||
int err = pthread_mutex_lock(&_mutex);\
|
||||
if (EDEADLK == err)\
|
||||
{\
|
||||
CHKT(t,Drop) \
|
||||
(*_NSLock_error_handler)(self, _cmd, YES, @"deadlock");\
|
||||
}\
|
||||
else if (err != 0)\
|
||||
{\
|
||||
CHKT(t,Drop) \
|
||||
[NSException raise: NSLockException format: @"failed to lock mutex"];\
|
||||
}\
|
||||
CHKT(t,Hold) \
|
||||
}
|
||||
|
||||
@implementation GSTracedLock
|
||||
#undef MSTACK
|
||||
#define MSTACK \
|
||||
- (GSStackTrace*) stack \
|
||||
{ \
|
||||
if (nil == stack) \
|
||||
{ \
|
||||
stack = [GSStackTrace new]; \
|
||||
} \
|
||||
return stack; \
|
||||
}
|
||||
|
||||
@implementation GSTracedCondition
|
||||
+ (id) allocWithZone: (NSZone*)z
|
||||
{
|
||||
return class_createInstance(tracedConditionClass, 0);
|
||||
}
|
||||
MDEALLOC
|
||||
MLOCK
|
||||
MLOCKBEFOREDATE
|
||||
MSTACK
|
||||
MTRYLOCK
|
||||
|
||||
- (void) wait
|
||||
{
|
||||
NSThread *t = GSCurrentThread();
|
||||
CHKT(t,Drop)
|
||||
CHKT(t,Wait)
|
||||
pthread_cond_wait(&_condition, &_mutex);
|
||||
CHKT(t,Hold)
|
||||
}
|
||||
|
||||
- (BOOL) waitUntilDate: (NSDate*)limit
|
||||
{
|
||||
NSTimeInterval ti = [limit timeIntervalSince1970];
|
||||
NSThread *t = GSCurrentThread();
|
||||
double secs, subsecs;
|
||||
struct timespec timeout;
|
||||
int retVal = 0;
|
||||
|
||||
// Split the float into seconds and fractions of a second
|
||||
subsecs = modf(ti, &secs);
|
||||
timeout.tv_sec = secs;
|
||||
// Convert fractions of a second to nanoseconds
|
||||
timeout.tv_nsec = subsecs * 1e9;
|
||||
|
||||
/* NB. On timeout the lock is still held even through condition is not met
|
||||
*/
|
||||
|
||||
CHKT(t,Drop)
|
||||
retVal = pthread_cond_timedwait(&_condition, &_mutex, &timeout);
|
||||
if (retVal == 0)
|
||||
{
|
||||
CHKT(t,Hold)
|
||||
return YES;
|
||||
}
|
||||
if (retVal == ETIMEDOUT)
|
||||
{
|
||||
CHKT(t,Hold)
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (retVal == EINVAL)
|
||||
{
|
||||
NSLog(@"Invalid arguments to pthread_cond_timedwait");
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
MUNLOCK
|
||||
@end
|
||||
@implementation GSTracedConditionLock
|
||||
+ (id) allocWithZone: (NSZone*)z
|
||||
{
|
||||
return class_createInstance(tracedConditionLockClass, 0);
|
||||
}
|
||||
- (id) initWithCondition: (NSInteger)value
|
||||
{
|
||||
if (nil != (self = [super init]))
|
||||
{
|
||||
if (nil == (_condition = [GSTracedCondition new]))
|
||||
{
|
||||
DESTROY(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
_condition_value = value;
|
||||
[_condition setName:
|
||||
[NSString stringWithFormat: @"condition-for-lock-%p", self]];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
@implementation GSTracedLock
|
||||
+ (id) allocWithZone: (NSZone*)z
|
||||
{
|
||||
return class_createInstance(tracedLockClass, 0);
|
||||
}
|
||||
MDEALLOC
|
||||
MLOCK
|
||||
MLOCKBEFOREDATE
|
||||
MSTACK
|
||||
MTRYLOCK
|
||||
MUNLOCK
|
||||
@end
|
||||
@implementation GSTracedRecursiveLock
|
||||
+ (id) allocWithZone: (NSZone*)z
|
||||
{
|
||||
return class_createInstance(tracedRecursiveLockClass, 0);
|
||||
}
|
||||
MDEALLOC
|
||||
MLOCK
|
||||
MLOCKBEFOREDATE
|
||||
MSTACK
|
||||
MTRYLOCK
|
||||
MUNLOCK
|
||||
@end
|
||||
|
@ -662,22 +800,28 @@ MUNLOCK
|
|||
|
||||
/* Versions of the lock classes where the locking is never traced
|
||||
*/
|
||||
#undef CHKT
|
||||
#define CHKT(T,X)
|
||||
#undef MLOCK
|
||||
#define MLOCK \
|
||||
- (void) lock\
|
||||
MLOCK_UNTRACED
|
||||
@implementation GSUntracedCondition
|
||||
+ (id) allocWithZone: (NSZone*)z
|
||||
{
|
||||
return class_createInstance(baseConditionClass, 0);
|
||||
}
|
||||
@end
|
||||
@implementation GSUntracedConditionLock
|
||||
+ (id) allocWithZone: (NSZone*)z
|
||||
{
|
||||
return class_createInstance(baseConditionLockClass, 0);
|
||||
}
|
||||
@end
|
||||
@implementation GSUntracedLock
|
||||
MLOCK
|
||||
MLOCKBEFOREDATE
|
||||
MTRYLOCK
|
||||
MUNLOCK
|
||||
+ (id) allocWithZone: (NSZone*)z
|
||||
{
|
||||
return class_createInstance(baseRecursiveLockClass, 0);
|
||||
}
|
||||
@end
|
||||
@implementation GSUntracedRecursiveLock
|
||||
MLOCK
|
||||
MLOCKBEFOREDATE
|
||||
MTRYLOCK
|
||||
MUNLOCK
|
||||
+ (id) allocWithZone: (NSZone*)z
|
||||
{
|
||||
return class_createInstance(baseRecursiveLockClass, 0);
|
||||
}
|
||||
@end
|
||||
|
||||
|
|
|
@ -1363,12 +1363,13 @@ lockInfoErr(NSString *str)
|
|||
|
||||
- (NSString *) mutexDrop: (id)mutex
|
||||
{
|
||||
if (GS_EXISTS_INTERNAL && traceLocks)
|
||||
if (GS_EXISTS_INTERNAL)
|
||||
{
|
||||
GSLockInfo *li = &lockInfo;
|
||||
GSStackTrace *stck;
|
||||
int err;
|
||||
|
||||
if (NO == traceLocks) return nil;
|
||||
err = pthread_spin_lock(&li->spin);
|
||||
if (EDEADLK == err) return lockInfoErr(@"thread spin lock deadlocked");
|
||||
if (EINVAL == err) return lockInfoErr(@"thread spin lock invalid");
|
||||
|
@ -1387,16 +1388,16 @@ lockInfoErr(NSString *str)
|
|||
if (stck->recursion-- == 0)
|
||||
{
|
||||
NSMapRemove(li->held, (void*)mutex);
|
||||
//fprintf(stderr, "%lu: Drop %p (final) %lu\n", (unsigned long)_threadID, mutex, [li->held count]);
|
||||
fprintf(stderr, "%lu: Drop %p (final) %lu\n", (unsigned long)_threadID, mutex, [li->held count]);
|
||||
}
|
||||
else
|
||||
{
|
||||
//fprintf(stderr, "%lu: Drop %p (%lu) %lu\n", (unsigned long)threadID, mutex, (unsigned long)stck->recursion, [li->held count]);
|
||||
fprintf(stderr, "%lu: Drop %p (%lu) %lu\n", (unsigned long)threadID, mutex, (unsigned long)stck->recursion, [li->held count]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//fprintf(stderr, "%lu: Drop %p (bad) %lu\n", (unsigned long)threadID, mutex, [li->held count]);
|
||||
fprintf(stderr, "%lu: Drop %p (bad) %lu\n", (unsigned long)threadID, mutex, [li->held count]);
|
||||
pthread_spin_unlock(&li->spin);
|
||||
return lockInfoErr(
|
||||
@"attempt to unlock mutex not locked by this thread");
|
||||
|
@ -1409,12 +1410,13 @@ lockInfoErr(NSString *str)
|
|||
|
||||
- (NSString *) mutexHold: (id)mutex
|
||||
{
|
||||
if (GS_EXISTS_INTERNAL && traceLocks)
|
||||
if (GS_EXISTS_INTERNAL)
|
||||
{
|
||||
GSLockInfo *li = &lockInfo;
|
||||
GSStackTrace *stck;
|
||||
int err;
|
||||
|
||||
if (NO == traceLocks) return nil;
|
||||
err = pthread_spin_lock(&li->spin);
|
||||
if (EDEADLK == err) return lockInfoErr(@"thread spin lock deadlocked");
|
||||
if (EINVAL == err) return lockInfoErr(@"thread spin lock invalid");
|
||||
|
@ -1435,15 +1437,15 @@ lockInfoErr(NSString *str)
|
|||
stck = NSMapGet(li->held, (void*)mutex);
|
||||
if (nil == stck)
|
||||
{
|
||||
stck = [GSStackTrace new];
|
||||
stck = [GSStackTrace new]; [stck trace];
|
||||
NSMapInsert(li->held, (void*)mutex, (void*)stck);
|
||||
RELEASE(stck);
|
||||
//fprintf(stderr, "%lu: Hold %p (initial) %lu\n", (unsigned long)threadID, mutex, [li->held count]);
|
||||
fprintf(stderr, "%lu: Hold %p (initial) %lu\n", (unsigned long)threadID, mutex, [li->held count]);
|
||||
}
|
||||
else
|
||||
{
|
||||
stck->recursion++;
|
||||
//fprintf(stderr, "%lu: Hold %p (%lu) %lu\n", (unsigned long)threadID, mutex, (unsigned long)stck->recursion, [li->held count]);
|
||||
fprintf(stderr, "%lu: Hold %p (%lu) %lu\n", (unsigned long)threadID, mutex, (unsigned long)stck->recursion, [li->held count]);
|
||||
}
|
||||
li->wait = nil;
|
||||
pthread_spin_unlock(&li->spin);
|
||||
|
@ -1454,12 +1456,13 @@ lockInfoErr(NSString *str)
|
|||
|
||||
- (NSString *) mutexWait: (id)mutex
|
||||
{
|
||||
if (GS_EXISTS_INTERNAL && traceLocks)
|
||||
if (GS_EXISTS_INTERNAL)
|
||||
{
|
||||
GSLockInfo *li = &lockInfo;
|
||||
BOOL owned = NO;
|
||||
int err;
|
||||
|
||||
if (NO == traceLocks) return nil;
|
||||
err = pthread_spin_lock(&li->spin);
|
||||
if (EDEADLK == err) return lockInfoErr(@"thread spin lock deadlocked");
|
||||
if (EINVAL == err) return lockInfoErr(@"thread spin lock invalid");
|
||||
|
@ -1477,6 +1480,7 @@ lockInfoErr(NSString *str)
|
|||
owned = YES;
|
||||
}
|
||||
pthread_spin_unlock(&li->spin);
|
||||
fprintf(stderr, "%lu: Wait %p\n", (unsigned long)_threadID, mutex);
|
||||
if (YES == owned && [mutex isKindOfClass: [NSRecursiveLock class]])
|
||||
{
|
||||
return nil; // We can't deadlock on a recursive lock we own
|
||||
|
@ -1625,7 +1629,7 @@ lockInfoErr(NSString *str)
|
|||
|
||||
if (nil != dependencies)
|
||||
{
|
||||
GSStackTrace *stack = [GSStackTrace new];
|
||||
GSStackTrace *stack = [GSStackTrace new]; [stack trace];
|
||||
NSUInteger count;
|
||||
NSUInteger index = 0;
|
||||
NSMutableString *m;
|
||||
|
|
Loading…
Reference in a new issue