Author: rfm
Date: Thu Jun 19 23:26:25 2014
New Revision: 37950

URL: http://svn.gna.org/viewcvs/gnustep?rev=37950&view=rev
Log:
initial thread pool implementation

Modified:
    libs/sqlclient/trunk/GNUmakefile
    libs/sqlclient/trunk/SQLClient.h
    libs/sqlclient/trunk/SQLClient.m
    libs/sqlclient/trunk/testPostgres.m

Modified: libs/sqlclient/trunk/GNUmakefile
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/sqlclient/trunk/GNUmakefile?rev=37950&r1=37949&r2=37950&view=diff
==============================================================================
--- libs/sqlclient/trunk/GNUmakefile    (original)
+++ libs/sqlclient/trunk/GNUmakefile    Thu Jun 19 23:26:25 2014
@@ -36,7 +36,7 @@
 
 SQLClient_INTERFACE_VERSION=1.7
 
-SQLClient_OBJC_FILES = SQLClient.m
+SQLClient_OBJC_FILES = SQLClient.m SQLClientPool.m
 SQLClient_LIBRARIES_DEPEND_UPON = -lPerformance
 SQLClient_HEADER_FILES = SQLClient.h
 SQLClient_AGSDOC_FILES = SQLClient.h

Modified: libs/sqlclient/trunk/SQLClient.h
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/sqlclient/trunk/SQLClient.h?rev=37950&r1=37949&r2=37950&view=diff
==============================================================================
--- libs/sqlclient/trunk/SQLClient.h    (original)
+++ libs/sqlclient/trunk/SQLClient.h    Thu Jun 19 23:26:25 2014
@@ -375,6 +375,7 @@
    * This should only be modified by the -setShouldTrim: method.
    */
   BOOL                  _shouldTrim;    /** Should whitespace be trimmed? */
+  BOOL                  _forUseInPool;  /** Should be used in a pool only */
   NSString             *_name;         /** Unique identifier for instance */
   NSString             *_client;       /** Identifier within backend */
   NSString             *_database;     /** The configured database name/host */
@@ -623,6 +624,13 @@
 - (id) initWithConfiguration: (NSDictionary*)config;
 
 /**
+ * Calls -initWithConfiguration:name:pool: passing NO to say the client is
+ * not in a pool.
+ */
+- (id) initWithConfiguration: (NSDictionary*)config
+                       name: (NSString*)reference;
+
+/**
  * Initialise using the supplied configuration, or if that is nil, try to
  * use values from NSUserDefaults (and automatically update when the
  * defaults change).<br />
@@ -630,10 +638,10 @@
  * a nil name is supplied, defaults to the value of SQLClientName in the
  * configuration dictionary (or in the standard user defaults).  If there is
  * no value for SQLClientName, uses the string 'Database'.<br />
- * If a SQLClient instance already exists with the name used for this
- * instance, the receiver is deallocated and the existing instance is
- * retained and returned ... there may only ever be one instance for a
- * particular reference name.<br />
+ * If forUseInPool is NO and a SQLClient instance already exists with the
+ * name used for this instance, the receiver is deallocated and the existing
+ * instance is retained and returned ... there may only ever be one instance
+ * for a particular reference name which is not in a pool.<br />
  * <br />
  * The config argument (or the SQLClientReferences user default)
  * is a dictionary with names as keys and dictionaries
@@ -653,7 +661,8 @@
  * connect to a database on a different host over the network.
  */
 - (id) initWithConfiguration: (NSDictionary*)config
-                       name: (NSString*)reference;
+                       name: (NSString*)reference
+                        pool: (BOOL)forUseInPool;
 
 /** Two clients are considered equal if they refer to the same database
  * and are logged in as the same database user using the same protocol.
@@ -1367,11 +1376,58 @@
  * asynchronous query to update them will be run on the cache thread.<br />
  * The rule is that, if the item's age is more than twice its nominal
  * lifetime, it will be retrieved immediately, otherwise it will be
- * retrieved asynchrnonously.<br />
+ * retrieved asynchronously.<br />
  * Currently this may only be the main thread or nil.  Any attempt to set
  * another thread will use the main thread instead.
  */
 - (void) setCacheThread: (NSThread*)aThread;
+@end
+
+/** <p>An SQLClientPool instance may be used to create/control a pool of
+ * client objects.  Code may obtain autoreleased proxies to the clients
+ * from the pool and use them safe in the knowledge that they won't be
+ * used anywhere else ... as soon as the proxy is deallocated the client
+ * is returned to the pool.
+ * </p>
+ * <p>All clients in the pool share the same cache object, so query results
+ * cached by one client will be available to other clients in the pool.
+ * </p>
+ */
+@interface     SQLClientPool : NSObject
+{
+  NSConditionLock       *lock;  /** Controls access to the pool contents */
+  SQLClient             **c;    /** The clients of the pool. */
+  SQLClient             **p;    /** The proxies of the pool. */
+  int                   max;    /** Maximum connection count */
+  int                   min;    /** Minimum connection count */
+}
+
+/**
+ * Calls -initWithConfiguration:name:pool: passing NO to say the client is
+ * not in a pool.
+ */
+- (id) initWithConfiguration: (NSDictionary*)config
+                       name: (NSString*)reference
+                         max: (int)maxConnections
+                         min: (int)minConnections;
+
+/** Fetches an (autoreleased) proxy to a client from the pool.
+ */
+- (SQLClient*) provideClient;
+
+/**
+ * Sets the cache for all the clients in the pool.
+ */
+- (void) setCache: (GSCache*)aCache;
+
+/** Takes the client form the provided proxy and places it back
+ * in the queue (so the proxy stops using it).  This happens automatically
+ * when the proxy is deallocated so you don't generally needs to do it.
+ * Returns YES if the supplied proxy referred to a client in the pool,
+ * NO otherwise.
+ */
+- (BOOL) swallowClient: (SQLClient*)proxy;
+
 @end
 
 /**

Modified: libs/sqlclient/trunk/SQLClient.m
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/sqlclient/trunk/SQLClient.m?rev=37950&r1=37949&r2=37950&view=diff
==============================================================================
--- libs/sqlclient/trunk/SQLClient.m    (original)
+++ libs/sqlclient/trunk/SQLClient.m    Thu Jun 19 23:26:25 2014
@@ -36,9 +36,9 @@
 #import        <Foundation/NSDictionary.h>
 #import        <Foundation/NSEnumerator.h>
 #import        <Foundation/NSException.h>
-#import        <Foundation/NSHashTable.h>
 #import        <Foundation/NSKeyValueCoding.h>
 #import        <Foundation/NSLock.h>
+#import        <Foundation/NSHashTable.h>
 #import        <Foundation/NSMapTable.h>
 #import        <Foundation/NSNotification.h>
 #import        <Foundation/NSNull.h>
@@ -104,237 +104,6 @@
   [super dealloc];
 }
 @end
-
-@interface     SQLClientPool : NSObject
-{
-  unsigned     pool;
-  NSString     *name;
-  NSString     *serv;
-  NSString     *user;
-  NSString     *pass;
-  NSString     *path;
-  NSHashTable  *idle;
-  NSHashTable  *used;
-}
-- (BOOL) isSingle;
-- (BOOL) makeIdle: (SQLClient*)c;
-- (BOOL) makeUsed: (SQLClient*)c;
-- (void) setConfiguration: (NSDictionary*)o;
-@end
-
-@implementation        SQLClientPool
-- (void) dealloc
-{
-  if (idle != 0)
-    {
-      NSFreeHashTable(idle);
-      idle = 0;
-    }
-  if (used != 0)
-    {
-      NSFreeHashTable(used);
-      used = 0;
-    }
-  [name release]; name = nil;
-  [serv release]; serv = nil;
-  [user release]; user = nil;
-  [pass release]; pass = nil;
-  [path release]; path = nil;
-  [super dealloc];
-}
-
-- (id) initWithConfiguration: (NSDictionary*)config
-                       name: (NSString*)reference
-{
-  name = [reference copy];
-  idle = NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 16);
-  used = NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 16);
-  [self setConfiguration: config];
-  return self;
-}
-
-- (BOOL) isSingle
-{
-  if (pool == 1)
-    {
-      return YES;
-    }
-  return NO;
-}
-
-- (BOOL) makeIdle: (SQLClient*)c
-{
-  if (NSHashGet(idle, (void*)c) == (void*)c)
-    {
-      return YES;                              // Already idle
-    }
-  if (NSHashGet(used, (void*)c) == (void*)c)
-    {
-      NSHashRemove(used, (void*)c);
-    }
-  if (NSCountHashTable(idle) + NSCountHashTable(used) < pool)
-    {
-      NSHashInsert(idle, (void*)c);
-      return YES;
-    }
-  return NO;
-}
-
-- (BOOL) makeUsed: (SQLClient*)c
-{
-  if (NSHashGet(used, (void*)c) == (void*)c)
-    {
-      return YES;                              // Already used
-    }
-  if (NSHashGet(idle, (void*)c) == (void*)c)
-    {
-      NSHashRemove(idle, (void*)c);
-    }
-  if (NSCountHashTable(idle) + NSCountHashTable(used) < pool)
-    {
-      NSHashInsert(used, (void*)c);
-      return YES;
-    }
-  return NO;
-}
-
-- (void) setConfiguration: (NSDictionary*)o
-{
-  NSDictionary *d;
-  NSString     *s;
-  BOOL         change = NO;
-  int          capacity;
-
-  /*
-   * get dictionary containing config info for this client by name.
-   */
-  d = [o objectForKey: @"SQLClientReferences"];
-  if ([d isKindOfClass: [NSDictionary class]] == NO)
-    {
-      d = nil;
-    }
-  d = [d objectForKey: name];
-  if ([d isKindOfClass: [NSDictionary class]] == NO)
-    {
-      d = nil;
-    }
-
-  s = [d objectForKey: @"ServerType"];
-  if ([s isKindOfClass: NSStringClass] == NO)
-    {
-      s = @"Postgres";
-    }
-  if (s != serv && [s isEqual: serv] == NO)
-    {
-      s = [s copy];
-      [serv release];
-      serv = s;
-      change = YES;
-    }
-
-  s = [d objectForKey: @"Database"];
-  if ([s isKindOfClass: NSStringClass] == NO)
-    {
-      s = [o objectForKey: @"Database"];
-      if ([s isKindOfClass: NSStringClass] == NO)
-       {
-         s = nil;
-       }
-    }
-  if (s != path && [s isEqual: path] == NO)
-    {
-      s = [s copy];
-      [path release];
-      path = s;
-      change = YES;
-    }
-
-  s = [d objectForKey: @"User"];
-  if ([s isKindOfClass: NSStringClass] == NO)
-    {
-      s = [o objectForKey: @"User"];
-      if ([s isKindOfClass: NSStringClass] == NO)
-       {
-         s = @"";
-       }
-    }
-  if (s != user && [s isEqual: user] == NO)
-    {
-      s = [s copy];
-      [user release];
-      user = s;
-      change = YES;
-    }
-
-  s = [d objectForKey: @"Password"];
-  if ([s isKindOfClass: NSStringClass] == NO)
-    {
-      s = [o objectForKey: @"Password"];
-      if ([s isKindOfClass: NSStringClass] == NO)
-       {
-         s = @"";
-       }
-    }
-  if (s != pass && [s isEqual: pass] == NO)
-    {
-      s = [s copy];
-      [pass release];
-      pass = s;
-      change = YES;
-    }
-
-  s = [d objectForKey: @"Password"];
-  if ([s isKindOfClass: NSStringClass] == NO)
-    {
-      s = @"1";
-    }
-  capacity = [s intValue];
-  if (capacity < 1) capacity = 1;
-  if (capacity > 100) capacity = 100;
-
-  if (change == YES)
-    {
-      NSResetHashTable(idle);
-      NSResetHashTable(used);
-    }
-  if (pool > capacity)
-    {
-      unsigned ic = NSCountHashTable(idle);
-      unsigned uc = NSCountHashTable(used);
-
-      if (ic + uc > capacity)
-        {
-         NSHashEnumerator      e = NSEnumerateHashTable(idle);
-         void                  *c;
-
-         while (ic + uc > capacity
-           && (c = NSNextHashEnumeratorItem(&e)) != nil)
-           {
-             NSHashRemove(idle, c);
-             ic--;
-           }
-         NSEndHashTableEnumeration(&e);
-         if (uc > capacity)
-           {
-             NSHashEnumerator  e = NSEnumerateHashTable(used);
-             void              *c;
-
-             while (uc > capacity
-               && (c = NSNextHashEnumeratorItem(&e)) != nil)
-               {
-                 NSHashRemove(used, c);
-                 uc--;
-               }
-             NSEndHashTableEnumeration(&e);
-           }
-        }
-    }
-  pool = capacity;
-}
-
-@end
-
-
 
 static Class aClass = 0;
 static Class rClass = 0;
@@ -866,8 +635,9 @@
 /**
  * Container for all instances.
  */
+static NSHashTable     *clientsHash = 0;
 static NSMapTable      *clientsMap = 0;
-static NSRecursiveLock *clientsMapLock = nil;
+static NSRecursiveLock *clientsLock = nil;
 static NSString                *beginString = @"begin";
 static NSArray         *beginStatement = nil;
 static NSString                *commitString = @"commit";
@@ -898,11 +668,19 @@
 
 + (NSArray*) allClients
 {
-  NSArray      *a;
-
-  [clientsMapLock lock];
-  a = NSAllMapTableValues(clientsMap);
-  [clientsMapLock unlock];
+  NSMutableArray       *a;
+  NSHashEnumerator     e;
+  id                    o;
+
+  [clientsLock lock];
+  a = [NSMutableArray arrayWithCapacity: NSCountHashTable(clientsHash)];
+  e = NSEnumerateHashTable(clientsHash);
+  while (nil != (o = (id)NSNextHashEnumeratorItem(&e)))
+    {
+      [a addObject: o];
+    }
+  NSEndHashTableEnumeration(&e);
+  [clientsLock unlock];
   return a;
 }
 
@@ -951,10 +729,10 @@
        }
     }
 
-  [clientsMapLock lock];
+  [clientsLock lock];
   existing = (SQLClient*)NSMapGet(clientsMap, reference);
   [[existing retain] autorelease];
-  [clientsMapLock unlock];
+  [clientsLock unlock];
   return existing;
 }
 
@@ -966,11 +744,12 @@
   queryModes = [[NSArray alloc] initWithObjects: modes count: 1];
   GSTickerTimeNow();
   [SQLRecord class];   // Force initialisation
-  if (clientsMap == 0)
-    {
+  if (0 == clientsHash)
+    {
+      clientsHash = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 0);
       clientsMap = NSCreateMapTable(NSObjectMapKeyCallBacks,
         NSNonRetainedObjectMapValueCallBacks, 0);
-      clientsMapLock = [NSRecursiveLock new];
+      clientsLock = [NSRecursiveLock new];
       beginStatement = [[NSArray arrayWithObject: beginString] retain];
       commitStatement = [[NSArray arrayWithObject: commitString] retain];
       rollbackStatement = [[NSArray arrayWithObject: rollbackString] retain];
@@ -992,15 +771,14 @@
 
 + (void) purgeConnections: (NSDate*)since
 {
-  NSMapEnumerator      e;
-  NSString             *n;
+  NSHashEnumerator     e;
   SQLClient            *o;
   unsigned int         connectionCount = 0;
   NSTimeInterval       t = [since timeIntervalSinceReferenceDate];
 
-  [clientsMapLock lock];
-  e = NSEnumerateMapTable(clientsMap);
-  while (NSNextMapEnumeratorPair(&e, (void**)&n, (void**)&o) != 0)
+  [clientsLock lock];
+  e = NSEnumerateHashTable(clientsHash);
+  while (nil != (o = (SQLClient*)NSNextHashEnumeratorItem(&e)))
     {
       if (since != nil)
        {
@@ -1016,8 +794,8 @@
          connectionCount++;
        }
     }
-  NSEndMapTableEnumeration(&e);
-  [clientsMapLock unlock];
+  NSEndHashTableEnumeration(&e);
+  [clientsLock unlock];
 
   while (connectionCount >= maxConnections)
     {
@@ -1025,9 +803,9 @@
       NSTimeInterval   oldest = 0.0;
   
       connectionCount = 0;
-      [clientsMapLock lock];
-      e = NSEnumerateMapTable(clientsMap);
-      while (NSNextMapEnumeratorPair(&e, (void**)&n, (void**)&o))
+      [clientsLock lock];
+      e = NSEnumerateHashTable(clientsHash);
+      while (nil != (o = (SQLClient*)NSNextHashEnumeratorItem(&e)))
        {
          if ([o connected] == YES)
            {
@@ -1041,8 +819,8 @@
                }
            }
        }
-      NSEndMapTableEnumeration(&e);
-      [clientsMapLock unlock];
+      NSEndHashTableEnumeration(&e);
+      [clientsLock unlock];
       connectionCount--;
       if ([other debugging] > 0)
        {
@@ -1233,12 +1011,13 @@
 {
   NSNotificationCenter *nc;
 
-  if (_name != nil)
-    {
-      [clientsMapLock lock];
+  [clientsLock lock];
+  NSHashRemove(clientsHash, (void*)self);
+  if (_name != nil && NO == _forUseInPool)
+    {
       NSMapRemove(clientsMap, (void*)_name);
-      [clientsMapLock unlock];
-    }
+    }
+  [clientsLock unlock];
   nc = [NSNotificationCenter defaultCenter];
   [nc removeObserver: self];
   [self disconnect];
@@ -1382,6 +1161,14 @@
 - (id) initWithConfiguration: (NSDictionary*)config
                        name: (NSString*)reference
 {
+  return [self initWithConfiguration: config name: reference pool: NO];
+}
+
+
+- (id) initWithConfiguration: (NSDictionary*)config
+                       name: (NSString*)reference
+                        pool: (BOOL)forUseInPool
+{
   NSNotification       *n;
   NSDictionary         *conf = config;
   id                   existing;
@@ -1401,8 +1188,16 @@
        }
     }
 
-  [clientsMapLock lock];
-  existing = (SQLClient*)NSMapGet(clientsMap, reference);
+  [clientsLock lock];
+  _forUseInPool = (NO == forUseInPool) ? NO : YES;
+  if (YES == _forUseInPool)
+    {
+      existing = (SQLClient*)NSMapGet(clientsMap, reference);
+    }
+  else
+    {
+      existing = nil;
+    }
   if (nil == existing)
     {
       lock = [NSRecursiveLock new];    // Ensure thread-safety.
@@ -1426,6 +1221,7 @@
        object: conf
        userInfo: nil];
 
+      NSHashInsert(clientsHash, (void*)self);
       [self _configure: n];    // Actually set up the configuration.
     }
   else
@@ -1433,7 +1229,7 @@
       [self release];
       self = [existing retain];
     }
-  [clientsMapLock unlock];
+  [clientsLock unlock];
 
   return self;
 }
@@ -1733,12 +1529,12 @@
    * it from the table so that no other thread will find it
    * and try to use it while it is being deallocated.
    */
-  [clientsMapLock lock];
+  [clientsLock lock];
   if (NSDecrementExtraRefCountWasZero(self))
     {
       [self dealloc];
     }
-  [clientsMapLock unlock];
+  [clientsLock unlock];
 }
 
 - (void) rollback
@@ -1803,24 +1599,27 @@
     {
       if ([s isEqual: _name] == NO)
         {
-          [clientsMapLock lock];
-          if (NSMapGet(clientsMap, s) != 0)
+          [clientsLock lock];
+          if (NO == _forUseInPool)
             {
-              [clientsMapLock unlock];
-              [lock unlock];
-              if ([self debugging] > 0)
+              if (NSMapGet(clientsMap, s) != 0)
                 {
-                  [self debug: @"Error attempt to re-use client name %@", s];
+                  [clientsLock unlock];
+                  [lock unlock];
+                  if ([self debugging] > 0)
+                    {
+                      [self
+                        debug: @"Error attempt to re-use client name %@", s];
+                    }
+                  NS_VOIDRETURN;
                 }
-              NS_VOIDRETURN;
             }
           if (connected == YES)
             {
               [self disconnect];
             }
-          if (_name != nil)
+          if (NO == _forUseInPool && _name != nil)
             {
-              [[self retain] autorelease];
               NSMapRemove(clientsMap, (void*)_name);
             }
           s = [s copy];
@@ -1828,8 +1627,11 @@
           _name = s;
           [_client release];
           _client = [[[NSProcessInfo processInfo] globallyUniqueString] 
retain];
-          NSMapInsert(clientsMap, (void*)_name, (void*)self);
-          [clientsMapLock unlock];
+          if (NO == _forUseInPool && _name != nil)
+            {
+              NSMapInsert(clientsMap, (void*)_name, (void*)self);
+            }
+          [clientsLock unlock];
         }
     }
   NS_HANDLER

Modified: libs/sqlclient/trunk/testPostgres.m
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/sqlclient/trunk/testPostgres.m?rev=37950&r1=37949&r2=37950&view=diff
==============================================================================
--- libs/sqlclient/trunk/testPostgres.m (original)
+++ libs/sqlclient/trunk/testPostgres.m Thu Jun 19 23:26:25 2014
@@ -42,6 +42,7 @@
 main()
 {
   NSAutoreleasePool    *pool = [NSAutoreleasePool new];
+  SQLClientPool         *sp;
   SQLClient            *db;
   NSUserDefaults       *defs;
   NSMutableArray       *records;
@@ -68,7 +69,11 @@
       nil]
     ];
 
-  db = [SQLClient clientWithConfiguration: nil name: @"test"];
+  sp = [[SQLClientPool alloc] initWithConfiguration: nil
+                                               name: @"test"
+                                                max: 2
+                                                min: 1];
+  db = [[sp autorelease] provideClient];
 
   l = [Logger new];
   [[NSNotificationCenter defaultCenter] addObserver: l


_______________________________________________
Gnustep-cvs mailing list
Gnustep-cvs@gna.org
https://mail.gna.org/listinfo/gnustep-cvs

Reply via email to