gnustep-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Gnustep-cvs] Add lazy locking classes


From: David Ayers
Subject: Re: [Gnustep-cvs] Add lazy locking classes
Date: Mon, 27 Oct 2003 23:15:44 +0100
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.5) Gecko/20031007

Richard Frith-Macdonald  wrote:
*
+* Sun Oct 26 14:45:00 2003  Richard Frith-Macdonald <address@hidden>
*+*
*+* * Source/Additions/GSLock.m: New lazy locking classes as discussed
*+* on the developer list.
*+* * Headers/Additions/GNUstepBase/GSLock.h: Declare new classes.
*+* Please try them out and provide feedback.


Hello,

Well my preliminary tests shows they work rather nicely. They use hardly more time than nil receivers yet are much faster as real locks as expected. I've attached a simple test case (maybe something for Testing/).

I was cleaning up some lock handling of GDL2 and pondering about an efficient way to initialize a global lock in a potentially already multi threaded environment, when I recalled that -baseadd doesn't initialize gnustep_global_lock yet either. So I'm proposing the attached patch which

1.) initializes gnustep_global_lock for -baseadd in a thread safe manner
2.) introduces (id)GSInitializeThreadSafe(id *variablePointer, id object)

This function can be used in macros as in the following proposed file. In libraries like AppKit/GSWeb, where the is designated entry point like *ApplicationMain() in which globals could be safely initialized without such a feature. But in support libraries like GDL2, macros like the one below can be used in each code block that doesn't know whether the lock has been initialized:

Source/Additions/GSAdditionsPrivate.h:
----------------------------CODE--------------------------------
#include <GNUstepBase/GSLock.h>
#define GS_GLOBAL_LOCK (gnustep_global_lock ? gnustep_global_lock \
           : GSInitializeThreadSafe(&gnustep_global_lock, \
                        [GSLazyRecursiveLock new]))

GS_EXPORT NSRecursiveLock *gnustep_global_lock;
----------------------------CODE--------------------------------

(the lock was originally declared in the currently obsolete 'Additions/GSCompatibility.h'. I missed that 'gnustep_global_lock' was declared here when moving the other declarations (that I wanted to make public) to 'GSCategories.h'. So it is currently public :-/ (my bad). But instead of moving it back, I'd like to add the new file to avoid any confusion with the GSCompatiblity.m files.)

Cheers,
David

Index: Headers/Additions/GNUstepBase/GSCategories.h
===================================================================
RCS file: 
/cvsroot/gnustep/gnustep/core/base/Headers/Additions/GNUstepBase/GSCategories.h,v
retrieving revision 1.2
diff -u -r1.2 GSCategories.h
--- Headers/Additions/GNUstepBase/GSCategories.h        13 Sep 2003 22:42:50 
-0000      1.2
+++ Headers/Additions/GNUstepBase/GSCategories.h        27 Oct 2003 14:40:27 
-0000
@@ -163,13 +163,6 @@
 
 
 /* ------------------------------------------------------------------------
- * Variables
- */
-
-GS_EXPORT NSRecursiveLock *gnustep_global_lock;
-
-
-/* ------------------------------------------------------------------------
  * Class/Method Extensions
  */
 
Index: Headers/Additions/GNUstepBase/GSLock.h
===================================================================
RCS file: 
/cvsroot/gnustep/gnustep/core/base/Headers/Additions/GNUstepBase/GSLock.h,v
retrieving revision 1.1
diff -u -r1.1 GSLock.h
--- Headers/Additions/GNUstepBase/GSLock.h      26 Oct 2003 13:45:49 -0000      
1.1
+++ Headers/Additions/GNUstepBase/GSLock.h      27 Oct 2003 14:40:27 -0000
@@ -29,6 +29,8 @@
 
 #include       <Foundation/NSLock.h>
 
address@hidden NSNotification;
+
 @interface     GSLazyLock : NSLock
 {
   int  locked;
Index: Headers/Additions/GNUstepBase/GSObjCRuntime.h
===================================================================
RCS file: 
/cvsroot/gnustep/gnustep/core/base/Headers/Additions/GNUstepBase/GSObjCRuntime.h,v
retrieving revision 1.3
diff -u -r1.3 GSObjCRuntime.h
--- Headers/Additions/GNUstepBase/GSObjCRuntime.h       10 Sep 2003 09:29:39 
-0000      1.3
+++ Headers/Additions/GNUstepBase/GSObjCRuntime.h       27 Oct 2003 14:40:28 
-0000
@@ -490,6 +490,29 @@
 GS_EXPORT BOOL
 GSPrintf (FILE *fptr, NSString *format, ...);
 
+
+/**
+ * Initializes the id pointed to by variablePointer
+ * with object in a thread safe manner, unless
+ * it has been previously initialized, and returns the
+ * contents of the variablePointer.
+ * The id is considered unintialized if it contains nil.
+ * If the id has been previously initialized, object
+ * will be released. <br/>
+ * You typically use this function in macros
+ * to initialize global lock variables when you cannot insure 
+ * that it can be initialized in a thread safe environment.
+ * <example>
+ * #define MY_GLOBAL_LOCK &bsol;
+ *           (my_global_lock ? my_global_lock &bsol;
+ *            : GSInitializeThreadSafe (&amp;my_global_lock, &bsol;
+ *                                      [GSLazyRecusiveLock new]))
+ * </example>
+ */
+
+GS_EXPORT id
+GSInitializeThreadSafe(id *variablePointer, id object);
+
 
 
 #ifndef NO_DEPRECATED
Index: Source/Additions/GSCompatibility.m
===================================================================
RCS file: 
/cvsroot/gnustep/gnustep/core/base/Source/Additions/GSCompatibility.m,v
retrieving revision 1.14
diff -u -r1.14 GSCompatibility.m
--- Source/Additions/GSCompatibility.m  13 Sep 2003 22:42:50 -0000      1.14
+++ Source/Additions/GSCompatibility.m  27 Oct 2003 14:40:28 -0000
@@ -27,7 +27,7 @@
 #include "GNUstepBase/GSCategories.h"
 #include "GNUstepBase/GCObject.h"
 
-/* FIXME: Need to initialize this */
+/* Initialized by GS_GLOBAL_LOCK/GSInitializeThreadSafe().  */
 NSRecursiveLock *gnustep_global_lock = NULL;
 
 NSString *GetEncodingName(NSStringEncoding availableEncodingValue)
Index: Source/Additions/GSObjCRuntime.m
===================================================================
RCS file: /cvsroot/gnustep/gnustep/core/base/Source/Additions/GSObjCRuntime.m,v
retrieving revision 1.28
diff -u -r1.28 GSObjCRuntime.m
--- Source/Additions/GSObjCRuntime.m    13 Sep 2003 22:42:50 -0000      1.28
+++ Source/Additions/GSObjCRuntime.m    27 Oct 2003 14:40:28 -0000
@@ -49,6 +49,8 @@
 #include "GNUstepBase/GSObjCRuntime.h"
 #include "GNUstepBase/GNUstep.h"
 #include "GNUstepBase/GSCategories.h"
+#include "GNUstepBase/GSLock.h"
+#include "GSAdditionsPrivate.h"
 #include <string.h>
 
 @class NSNull;
@@ -1563,7 +1565,7 @@
 
   if (stringClass == 0)
     {
-      [gnustep_global_lock lock];
+      [GS_GLOBAL_LOCK lock];
       if (stringClass == 0)
         {
           stringClass = [NSString class];
@@ -1593,4 +1595,52 @@
     }
   RELEASE(arp);
   return ok;
+}
+
+/* This class only exists to provide a thread safe mechanism 
+   to initialize gnustep_global_lock, as +initialize is called
+   under a lock in ObjC runtimes.  It is a sublcass of NSObject
+   to insure that for NSObject has already executed
+   its +initialize method, which would have initialized gnustep_global_lock
+   for gnustep-base.  */
address@hidden _GSLockInitializer : NSObject
address@hidden
address@hidden _GSLockInitializer
++ (void)initialize
+{
+  if (!gnustep_global_lock)
+    {
+      gnustep_global_lock = [GSLazyRecursiveLock new];
+    }
+}
address@hidden
+
+id
+GSInitializeThreadSafe(id *variablePointer, id object)
+{
+  if (!*variablePointer)
+    {
+      /* Here we do not use GS_GLOBAL_LOCK to prevent a recursion.  */
+      if (!gnustep_global_lock)
+       {
+         [_GSLockInitializer class];
+       }
+
+      [gnustep_global_lock lock];
+      if (!*variablePointer)
+       {
+         *variablePointer = object;
+       }
+      else
+       {
+         [object release];
+       }
+      [gnustep_global_lock unlock];
+    }
+  else
+    {
+      [object release];
+    }
+
+  return *variablePointer;
 }
Index: Source/Additions/Unicode.m
===================================================================
RCS file: /cvsroot/gnustep/gnustep/core/base/Source/Additions/Unicode.m,v
retrieving revision 1.37
diff -u -r1.37 Unicode.m
--- Source/Additions/Unicode.m  18 Oct 2003 06:10:41 -0000      1.37
+++ Source/Additions/Unicode.m  27 Oct 2003 14:40:28 -0000
@@ -41,6 +41,7 @@
 #endif
 #include "GNUstepBase/GSCategories.h"
 #include "GNUstepBase/Unicode.h"
+#include "GSAdditionsPrivate.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -207,7 +208,7 @@
 {
   if (encodingTable == 0)
     {
-      [gnustep_global_lock lock];
+      [GS_GLOBAL_LOCK lock];
       if (encodingTable == 0)
        {
          static struct _strenc_        **encTable = 0;
@@ -325,7 +326,7 @@
 {
   if (_availableEncodings == 0)
     {
-      [gnustep_global_lock lock];
+      [GS_GLOBAL_LOCK lock];
       if (_availableEncodings == 0)
        {
          NSStringEncoding      *encodings;
@@ -559,7 +560,7 @@
       char             *encoding;
       unsigned int     count;
 
-      [gnustep_global_lock lock];
+      [GS_GLOBAL_LOCK lock];
       if (defEnc != GSUndefinedEncoding)
        {
          [gnustep_global_lock unlock];
/** gslock - Program to test GSLazyLocks.
   Copyright (C) 2003 Free Software Foundation, Inc.

   Written by:  David Ayers  <address@hidden>

   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;

volatile int counter = 0;
volatile int threadExitCounter;

wait_a_while()
{
  volatile int i;
  for (i = 0; i < 5; i++)
      i = --i + 1;
}

#define NUM_ITERATIONS 10000

@interface Tester : NSObject
- (void)runTest:(NSString *)ident;
- (void)dummy:(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];
}
@end

int
main()
{
  NSAutoreleasePool *pool;
  Tester *tester;
  int i;

  [NSAutoreleasePool enableDoubleReleaseCheck:YES];
  pool = [[NSAutoreleasePool alloc] init];
  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 );
  
  [pool release];

  exit(0);
}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]