* Headers/Additions/GNUstepBase/GSLock.h: Added missing

forward declaration.
	* Headers/Additions/GNUstepBase/GSCategories.h: Remove
	declaraion of gnustep_global_lock.
	(GS_INITIALIZED_LOCK): New macro.
	(+[NSLock newLockAt:]): New method.
	(+[NSRecursiveLock newLockAt:]): Ditto.
	* Headers/Foundation/NSLock.h: Ditto.
	* Source/Additions/GSCategories.m: Replace global lock with
	local lock.
	(_GSLockInitializer): New class to initialize local lock
	safely.
	(newLockAt): New static function shared by +newLockAt:
	implementations to safely intialize lock variables.
	(+[NSLock newLockAt:]): Implemented and documented.
	(+[NSRecursiveLock newLockAt:]): Ditto.
	* Source/Additions/GSCompatibility.m: Remove
	gnustep_global_lock.
	* Source/Additions/GSObjCRuntime.m: Remove superfluous
	locking.
	* Source/Additions/Unicode.m: Use new GS_INITIALIZED_LOCK
	macro and replace global lock with local lock.
	* Source/NSLock.m
	(-[NSConditionLock lockWhenCondition:beforeDate:]):
	Implemented.
	* Testing/gslock.m: New test case.
	* Testing/GNUmakefile: Add new test case.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@18010 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
ayers 2003-10-30 13:44:55 +00:00
parent b462208383
commit 86f35c472c
11 changed files with 471 additions and 25 deletions

View file

@ -1,3 +1,29 @@
2003-10-30 David Ayers <d.ayers@inode.at>
* Headers/Additions/GNUstepBase/GSLock.h: Added missing forward
declaration.
* Headers/Additions/GNUstepBase/GSCategories.h: Remove declaraion
of gnustep_global_lock.
(GS_INITIALIZED_LOCK): New macro.
(+[NSLock newLockAt:]): New method.
(+[NSRecursiveLock newLockAt:]): Ditto.
* Headers/Foundation/NSLock.h: Ditto.
* Source/Additions/GSCategories.m: Replace global lock with local
lock.
(_GSLockInitializer): New class to initialize local lock safely.
(newLockAt): New static function shared by +newLockAt:
implementations to safely intialize lock variables.
(+[NSLock newLockAt:]): Implemented and documented.
(+[NSRecursiveLock newLockAt:]): Ditto.
* Source/Additions/GSCompatibility.m: Remove gnustep_global_lock.
* Source/Additions/GSObjCRuntime.m: Remove superfluous locking.
* Source/Additions/Unicode.m: Use new GS_INITIALIZED_LOCK macro
and replace global lock with local lock.
* Source/NSLock.m
(-[NSConditionLock lockWhenCondition:beforeDate:]): Implemented.
* Testing/gslock.m: New test case.
* Testing/GNUmakefile: Add new test case.
2003-10-28 Adam Fedor <fedor@gnu.org>
* configure.ac: Simplify and clarify ffi errors.

View file

@ -162,12 +162,9 @@ typedef enum _NSGNUstepStringEncoding
} NSGNUstepStringEncoding;
/* ------------------------------------------------------------------------
* Variables
*/
GS_EXPORT NSRecursiveLock *gnustep_global_lock;
/* Taken from base/Headers/Foundation/NSLock.h */
#define GS_INITIALIZED_LOCK(IDENT,CLASSNAME) \
(IDENT != nil ? IDENT : [CLASSNAME newLockAt: &IDENT])
/* ------------------------------------------------------------------------
* Class/Method Extensions
@ -279,6 +276,14 @@ GS_EXPORT NSRecursiveLock *gnustep_global_lock;
- (NSMutableSet *) debugSet;
@end
@interface NSLock (GSCategories)
+ (id)newLockAt:(NSLock **)location;
@end
@interface NSRecursiveLock (GSCategories)
+ (id)newLockAt:(NSLock **)location;
@end
/* ------------------------------------------------------------------------
* Functions

View file

@ -29,6 +29,8 @@
#include <Foundation/NSLock.h>
@class NSNotification;
@interface GSLazyLock : NSLock
{
int locked;

View file

@ -128,4 +128,42 @@
@end
#ifndef NO_GNUSTEP
/**
* Returns IDENT which will be initialized
* to an instance of a CLASSNAME in a thread safe manner.
* If IDENT has been previoulsy initilized
* this macro merely returns IDENT.
* IDENT is considered uninitialzed, if it contains nil.
* CLASSNAME must be either NSLock, NSRecursiveLock or one
* of thier subclasses.
* See [NSLock+newLockAt:] for details.
* This macro is intended for code that cannot insure
* that a lock can be initialized in thread safe manner otherwise.
* <example>
* NSLock *my_lock = nil;
*
* void function (void)
* {
* [GS_INITIALIZED_LOCK(my_lock, NSLock) lock];
* do_work ();
* [my_lock unlock];
* }
*
* </example>
*/
#define GS_INITIALIZED_LOCK(IDENT,CLASSNAME) \
(IDENT != nil ? IDENT : [CLASSNAME newLockAt: &IDENT])
@interface NSLock (GSCategories)
+ (id)newLockAt:(id *)location;
@end
@interface NSRecursiveLock (GSCategories)
+ (id)newLockAt:(id *)location;
@end
#endif /* NO_GNUSTEP */
#endif /* _GNUstep_H_NSLock*/

View file

@ -25,6 +25,7 @@
#include <string.h>
#include <Foundation/Foundation.h>
#include "GNUstepBase/GSCategories.h"
#include "GNUstepBase/GSLock.h"
/**
* Extension methods for the NSCalendarDate class
@ -874,3 +875,120 @@ static void MD5Transform (unsigned long buf[4], unsigned long const in[16])
}
@end
/**
* GNUstep specific (non-standard) additions to the NSLock class.
*/
static NSRecursiveLock *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)
{
local_lock = [GSLazyRecursiveLock new];
}
}
@end
GS_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 = [[self alloc] init];
}
[local_lock unlock];
}
return *location;
}
@implementation NSLock (GSCategories)
/**
* Initializes the id pointed to by location
* with a new instance of the receiver's class
* in a thread safe manner, unless
* it has been previously initialized.
* Returns the contents pointed to by location.
* The location is considered unintialized if it contains nil.
* <br/>
* This method is used in the GS_INITIALIZED_LOCK macro
* to initialize lock variables when it cannot be insured
* that they can be initialized in a thread safe environment.
* <example>
* NSLock *my_lock = nil;
*
* void function (void)
* {
* [GS_INITIALIZED_LOCK(my_lock, NSLock) lock];
* do_work ();
* [my_lock unlock];
* }
*
* </example>
*/
+ (id)newLockAt:(id *)location
{
return newLockAt(self, _cmd, location);
}
@end
@implementation NSRecursiveLock (GSCategories)
/**
* Initializes the id pointed to by location
* with a new instance of the receiver's class
* in a thread safe manner, unless
* it has been previously initialized.
* Returns the contents pointed to by location.
* The location is considered unintialized if it contains nil.
* <br/>
* This method is used in the GS_INITIALIZED_LOCK macro
* to initialize lock variables when it cannot be insured
* that they can be initialized in a thread safe environment.
* <example>
* NSLock *my_lock = nil;
*
* void function (void)
* {
* [GS_INITIALIZED_LOCK(my_lock, NSRecursiveLock) lock];
* do_work ();
* [my_lock unlock];
* }
*
* </example>
*/
+ (id)newLockAt:(id *)location
{
return newLockAt(self, _cmd, location);
}
@end

View file

@ -27,9 +27,6 @@
#include "GNUstepBase/GSCategories.h"
#include "GNUstepBase/GCObject.h"
/* FIXME: Need to initialize this */
NSRecursiveLock *gnustep_global_lock = NULL;
NSString *GetEncodingName(NSStringEncoding availableEncodingValue)
{
// Deprecated

View file

@ -1563,13 +1563,8 @@ GSPrintf (FILE *fptr, NSString* format, ...)
if (stringClass == 0)
{
[gnustep_global_lock lock];
if (stringClass == 0)
{
stringClass = [NSString class];
enc = [stringClass defaultCStringEncoding];
}
[gnustep_global_lock unlock];
stringClass = [NSString class];
enc = [stringClass defaultCStringEncoding];
}
message = [stringClass allocWithZone: NSDefaultMallocZone()];
va_start (ap, format);

View file

@ -39,6 +39,7 @@
#else
#include <Foundation/Foundation.h>
#endif
#include "GNUstepBase/GSLock.h"
#include "GNUstepBase/GSCategories.h"
#include "GNUstepBase/Unicode.h"
#include <stdio.h>
@ -82,6 +83,8 @@ typedef struct {unichar from; unsigned char to;} _ucc_;
#define UNICODE_ENC ((unicode_enc) ? unicode_enc : internal_unicode_enc())
static NSRecursiveLock *local_lock = nil;
static const char *unicode_enc = NULL;
/* Check to see what type of internal unicode format the library supports */
@ -207,7 +210,7 @@ static void GSSetupEncodingTable(void)
{
if (encodingTable == 0)
{
[gnustep_global_lock lock];
[GS_INITIALIZED_LOCK(local_lock, GSLazyRecursiveLock) lock];
if (encodingTable == 0)
{
static struct _strenc_ **encTable = 0;
@ -257,7 +260,7 @@ static void GSSetupEncodingTable(void)
}
encodingTable = encTable;
}
[gnustep_global_lock unlock];
[local_lock unlock];
}
}
@ -325,7 +328,7 @@ GetAvailableEncodings()
{
if (_availableEncodings == 0)
{
[gnustep_global_lock lock];
[GS_INITIALIZED_LOCK(local_lock, GSLazyRecursiveLock) lock];
if (_availableEncodings == 0)
{
NSStringEncoding *encodings;
@ -352,7 +355,7 @@ GetAvailableEncodings()
encodings[pos] = 0;
_availableEncodings = encodings;
}
[gnustep_global_lock unlock];
[local_lock unlock];
}
return _availableEncodings;
}
@ -559,10 +562,10 @@ GetDefEncoding()
char *encoding;
unsigned int count;
[gnustep_global_lock lock];
[GS_INITIALIZED_LOCK(local_lock, GSLazyRecursiveLock) lock];
if (defEnc != GSUndefinedEncoding)
{
[gnustep_global_lock unlock];
[local_lock unlock];
return defEnc;
}
@ -604,7 +607,7 @@ GetDefEncoding()
" NSISOLatin1StringEncoding set as default.\n");
defEnc = NSISOLatin1StringEncoding;
}
[gnustep_global_lock unlock];
[local_lock unlock];
}
return defEnc;
}

View file

@ -447,8 +447,40 @@ NSString *NSRecursiveLockException = @"NSRecursiveLockException";
beforeDate: (NSDate*)limitDate
{
#ifndef HAVE_OBJC_CONDITION_TIMEDWAIT
[self notImplemented: _cmd];
GSSleepInfo ctxt;
CHECK_RECURSIVE_CONDITION_LOCK(_mutex);
GSSleepInit(limitDate, &ctxt);
do
{
if (_condition_value == condition_to_meet)
{
while (objc_mutex_trylock(_mutex) == -1)
{
if (GSSleepOrFail(&ctxt) == NO)
{
return NO;
}
}
if (_condition_value == condition_to_meet)
{
return YES;
}
if (objc_mutex_unlock(_mutex) == -1)
{
[NSException raise: NSConditionLockException
format: @"%s failed to unlock mutex",
GSNameFromSelector(_cmd)];
/* NOT REACHED */
}
}
}
while (GSSleepOrFail(&ctxt) == YES);
return NO;
#else
NSTimeInterval atimeinterval;
struct timespec endtime;

View file

@ -36,6 +36,7 @@ CHECKABLE_TOOLS = \
call \
containers \
fref \
gslock \
nsarchiver \
nsarray \
nsattributedstring \
@ -74,7 +75,8 @@ ADDITIONAL_TOOLS = \
nsconnection_client \
nsconnection_server \
// TEST_TOOL_NAME += nsconnection_client nsconnection_server
# TEST_TOOL_NAME += nsconnection_client nsconnection_server
TEST_TOOL_NAME += nsconnection_client nsconnection_server $(ADDITIONAL_TOOLS)
# The tool Objective-C source files to be compiled
awake_OBJC_FILES = awake.m
@ -84,6 +86,7 @@ call_OBJC_FILES = call.m
containers_OBJC_FILES = containers.m
diningPhilosophers_OBJC_FILES = diningPhilosophers.m
fref_OBJC_FILES = fref.m
gslock_OBJC_FILES = gslock.m
gstcpport-client_OBJC_FILES = gstcpport-client.m
gstcpport-server_OBJC_FILES = gstcpport-server.m
nsarchiver_OBJC_FILES = nsarchiver.m

227
Testing/gslock.m Normal file
View file

@ -0,0 +1,227 @@
/** gslock - Program to test GSLazyLocks.
Copyright (C) 2003 Free Software Foundation, Inc.
Written by: David Ayers <d.ayers@inode.at>
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 Library 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 Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSValue.h>
#include <Foundation/NSString.h>
#include <Foundation/NSException.h>
#include <Foundation/NSFileHandle.h>
#include <Foundation/NSThread.h>
#include <GNUstepBase/GSLock.h>
NSLock *lock = nil;
NSLock *gLock1 = nil;
GSLazyRecursiveLock *gLock2 = nil;
NSConditionLock *cLock = nil;
volatile int counter = 0;
volatile int threadExitCounter;
void
wait_a_while()
{
volatile int i;
for (i = 0; i < 5; i++)
i = ((i + 1) + (i - 1) / 2);
}
#define NUM_ITERATIONS 10000
@interface Tester : NSObject
- (void)runTest:(NSString *)ident;
- (void)dummy:(id)none;
- (void)createNewLockAt:(id)none;
@end
@implementation Tester
- (void)dummy:(id)none
{
NSLog(@"Multithreaded:%@",[NSThread currentThread]);
}
- (void)runTest:(NSString *)ident
{
NSDate *start;
NSDate *end;
int i,j;
NSTimeInterval time = 0;
NSAutoreleasePool *pool;
BOOL makeMulti;
pool = [[NSAutoreleasePool alloc] init];
makeMulti = ([ident isEqualToString: @"Make Multithreaded GS"]);
for (i = 0; i < 100; i++)
{
start = [NSDate date];
for (j = 0; j < NUM_ITERATIONS; j++)
{
volatile int temp;
[lock lock];
temp = counter;
wait_a_while();
if (makeMulti && i == 49 )
{
[NSThread detachNewThreadSelector: @selector(dummy:)
toTarget: self
withObject: nil];
makeMulti = NO;
}
counter = temp + 1;
wait_a_while();
[lock unlock];
}
end = [NSDate date];
time += [end timeIntervalSinceDate: start];
}
NSLog(@"End (%@/%@/%@):%f ",
[NSThread currentThread], ident, lock, time / 100 );
threadExitCounter++;
[pool release];
}
-(void)createNewLockAt:(id)none
{
[cLock lock];
GS_INITIALIZED_LOCK(gLock1,NSLock);
GS_INITIALIZED_LOCK(gLock2,GSLazyRecursiveLock);
NSLog(@"Created locks: %@ %@", gLock1, gLock2);
[cLock unlockWithCondition: YES];
}
@end
void
test_lazyLocks()
{
Tester *tester;
int i;
tester = [Tester new];
[tester runTest:@"empty"];
lock = [GSLazyLock new];
[tester runTest:@"single GS"];
lock = [GSLazyRecursiveLock new];
[tester runTest:@"single (r) GS"];
lock = [NSLock new];
[tester runTest:@"single NS"];
lock = [NSRecursiveLock new];
[tester runTest:@"single (r) NS"];
lock = [GSLazyLock new];
[tester runTest:@"Make Multithreaded GS"];
/* We are now multithreaded. */
NSCAssert1 ([lock class] == [NSLock class],
@"Class didn't morph:%@", lock);
lock = [GSLazyLock new];
NSCAssert1 ([lock class] == [NSLock class],
@"Returned wrong lock:%@", lock);
/* These tests actually only test NS*Lock locking, but... */
[tester runTest:@"multi simple GS"];
lock = [GSLazyRecursiveLock new];
NSCAssert1 ([lock class] == [NSRecursiveLock class],
@"Returned wrong lock:%@", lock);
[tester runTest:@"multi simple (r) GS"];
lock = [NSLock new];
[tester runTest:@"multi simple NS"];
lock = [NSRecursiveLock new];
[tester runTest:@"multi simple NS"];
/* Let's test locking anyway while we're at it. */
for (threadExitCounter = 0, i = 0; i < 3; i++)
{
NSString *ident;
ident = [NSString stringWithFormat: @"multi complex (%d)", i];
[NSThread detachNewThreadSelector: @selector(runTest:)
toTarget: tester
withObject: ident];
}
while (threadExitCounter < 3)
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 10.0]];
NSCAssert1 (counter == NUM_ITERATIONS * 1300,
@"Locks broken! %d", counter );
}
void
test_newLockAt(void)
{
Tester *t = [Tester new];
cLock = [[NSConditionLock alloc] initWithCondition: NO];
[NSThread detachNewThreadSelector: @selector(createNewLockAt:)
toTarget: t
withObject: nil];
[cLock lockWhenCondition: YES
beforeDate: [NSDate dateWithTimeIntervalSinceNow: 10.0]];
[cLock unlock];
NSCAssert1([gLock1 isKindOfClass: [NSLock class]],
@"-[NSLock newLockAt:] returned %@", gLock1);
NSCAssert1([gLock2 isKindOfClass: [NSRecursiveLock class]],
@"-[GSLazyRecursiveLock newLockAt:] returned %@", gLock1);
}
int
main()
{
NSAutoreleasePool *pool;
[NSAutoreleasePool enableDoubleReleaseCheck:YES];
pool = [[NSAutoreleasePool alloc] init];
test_lazyLocks();
test_newLockAt();
[pool release];
exit(0);
}