Thread-safety fixes.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@39902 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2016-06-22 09:09:29 +00:00
parent 0c7237ec08
commit 1b63746799
3 changed files with 148 additions and 93 deletions

View file

@ -6,6 +6,8 @@
* Source/NSOperation.m: When starting an operation, have it retain * Source/NSOperation.m: When starting an operation, have it retain
itself in case it'ss removed from the queue and released while itself in case it'ss removed from the queue and released while
running. running.
* Headers/Foundation/NSDistributedLock.h: Add lock ivar.
* Source/NSDistributedLock.m: Make class thread-safe using lock.
2016-06-19 Richard Frith-Macdonald <rfm@gnu.org> 2016-06-19 Richard Frith-Macdonald <rfm@gnu.org>

View file

@ -27,8 +27,10 @@
#import <GNUstepBase/GSVersionMacros.h> #import <GNUstepBase/GSVersionMacros.h>
#import <Foundation/NSObject.h> #import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
#import <Foundation/NSDate.h> @class NSDate;
@class NSLock;
@class NSString;
#if defined(__cplusplus) #if defined(__cplusplus)
extern "C" { extern "C" {
@ -39,6 +41,7 @@ extern "C" {
#if GS_EXPOSE(NSDistributedLock) #if GS_EXPOSE(NSDistributedLock)
NSString *_lockPath; NSString *_lockPath;
NSDate *_lockTime; NSDate *_lockTime;
NSLock *_localLock;
#endif #endif
#if GS_NONFRAGILE #if GS_NONFRAGILE
#else #else

View file

@ -28,8 +28,9 @@
#import "common.h" #import "common.h"
#define EXPOSE_NSDistributedLock_IVARS 1 #define EXPOSE_NSDistributedLock_IVARS 1
#import "Foundation/NSDistributedLock.h" #import "Foundation/NSDistributedLock.h"
#import "Foundation/NSFileManager.h"
#import "Foundation/NSException.h" #import "Foundation/NSException.h"
#import "Foundation/NSFileManager.h"
#import "Foundation/NSLock.h"
#import "Foundation/NSValue.h" #import "Foundation/NSValue.h"
#import "GSPrivate.h" #import "GSPrivate.h"
@ -73,6 +74,9 @@ static NSFileManager *mgr = nil;
* Raises an NSGenericException if unable to remove the lock. * Raises an NSGenericException if unable to remove the lock.
*/ */
- (void) breakLock - (void) breakLock
{
[_localLock lock];
NS_DURING
{ {
NSDictionary *attributes; NSDictionary *attributes;
@ -86,7 +90,8 @@ static NSFileManager *mgr = nil;
{ {
NSString *err = [[NSError _last] localizedDescription]; NSString *err = [[NSError _last] localizedDescription];
attributes = [mgr fileAttributesAtPath: _lockPath traverseLink: YES]; attributes = [mgr fileAttributesAtPath: _lockPath
traverseLink: YES];
if ([modDate isEqual: [attributes fileModificationDate]] == YES) if ([modDate isEqual: [attributes fileModificationDate]] == YES)
{ {
[NSException raise: NSGenericException [NSException raise: NSGenericException
@ -96,6 +101,14 @@ static NSFileManager *mgr = nil;
} }
} }
} }
NS_HANDLER
{
[_localLock unlock];
[localException raise];
}
NS_ENDHANDLER
[_localLock unlock];
}
- (void) dealloc - (void) dealloc
{ {
@ -107,21 +120,27 @@ static NSFileManager *mgr = nil;
} }
RELEASE(_lockPath); RELEASE(_lockPath);
RELEASE(_lockTime); RELEASE(_lockTime);
RELEASE(_localLock);
[super dealloc]; [super dealloc];
} }
- (NSString*) description - (NSString*) description
{ {
NSString *result;
[_localLock lock];
if (_lockTime == nil) if (_lockTime == nil)
{ {
return [[super description] stringByAppendingFormat: result = [[super description] stringByAppendingFormat:
@" path '%@' not locked", _lockPath]; @" path '%@' not locked", _lockPath];
} }
else else
{ {
return [[super description] stringByAppendingFormat: result = [[super description] stringByAppendingFormat:
@" path '%@' locked at %@", _lockPath, _lockTime]; @" path '%@' locked at %@", _lockPath, _lockTime];
} }
[_localLock unlock];
return result;
} }
/** /**
@ -137,6 +156,7 @@ static NSFileManager *mgr = nil;
NSString *lockDir; NSString *lockDir;
BOOL isDirectory; BOOL isDirectory;
_localLock = [NSLock new];
_lockPath = [[aPath stringByStandardizingPath] copy]; _lockPath = [[aPath stringByStandardizingPath] copy];
_lockTime = nil; _lockTime = nil;
@ -188,11 +208,21 @@ static NSFileManager *mgr = nil;
* May raise an NSGenericException if a problem occurs. * May raise an NSGenericException if a problem occurs.
*/ */
- (BOOL) tryLock - (BOOL) tryLock
{
BOOL locked = NO;
[_localLock lock];
NS_DURING
{ {
NSMutableDictionary *attributesToSet; NSMutableDictionary *attributesToSet;
NSDictionary *attributes; NSDictionary *attributes;
BOOL locked;
if (nil != _lockTime)
{
[NSException raise: NSGenericException
format: @"Attempt to re-lock distributed lock %@",
_lockPath];
}
attributesToSet = [NSMutableDictionary dictionaryWithCapacity: 1]; attributesToSet = [NSMutableDictionary dictionaryWithCapacity: 1];
[attributesToSet setObject: [NSNumber numberWithUnsignedInt: 0755] [attributesToSet setObject: [NSNumber numberWithUnsignedInt: 0755]
forKey: NSFilePosixPermissions]; forKey: NSFilePosixPermissions];
@ -201,14 +231,14 @@ static NSFileManager *mgr = nil;
withIntermediateDirectories: YES withIntermediateDirectories: YES
attributes: attributesToSet attributes: attributesToSet
error: NULL]; error: NULL];
if (locked == NO) if (NO == locked)
{ {
BOOL dir; BOOL dir;
/* /* We expect the directory creation to have failed because
* We expect the directory creation to have failed because it already * it already exists as another processes lock.
* exists as another processes lock. If the directory doesn't exist, * If the directory doesn't exist, then either the other
* then either the other process has removed it's lock (and we can retry) * process has removed it's lock (and we can retry)
* or we have a severe problem! * or we have a severe problem!
*/ */
if ([mgr fileExistsAtPath: _lockPath isDirectory: &dir] == NO) if ([mgr fileExistsAtPath: _lockPath isDirectory: &dir] == NO)
@ -217,7 +247,7 @@ static NSFileManager *mgr = nil;
withIntermediateDirectories: YES withIntermediateDirectories: YES
attributes: attributesToSet attributes: attributesToSet
error: NULL]; error: NULL];
if (locked == NO) if (NO == locked)
{ {
NSLog(@"Failed to create lock directory '%@' - %@", NSLog(@"Failed to create lock directory '%@' - %@",
_lockPath, [NSError _last]); _lockPath, [NSError _last]);
@ -225,11 +255,7 @@ static NSFileManager *mgr = nil;
} }
} }
if (locked == NO) if (YES == locked)
{
return NO;
}
else
{ {
attributes = [mgr fileAttributesAtPath: _lockPath attributes = [mgr fileAttributesAtPath: _lockPath
traverseLink: YES]; traverseLink: YES];
@ -240,9 +266,23 @@ static NSFileManager *mgr = nil;
_lockPath]; _lockPath];
} }
ASSIGN(_lockTime, [attributes fileModificationDate]); ASSIGN(_lockTime, [attributes fileModificationDate]);
return YES; if (nil == _lockTime)
{
[NSException raise: NSGenericException
format: @"Unable to get date of lock file we made at %@",
_lockPath];
} }
} }
}
NS_HANDLER
{
[_localLock unlock];
[localException raise];
}
NS_ENDHANDLER
[_localLock unlock];
return locked;
}
/** /**
* Releases the lock. Raises an NSGenericException if unable to release * Releases the lock. Raises an NSGenericException if unable to release
@ -250,6 +290,9 @@ static NSFileManager *mgr = nil;
* process has broken it). * process has broken it).
*/ */
- (void) unlock - (void) unlock
{
[_localLock lock];
NS_DURING
{ {
NSDictionary *attributes; NSDictionary *attributes;
@ -258,8 +301,7 @@ static NSFileManager *mgr = nil;
[NSException raise: NSGenericException format: @"not locked by us"]; [NSException raise: NSGenericException format: @"not locked by us"];
} }
/* /* Don't remove the lock if it has already been broken by someone
* Don't remove the lock if it has already been broken by someone
* else and re-created. Unfortunately, there is a window between * else and re-created. Unfortunately, there is a window between
* testing and removing, but we do the bset we can. * testing and removing, but we do the bset we can.
*/ */
@ -289,5 +331,13 @@ static NSFileManager *mgr = nil;
} }
DESTROY(_lockTime); DESTROY(_lockTime);
} }
NS_HANDLER
{
[_localLock unlock];
[localException raise];
}
NS_ENDHANDLER
[_localLock unlock];
}
@end @end