locking updates (fine grained locking rather than global lock)

This commit is contained in:
rfm 2024-05-30 10:40:52 +01:00
parent 430629b097
commit 241e2a47ca
24 changed files with 176 additions and 732 deletions

View file

@ -39,7 +39,6 @@ Additions_OBJC_FILES =\
GCObject.m \
GCArray.m \
GCDictionary.m \
GSLock.m \
GSMime.m \
GSXML.m \
GSFunctions.m \
@ -53,7 +52,6 @@ Additions_OBJC_FILES =\
NSError+GNUstepBase.m \
NSHashTable+GNUstepBase.m \
NSFileHandle+GNUstepBase.m \
NSLock+GNUstepBase.m \
NSMutableString+GNUstepBase.m \
NSNumber+GNUstepBase.m \
NSObject+GNUstepBase.m \

View file

@ -1,305 +0,0 @@
/** Implementation for GSLock
Copyright (C) 2003 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: October 2003
This file is part of the GNUstep Base Library.
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.
*/
#import "common.h"
#define EXPOSE_GSLock_IVARS 1
#import "Foundation/NSException.h"
#import "Foundation/NSLock.h"
#import "Foundation/NSNotification.h"
#import "Foundation/NSThread.h"
#import "GNUstepBase/GSLock.h"
/**
* This implements a class which, when used in single-threaded mode,
* acts like a lock while avoiding the overheads of actually using
* a real lock. However, when the programm in which the class is
* used becomes multi-threaded, all instances of this class transform
* themselves into real locks in the correct state (locked/unlocked)
* corresponding to whether the lazy lock was locked or not at the
* point where the program became multi threadeed.<br />
* Use of this class allows you to write thread-safe code which avoids
* locking inefficiencies when used in a single threaded application,
* without having to worry about dealing with the issue yourself.
*/
@implementation GSLazyLock
/**
* Do not use this method ... it is used internally to handle the transition
* from a single threaded system to a multi threaded one.
*/
- (void) _becomeThreaded: (NSNotification*)n
{
[[NSNotificationCenter defaultCenter] removeObserver: self];
object_setClass(self, [NSLock class]);
if (locked == YES)
{
if ([self tryLock] == NO)
{
[NSException raise: NSInternalInconsistencyException
format: @"Failed to lock mutex"];
}
}
/*
* While we have changed 'isa', it's possible someone might have
* cached our old method implementations, so we set the 'locked'
* ivar to a value to tell the old method implementations to use
* the superclass implementatins.
*/
locked = -1;
}
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver: self];
[super dealloc];
}
- (id) init
{
self = [super init];
if ([NSThread isMultiThreaded] == YES)
{
DESTROY(self);
return (GSLazyLock*)[NSLock new];
}
else if (self != nil)
{
locked = NO;
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector(_becomeThreaded:)
name: NSWillBecomeMultiThreadedNotification
object: nil];
}
return self;
}
- (void) lock
{
if (locked == NO)
{
locked = YES;
}
else if (locked == YES)
{
[NSException raise: NSGenericException
format: @"lock: when already locked"];
}
else
{
[super lock];
}
}
- (BOOL) lockBeforeDate: (NSDate*)limit
{
BOOL result;
if (locked == NO)
{
result = YES;
}
else if (locked == YES)
{
result = NO;
[NSException raise: NSGenericException
format: @"lock: when already locked"];
}
else
{
result = [super lockBeforeDate: limit];
}
return result;
}
- (BOOL) tryLock
{
if (locked == NO)
{
locked = YES;
return YES;
}
else if (locked == YES)
{
return NO;
}
else
{
return [super tryLock];
}
}
- (void) unlock
{
if (locked == YES)
{
locked = NO;
}
else if (locked == NO)
{
[NSException raise: NSGenericException
format: @"unlock: when already unlocked"];
}
else
{
[super unlock];
}
}
@end
/**
* This implements a class which, when used in single-threaded mode,
* acts like a recursive lock while avoiding the overheads of using
* a real lock. However, when the programm in which the class is
* used becomes multi-threaded, all instances of this class transform
* themselves into real locks in the correct state (locked/unlocked)
* corresponding to whether the lazy recursive lock was locked or not
* at the point where the program became multi threadeed.<br />
* Use of this class allows you to write thread-safe code which avoids
* locking inefficiencies when used in a single threaded application,
* without having to worry about dealing with the issue yourself.
*/
@implementation GSLazyRecursiveLock
/**
* Do not use this method ... it is used internally to handle the transition
* from a single threaded system to a multi threaded one.
*/
- (void) _becomeThreaded: (NSNotification*)n
{
[[NSNotificationCenter defaultCenter] removeObserver: self];
object_setClass(self, [NSRecursiveLock class]);
while (counter-- > 0)
{
if ([self tryLock] == NO)
{
[NSException raise: NSInternalInconsistencyException
format: @"Failed to lock mutex"];
}
}
/*
* While we have changed 'isa', it's possible someone might have
* cached our old method implementations, so we set the 'locked'
* ivar to a value to tell the old method implementations to use
* the superclass implementatins.
*/
counter = -1;
}
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver: self];
[super dealloc];
}
- (id) init
{
self = [super init];
if ([NSThread isMultiThreaded] == YES)
{
DESTROY(self);
return (GSLazyRecursiveLock*)[NSRecursiveLock new];
}
else
{
if (self != nil)
{
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector(_becomeThreaded:)
name: NSWillBecomeMultiThreadedNotification
object: nil];
}
}
return self;
}
- (void) lock
{
if (counter >= 0)
{
counter++;
}
else
{
[super lock];
}
}
- (BOOL) lockBeforeDate: (NSDate*)limit
{
if (counter >= 0)
{
counter++;
return YES;
}
else
{
return [super lockBeforeDate: limit];
}
}
- (BOOL) tryLock
{
if (counter >= 0)
{
counter++;
return YES;
}
else
{
return [super tryLock];
}
}
- (void) unlock
{
if (counter > 0)
{
counter--;
}
else if (counter == 0)
{
[NSException raise: NSGenericException
format: @"unlock: failed to unlock mutex"];
}
else
{
[super unlock];
}
}
@end
/* Global lock to be used by classes when operating on any global
data that invoke other methods which also access global; thus,
creating the potential for deadlock. */
NSRecursiveLock *gnustep_global_lock = nil;

View file

@ -48,7 +48,7 @@ strerror_r(int eno, char *buf, int len)
const char *ptr;
int result;
[gnustep_global_lock lock];
[GSPrivateGlobalLock() lock];
ptr = strerror(eno);
if (ptr == 0)
{
@ -61,7 +61,7 @@ strerror_r(int eno, char *buf, int len)
result = 0;
}
buf[len - 1] = '\0';
[gnustep_global_lock unlock];
[GSPrivateGlobalLock() unlock];
return result;
}
#else

View file

@ -1,100 +0,0 @@
/* Implementation of extension methods to base additions
Copyright (C) 2010 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
This file is part of the GNUstep Base Library.
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.
*/
#import "common.h"
#import "Foundation/NSException.h"
#import "GNUstepBase/NSLock+GNUstepBase.h"
#import "GNUstepBase/GSLock.h"
/**
* GNUstep specific (non-standard) additions to the NSLock class.
*/
static GSLazyRecursiveLock *local_lock = nil;
/* This class only exists to provide a thread safe mechanism to
initialize local_lock as +initialize is called under a lock in ObjC
runtimes. User code should resort to GS_INITIALIZED_LOCK(), which
uses the +newLockAt: extension. */
@interface _GSLockInitializer : NSObject
@end
@implementation _GSLockInitializer
+ (void) initialize
{
if (local_lock == nil)
{
/* As we do not know whether creating custom locks may
implicitly create other locks, we use a recursive lock. */
local_lock = [GSLazyRecursiveLock new];
}
}
@end
static inline id
newLockAt(Class self, SEL _cmd, id *location)
{
if (location == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"'%@' called with nil location",
NSStringFromSelector(_cmd)];
}
if (*location == nil)
{
if (local_lock == nil)
{
[_GSLockInitializer class];
}
[local_lock lock];
if (*location == nil)
{
*location = [[(id)self alloc] init];
}
[local_lock unlock];
}
return *location;
}
@implementation NSLock (GNUstepBase)
+ (id) newLockAt: (id *)location
{
return newLockAt(self, _cmd, location);
}
@end
@implementation NSRecursiveLock (GNUstepBase)
+ (id) newLockAt: (id *)location
{
return newLockAt(self, _cmd, location);
}
@end

View file

@ -23,6 +23,7 @@
*/
#import "common.h"
#import "GSPThread.h"
#import "Foundation/NSArray.h"
#import "Foundation/NSException.h"
#import "Foundation/NSHashTable.h"
@ -162,12 +163,14 @@ static inline void setup()
{
if (nil == exitLock)
{
[gnustep_global_lock lock];
static gs_mutex_t setupLock = GS_MUTEX_INIT_STATIC;
GS_MUTEX_LOCK(setupLock);
if (nil == exitLock)
{
exitLock = [NSLock new];
}
[gnustep_global_lock unlock];
GS_MUTEX_UNLOCK(setupLock);
}
}

View file

@ -3100,7 +3100,7 @@ GSPrivateNativeCStringEncoding()
char *old;
/* Take it from the system locale information. */
[gnustep_global_lock lock];
[GSPrivateGlobalLock() lock];
/* Initialise locale system by setting current locale from
* environment and then resetting it. Must be done before
* any call to nl_langinfo()
@ -3111,7 +3111,7 @@ GSPrivateNativeCStringEncoding()
}
strncpy(encbuf, nl_langinfo(CODESET), sizeof(encbuf)-1);
encbuf[sizeof(encbuf)-1] = '\0';
[gnustep_global_lock unlock];
[GSPrivateGlobalLock() unlock];
#else
encbuf[0] = '\0';
#endif