diff --git a/SQLClient.h b/SQLClient.h index 2a8bf6f..93b3698 100644 --- a/SQLClient.h +++ b/SQLClient.h @@ -1238,7 +1238,11 @@ SQLCLIENT_PRIVATE /** * The default implementation calls NSLogv to log a debug message.
- * Override this in a category to provide more sophisticated logging. + * Override this in a category to provide more sophisticated logging.
+ * Do NOT override with code which can be slow or which calls (directly + * or indirectly) any SQLCLient methods, since this method will be used + * inside locked regions of the SQLClient code and you could cause + * deadlocks or long delays to other threads using the class. */ - (void) debug: (NSString*)fmt, ...; diff --git a/SQLClient.m b/SQLClient.m index e0cd0e9..cc57e56 100644 --- a/SQLClient.m +++ b/SQLClient.m @@ -1403,7 +1403,7 @@ static unsigned int maxConnections = 8; [clientsMapLock lock]; existing = (SQLClient*)NSMapGet(clientsMap, reference); - if (existing == nil) + if (nil == existing) { lock = [NSRecursiveLock new]; // Ensure thread-safety. [self setDebugging: [[self class] debugging]]; @@ -2194,142 +2194,154 @@ static unsigned int maxConnections = 8; */ - (void) _configure: (NSNotification*)n { - NSDictionary *o = [n object]; + NSDictionary *o; NSDictionary *d; NSString *s; Class c; - /* - * get dictionary containing config info for this client by name. - */ - d = [o objectForKey: @"SQLClientReferences"]; - if ([d isKindOfClass: [NSDictionary class]] == NO) + [lock lock]; + NS_DURING { - [self debug: @"Unable to find SQLClientReferences config dictionary"]; - d = nil; - } - d = [d objectForKey: _name]; - if ([d isKindOfClass: [NSDictionary class]] == NO) - { - [self debug: @"Unable to find config for client '%@'", _name]; - d = nil; - } + o = [n object]; + /* + * get dictionary containing config info for this client by name. + */ + d = [o objectForKey: @"SQLClientReferences"]; + if ([d isKindOfClass: [NSDictionary class]] == NO) + { + [self debug: @"Unable to find SQLClientReferences config dictionary"]; + d = nil; + } + d = [d objectForKey: _name]; + if ([d isKindOfClass: [NSDictionary class]] == NO) + { + [self debug: @"Unable to find config for client '%@'", _name]; + d = nil; + } - s = [d objectForKey: @"ServerType"]; - if ([s isKindOfClass: NSStringClass] == NO) - { - s = @"Postgres"; - } + s = [d objectForKey: @"ServerType"]; + if ([s isKindOfClass: NSStringClass] == NO) + { + s = @"Postgres"; + } - c = NSClassFromString([@"SQLClient" stringByAppendingString: s]); - if (c == nil) - { - NSString *path; - NSBundle *bundle; - NSArray *paths; - NSMutableArray *tried; - unsigned count; - - paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, - NSAllDomainsMask, YES); - count = [paths count]; - tried = [NSMutableArray arrayWithCapacity: count]; - while (count-- > 0) - { - path = [paths objectAtIndex: count]; - path = [path stringByAppendingPathComponent: @"Bundles"]; - path = [path stringByAppendingPathComponent: @"SQLClient"]; - path = [path stringByAppendingPathComponent: s]; - path = [path stringByAppendingPathExtension: @"bundle"]; - bundle = [NSBundle bundleWithPath: path]; - if (bundle != nil) - { - [tried addObject: path]; - if ((c = [bundle principalClass]) != nil) - { - break; // Found it. - } - } - /* Try alternative version with more libraries linked in. - * In some systems and situations the dynamic linker needs - * to haved the SQLClient, gnustep-base, and objc libraries - * explicitly linked into the bundle, but in others it - * requires them to not be linked. To handle that, we create - * two versions of each bundle, the seond version has _libs - * appended to the bundle name, and has the extra libraries linked. - */ - path = [path stringByDeletingPathExtension]; - path = [path stringByAppendingString: @"_libs"]; - path = [path stringByAppendingPathExtension: @"bundle"]; - bundle = [NSBundle bundleWithPath: path]; - if (bundle != nil) - { - [tried addObject: path]; - if ((c = [bundle principalClass]) != nil) - { - break; // Found it. - } - } - } + c = NSClassFromString([@"SQLClient" stringByAppendingString: s]); if (c == nil) - { - if ([tried count] == 0) - { - [self debug: @"unable to load bundle for '%@' server type" - @" ... failed to locate bundle in %@", s, paths]; - } - else - { - [self debug: @"unable to load backend class for '%@' server type" - @" ... dynamic library load failed in %@", s, tried]; - } - return; - } - } - if (c != [self class]) - { - [self disconnect]; + { + NSString *path; + NSBundle *bundle; + NSArray *paths; + NSMutableArray *tried; + unsigned count; + + paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, + NSAllDomainsMask, YES); + count = [paths count]; + tried = [NSMutableArray arrayWithCapacity: count]; + while (count-- > 0) + { + path = [paths objectAtIndex: count]; + path = [path stringByAppendingPathComponent: @"Bundles"]; + path = [path stringByAppendingPathComponent: @"SQLClient"]; + path = [path stringByAppendingPathComponent: s]; + path = [path stringByAppendingPathExtension: @"bundle"]; + bundle = [NSBundle bundleWithPath: path]; + if (bundle != nil) + { + [tried addObject: path]; + if ((c = [bundle principalClass]) != nil) + { + break; // Found it. + } + } + /* Try alternative version with more libraries linked in. + * In some systems and situations the dynamic linker needs + * to haved the SQLClient, gnustep-base, and objc libraries + * explicitly linked into the bundle, but in others it + * requires them to not be linked. To handle that, we create + * two versions of each bundle, the seond version has _libs + * appended to the bundle name, and has the extra libraries linked. + */ + path = [path stringByDeletingPathExtension]; + path = [path stringByAppendingString: @"_libs"]; + path = [path stringByAppendingPathExtension: @"bundle"]; + bundle = [NSBundle bundleWithPath: path]; + if (bundle != nil) + { + [tried addObject: path]; + if ((c = [bundle principalClass]) != nil) + { + break; // Found it. + } + } + } + if (c == nil) + { + if ([tried count] == 0) + { + [self debug: @"unable to load bundle for '%@' server type" + @" ... failed to locate bundle in %@", s, paths]; + } + else + { + [self debug: @"unable to load backend class for '%@' server type" + @" ... dynamic library load failed in %@", s, tried]; + } + return; + } + } + if (c != [self class]) + { + [self disconnect]; #ifdef GNUSTEP - GSDebugAllocationRemove(object_getClass(self), self); + GSDebugAllocationRemove(object_getClass(self), self); #endif - object_setClass(self, c); + object_setClass(self, c); #ifdef GNUSTEP - GSDebugAllocationAdd(object_getClass(self), self); + GSDebugAllocationAdd(object_getClass(self), self); #endif - } + } - s = [d objectForKey: @"Database"]; - if ([s isKindOfClass: NSStringClass] == NO) - { - s = [o objectForKey: @"Database"]; + s = [d objectForKey: @"Database"]; if ([s isKindOfClass: NSStringClass] == NO) - { - s = nil; - } - } - [self setDatabase: s]; + { + s = [o objectForKey: @"Database"]; + if ([s isKindOfClass: NSStringClass] == NO) + { + s = nil; + } + } + [self setDatabase: s]; - s = [d objectForKey: @"User"]; - if ([s isKindOfClass: NSStringClass] == NO) - { - s = [o objectForKey: @"User"]; + s = [d objectForKey: @"User"]; if ([s isKindOfClass: NSStringClass] == NO) - { - s = @""; - } - } - [self setUser: s]; + { + s = [o objectForKey: @"User"]; + if ([s isKindOfClass: NSStringClass] == NO) + { + s = @""; + } + } + [self setUser: s]; - s = [d objectForKey: @"Password"]; - if ([s isKindOfClass: NSStringClass] == NO) - { - s = [o objectForKey: @"Password"]; + s = [d objectForKey: @"Password"]; if ([s isKindOfClass: NSStringClass] == NO) - { - s = @""; - } + { + s = [o objectForKey: @"Password"]; + if ([s isKindOfClass: NSStringClass] == NO) + { + s = @""; + } + } + [self setPassword: s]; } - [self setPassword: s]; + NS_HANDLER + { + [lock unlock]; + [localException raise]; + } + NS_ENDHANDLER + [lock unlock]; } /** Internal method to populate the cache with the result of a query.