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
[email protected]
https://mail.gna.org/listinfo/gnustep-cvs