Commits

Tuukka Norri committed e819f7e

Thread safety
- Delegates are now set to nil in -dealloc for additional safety.
- Changed various finalizers so that Objective-C objects other than self won't receive any messages.
- Wrote a class for locking in methods like -invalidate. It encapsulates a POSIX read-write lock, which is acquired exclusively when the owning object will be invalidated. The invalidating thread can then block until callbacks in other threads have finished.
- BXConnectionMonitor now retains connections until they have disconnected.
- Fixed a problem which caused an NSMachPort to be leaked in BXConnectionMonitor.

  • Participants
  • Parent commits de3d0f1

Comments (0)

Files changed (26)

BaseTen.xcodeproj/project.pbxproj

 		5390DE590E32B788003980BE /* BXPGCurrentCompatVersion.m.m4 in Sources */ = {isa = PBXBuildFile; fileRef = 5390DE580E32B788003980BE /* BXPGCurrentCompatVersion.m.m4 */; };
 		53915F9E0E3FD22D0098B419 /* libpq_additions.h in Headers */ = {isa = PBXBuildFile; fileRef = 53915F9C0E3FD22D0098B419 /* libpq_additions.h */; };
 		53915F9F0E3FD22D0098B419 /* libpq_additions.c in Sources */ = {isa = PBXBuildFile; fileRef = 53915F9D0E3FD22D0098B419 /* libpq_additions.c */; };
+		5392034211AEA108000E2BEC /* BXValidationLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 5392034011AEA108000E2BEC /* BXValidationLock.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		5392034311AEA108000E2BEC /* BXValidationLock.m in Sources */ = {isa = PBXBuildFile; fileRef = 5392034111AEA108000E2BEC /* BXValidationLock.m */; };
+		5392038611AEA8BF000E2BEC /* BXSocketDescriptorPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 5392038511AEA8BF000E2BEC /* BXSocketDescriptorPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		53937B670EFD097700510DBD /* PGTSInvocationRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 53937B650EFD097700510DBD /* PGTSInvocationRecorder.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		53937B680EFD097700510DBD /* PGTSInvocationRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 53937B660EFD097700510DBD /* PGTSInvocationRecorder.m */; };
 		5395E7260E2F77DF006E8441 /* BXPGEntityImporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 5395E7240E2F77DF006E8441 /* BXPGEntityImporter.h */; settings = {ATTRIBUTES = (Private, ); }; };
 			isa = PBXContainerItemProxy;
 			containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
 			proxyType = 1;
-			remoteGlobalIDString = 537EB5EB11AD8448008FA78E /* PCRE */;
+			remoteGlobalIDString = 537EB5EB11AD8448008FA78E;
 			remoteInfo = PCRE;
 		};
 /* End PBXContainerItemProxy section */
 		5390DE580E32B788003980BE /* BXPGCurrentCompatVersion.m.m4 */ = {isa = PBXFileReference; explicitFileType = sourcecode; fileEncoding = 4; name = BXPGCurrentCompatVersion.m.m4; path = Sources/BXPGCurrentCompatVersion.m.m4; sourceTree = "<group>"; };
 		53915F9C0E3FD22D0098B419 /* libpq_additions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libpq_additions.h; path = Sources/libpq_additions.h; sourceTree = "<group>"; };
 		53915F9D0E3FD22D0098B419 /* libpq_additions.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = libpq_additions.c; path = Sources/libpq_additions.c; sourceTree = "<group>"; };
+		5392034011AEA108000E2BEC /* BXValidationLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BXValidationLock.h; path = Sources/BXValidationLock.h; sourceTree = "<group>"; };
+		5392034111AEA108000E2BEC /* BXValidationLock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BXValidationLock.m; path = Sources/BXValidationLock.m; sourceTree = "<group>"; };
+		5392038511AEA8BF000E2BEC /* BXSocketDescriptorPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BXSocketDescriptorPrivate.h; path = Sources/BXSocketDescriptorPrivate.h; sourceTree = "<group>"; };
 		53933EF409EBD082001D00BB /* BXInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = BXInterface.h; path = Sources/BXInterface.h; sourceTree = "<group>"; };
 		53937B650EFD097700510DBD /* PGTSInvocationRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PGTSInvocationRecorder.h; path = Sources/PGTSInvocationRecorder.h; sourceTree = "<group>"; };
 		53937B660EFD097700510DBD /* PGTSInvocationRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PGTSInvocationRecorder.m; path = Sources/PGTSInvocationRecorder.m; sourceTree = "<group>"; };
 				5339AB920A7542EC00994B1A /* BXException.m */,
 				5329E7F7119F4878000EC6ED /* BXRegularExpressions.h */,
 				5329E7F5119F4845000EC6ED /* BXRegularExpressions.m */,
+				5392034011AEA108000E2BEC /* BXValidationLock.h */,
+				5392034111AEA108000E2BEC /* BXValidationLock.m */,
 				53937B650EFD097700510DBD /* PGTSInvocationRecorder.h */,
 				53937B660EFD097700510DBD /* PGTSInvocationRecorder.m */,
 				5350CC360EFBE66500EBC328 /* PGTSCollections.h */,
 			isa = PBXGroup;
 			children = (
 				538E031A11A4A5F700C3447F /* BXSocketDescriptor.h */,
+				5392038511AEA8BF000E2BEC /* BXSocketDescriptorPrivate.h */,
 				538E031B11A4A5F700C3447F /* BXSocketDescriptor.m */,
 				538E031E11A4A61F00C3447F /* BXRunLoopSocketDesciptor.h */,
 				538E031F11A4A61F00C3447F /* BXRunLoopSocketDesciptor.m */,
 				53776BA711A75DB3009F1A8E /* BXSystemEventNotifier.h in Headers */,
 				534E3C7D11A77088002D49C8 /* BXSocketReachabilityObserver.h in Headers */,
 				5334324511A881F200B71D24 /* BXConnectionMonitor.h in Headers */,
+				5392034211AEA108000E2BEC /* BXValidationLock.h in Headers */,
+				5392038611AEA8BF000E2BEC /* BXSocketDescriptorPrivate.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 				53776BA811A75DB3009F1A8E /* BXSystemEventNotifier.m in Sources */,
 				534E3C7E11A77088002D49C8 /* BXSocketReachabilityObserver.m in Sources */,
 				5334324611A881F200B71D24 /* BXConnectionMonitor.m in Sources */,
+				5392034311AEA108000E2BEC /* BXValidationLock.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

BaseTenAppKit/Sources/BXAppKitSystemEventNotifier.m

 //
 
 #import "BXAppKitSystemEventNotifier.h"
+#import <BaseTen/BXValidationLock.h>
 #import <AppKit/AppKit.h>
 
 
 
 - (void) dealloc
 {
+	[self invalidate];
+	
 	[[NSNotificationCenter defaultCenter] removeObserver: self];
 	[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver: self];
 	

Sources/BXConnectionMonitor.h

 
 @interface BXConnectionMonitor : NSObject 
 {
-	CFMutableDictionaryRef mConnections;
+	NSMapTable *mConnections;
 	BXSystemEventNotifier *mSystemEventNotifier;
 	BXConnectionMonitorThread *mMonitorThread;
 }

Sources/BXConnectionMonitor.m

 
 
 static NSArray*
-DictionaryKeys (CFDictionaryRef dict)
+DictionaryKeys (NSMapTable *dict)
 {
 	NSArray *retval = nil;
-	@synchronized ((id) dict)
+	@synchronized (dict)
 	{
-		retval = [(id) dict allKeys];
+		retval = [[dict keyEnumerator] allObjects];
 	}
 	return retval;
 }
 {
 	mRunLoop = CFRunLoopGetCurrent ();
 	
-	NSPort *port = [NSPort port];
+	NSPort *port = [[NSPort alloc] init];
 	[port scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
 	
 	while (1)
 			break;
 	}
 	
+	[port release];
 	mRunLoop = NULL;
 }
 
 
+- (void) finalize
+{
+	if (mRunLoop)
+		CFRunLoopStop (mRunLoop);
+	
+	[super finalize];
+}
+
+
 - (CFRunLoopRef) runLoop
 {
 	return mRunLoop;
 				[nc addObserver: self selector: callbacks [i] name: notifications [i] object: mSystemEventNotifier];
 		}
 		
-		mConnections = CFDictionaryCreateMutable (kCFAllocatorDefault, 0, 
-												  &kCFTypeDictionaryKeyCallBacks,
-												  &kCFTypeDictionaryValueCallBacks);
+		mConnections = [[NSMapTable mapTableWithStrongToStrongObjects] retain];
 		mMonitorThread = [[BXConnectionMonitorThread alloc] init];
 		[mMonitorThread setName: @"org.basetenframework.ConnectionMonitorThread"];
 		[mMonitorThread start];
 - (void) dealloc
 {
 	[[NSNotificationCenter defaultCenter] removeObserver: self];
+	[mSystemEventNotifier invalidate];
 	[mSystemEventNotifier release];
 	
 	CFRunLoopRef runLoop = [mMonitorThread runLoop];
 		CFRunLoopStop ([mMonitorThread runLoop]);
 	
 	[mMonitorThread release];
-	
-	if (mConnections)
-		CFRelease (mConnections);
-	
+	[mConnections release];
 	[super dealloc];
 }
 
 
-- (void) finalize
-{
-	CFRunLoopRef runLoop = [mMonitorThread runLoop];
-	if (runLoop)
-		CFRunLoopStop ([mMonitorThread runLoop]);
-	
-	[mSystemEventNotifier invalidate];
-	
-	if (mConnections)
-		CFRelease (mConnections);
-
-	[super finalize];
-}
-
-
 - (void) clientDidStartConnectionAttempt: (id <BXConnectionMonitorClient>) connection
 {
-	@synchronized ((id) mConnections)
+	@synchronized (mConnections)
 	{
-		CFDictionarySetValue (mConnections, connection, kCFNull);
+		[mConnections setObject: [NSNull null] forKey: connection];
 	}
 }
 
 
 - (void) clientDidFailConnectionAttempt: (id <BXConnectionMonitorClient>) connection
 {
-	@synchronized ((id) mConnections)
+	@synchronized (mConnections)
 	{
-		CFDictionaryRemoveValue (mConnections, connection);
+		[mConnections removeObjectForKey: connection];
 	}
 }
 
 				   withObject: observer
 				waitUntilDone: NO];
 		
-		@synchronized ((id) mConnections)
+		@synchronized (mConnections)
 		{
-			CFDictionarySetValue (mConnections, connection, observer);
+			[mConnections setObject: observer forKey: connection];
 		}
 		
 		[observer release];
 - (void) clientWillDisconnect: (id <BXConnectionMonitorClient>) connection
 {
 	BXSocketReachabilityObserver *observer = nil;
-	@synchronized ((id) mConnections)
+	@synchronized (mConnections)
 	{
-		observer = (id) CFDictionaryGetValue (mConnections, connection);
+		observer = [mConnections objectForKey: connection];
 		[observer retain];
-		CFDictionaryRemoveValue (mConnections, connection);
+		[connection retain];
+		[mConnections removeObjectForKey: connection];
 	}
 	
-	if ((id) kCFNull != observer)
+	if ((id) [NSNull null] != observer)
+	{
 		[observer invalidate];
+		[observer setDelegate: nil];
+	}
 	
 	[observer release];
+	[connection release];
 }
 
 
 - (BOOL) clientCanSend: (id <BXConnectionMonitorClient>) connection
 {
 	BOOL retval = NO;
-	@synchronized ((id) mConnections)
+	@synchronized (mConnections)
 	{
-		BXSocketReachabilityObserver *observer = (id) CFDictionaryGetValue (mConnections, connection);
+		BXSocketReachabilityObserver *observer = [mConnections objectForKey: connection];
 		if ((id) kCFNull == observer)
 		{
 			//If we don't have an observer, it wasn't needed.

Sources/BXDatabaseContext.m

 }
 //@}
 
+
 - (void) dealloc
 {
     mDeallocating = YES;
     
 	[mObjectModel release];
 	[mObjectModelStorage release];
+	
+	[mDatabaseInterface disconnect];
     [mDatabaseInterface release];
+	
     [mDatabaseURI release];
     [mObjects release];
     [mModifiedObjectIDs release];

Sources/BXDispatchSocketDescriptor.m

 //
 
 #import "BXDispatchSocketDescriptor.h"
+#import "BXSocketDescriptorPrivate.h"
 #import <libkern/OSAtomic.h>
 
 
 }
 
 
+- (void) finalize
+{
+	[self invalidate];
+	[super finalize];
+}
+
+
 - (void) install
 {
 	NSString *delegateClassName = [[mDelegate class] description];
 		
 		dispatch_source_set_event_handler (mSocketSource, ^{
 			unsigned long estimated = dispatch_source_get_data (mSocketSource);			
-			[mDelegate socketReadyForReading: mSocket estimatedSize: estimated];
+			[self _socketReadyForReading: mSocket estimatedSize: estimated];
 		});
 		
 		// No cancellation handler because libpq manages the socket.
 
 - (void) invalidate
 {
+	[super invalidate];
+	
 	if (mSocketSource)
 	{
 		dispatch_source_cancel (mSocketSource);

Sources/BXIOKitSystemEventNotifier.m

 static void
 WorkspaceWillSleep (void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
 {
-	
 	BXIOKitSystemEventNotifier *notifier = (id) refCon;
     switch (messageType)
     {
 
 - (void) invalidate
 {
+	[super invalidate];
+	
 	@synchronized ([self class])
 	{
 		CFSetRemoveValue (stInstances, self);

Sources/BXPGAutocommitTransactionHandler.m

 
 
 @implementation BXPGAutocommitTransactionHandler (Connecting)
-- (void) disconnect
-{
-	[mConnection disconnect];
-	[self didDisconnect];
-}
-
-
 - (void) connectAsync
 {
 	[self prepareForConnecting];

Sources/BXPGEntityImporter.m

 - (void) dealloc
 {
 	[mContext release];
+	[mEntityConverter setDelegate: nil];
 	[mEntityConverter release];
 	[mEntities release];
 	[mSchemaName release];
 	{
 		[mEntityConverter setDelegate: nil];
 		[mEntityConverter release];
+		
 		mEntityConverter = [aConverter retain];
 		[mEntityConverter setDelegate: self];
 	}

Sources/BXPGInterface.m

 - (void) disconnect
 {
 	[mTransactionHandler disconnect];
+	[mTransactionHandler setInterface: nil];
 	[mTransactionHandler release];
 	mTransactionHandler = nil;
 }
 
 - (void) dealloc
 {
+	[self disconnect];
 	[mCurrentlyChangedEntity release];
-	[mTransactionHandler release];
 	[mLockedObjects release];
 	[mQueryBuilder release];
 	[super dealloc];

Sources/BXPGManualCommitTransactionHandler.m

 }
 
 
-- (void) dealloc
-{
-	[mNotifyConnection release];
-	[super dealloc];
-}
-
-
 - (BOOL) isSSLInUse
 {
 	return ([super isSSLInUse] && [mNotifyConnection SSLStruct] ? YES : NO);
 {
 	[mNotifyConnection executeQuery: @"SELECT baseten.lock_unlock ()"];
 	[mNotifyConnection disconnect];
-	[mConnection disconnect];
-	[self didDisconnect];
+	[mNotifyConnection setDelegate: nil];
+	[mNotifyConnection release];
+	mNotifyConnection = nil;
+
+	[super disconnect];
 }
 
 

Sources/BXPGSQLScriptReader.m

 {
 	if (mFile)
 		fclose (mFile);
+	
+	[mScanner setDelegate: nil];
 	[mScanner release];
+	
 	[mConnection release];
 	[mDelegateUserInfo release];
 	[super dealloc];

Sources/BXPGTransactionHandler.m

 
 - (void) dealloc
 {
+	[self disconnect];
 	[mCertificateVerificationDelegate release];
-	[mConnection release];
 	[mObservedEntities release];
 	[mObservers release];
 	[mChangeHandlers release];
 	[super dealloc];
 }
 
+
 - (PGTSConnection *) connection
 {
 	return mConnection;
 	{
 		mConnection = [[PGTSConnection alloc] init];
 		[mConnection setDelegate: self];
+		[mConnection setLogsQueries: [mInterface logsQueries]];
 		[mConnection setCertificateVerificationDelegate: mCertificateVerificationDelegate];
-		[mConnection setLogsQueries: [mInterface logsQueries]];
 	}	
 }
 
 
 - (void) disconnect
 {
-	[self doesNotRecognizeSelector: _cmd];
+	[mConnection disconnect];
+	[mConnection setDelegate: nil];
+	[mConnection release];
+	mConnection = nil;
+	[self didDisconnect];
 }
 
 

Sources/BXRunLoopSocketDesciptor.m

 //
 
 #import "BXRunLoopSocketDesciptor.h"
+#import "BXSocketDescriptorPrivate.h"
 #import "BXLogger.h"
 
 
 	if (kCFSocketReadCallBack & callbackType)
 	{
 		BXSocketDescriptor *descriptor = (id) descriptorPtr;
-		[[descriptor delegate] socketReadyForReading: CFSocketGetNative (socket) estimatedSize: 0];
+		[descriptor _socketReadyForReading: CFSocketGetNative (socket) estimatedSize: 0];
 	}
 }
 
 
 - (id) initWithSocket: (int) socket
 {
-	if ((self = [super init]))
+	if ((self = [super initWithSocket: socket]))
 	{
 		CFSocketContext context = {0, self, NULL, NULL, NULL};
 		CFSocketCallBackType callbacks = (CFSocketCallBackType) (kCFSocketReadCallBack | kCFSocketWriteCallBack);
 
 - (void) invalidate
 {
+	[super invalidate];
+	
 	if (mSocketSource)
 	{
 		CFRunLoopSourceInvalidate (mSocketSource);

Sources/BXSocketDescriptor.h

 //
 
 #import <Foundation/Foundation.h>
+@class BXValidationLock;
 
 
 @protocol BXSocketDescriptorDelegate <NSObject>
 
 @interface BXSocketDescriptor : NSObject
 {
+	BXValidationLock *mValidationLock;
 	id <BXSocketDescriptorDelegate> mDelegate;
 }
 + (id) copyDescriptorWithSocket: (int) socket;

Sources/BXSocketDescriptor.m

 // $Id$
 //
 
-#import "BXSocketDescriptor.h"
+#import "BXSocketDescriptorPrivate.h"
 #import "BXDispatchSocketDescriptor.h"
 #import "BXRunLoopSocketDesciptor.h"
+#import "BXValidationLock.h"
 #import <dispatch/dispatch.h>
 
 
 
 - (id) initWithSocket: (int) socket
 {
-	[self doesNotRecognizeSelector: _cmd];
-	return nil;
+	if ([self class] == [BXSocketDescriptor class])
+		[self doesNotRecognizeSelector: _cmd];
+	
+	if ((self = [super init]))
+	{
+		mValidationLock = [[BXValidationLock alloc] init];
+	}
+	return self;
+}
+
+
+- (void) dealloc
+{
+	[mValidationLock invalidate];
+	[mValidationLock release];
+	[super dealloc];
+}
+
+
+- (void) _socketReadyForReading: (int) fd estimatedSize: (unsigned long) size
+{
+	if ([mValidationLock lockIfValid])
+	{
+		[[self delegate] socketReadyForReading: fd estimatedSize: size];
+		[mValidationLock unlock];
+	}
 }
 
 
  */
 - (void) invalidate
 {
-	[self doesNotRecognizeSelector: _cmd];
+	[mValidationLock invalidate];
 }
 
 

Sources/BXSocketDescriptorPrivate.h

+//
+// BXSocketDescriptorPrivate.h
+// BaseTen
+//
+// Copyright (C) 2010 Marko Karppinen & Co. LLC.
+//
+// Before using this software, please review the available licensing options
+// by visiting http://www.karppinen.fi/baseten/licensing/ or by contacting
+// us at sales@karppinen.fi. Without an additional license, this software
+// may be distributed only in compliance with the GNU General Public License.
+//
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2.0,
+// as published by the Free Software Foundation.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+// $Id$
+//
+
+
+#import <BaseTen/BXSocketDescriptor.h>
+
+
+@interface BXSocketDescriptor ()
+- (void) _socketReadyForReading: (int) fd estimatedSize: (unsigned long) size;
+@end

Sources/BXSocketReachabilityObserver.h

 #import <Foundation/Foundation.h>
 #import <SystemConfiguration/SystemConfiguration.h>
 @class BXSocketReachabilityObserver;
+@class BXValidationLock;
 
 
 
 	SCNetworkReachabilityRef mSyncReachability;
 	SCNetworkReachabilityRef mAsyncReachability;
 	CFRunLoopRef mRunLoop;
+	BXValidationLock *mValidationLock;
 	id <BXSocketReachabilityObserverDelegate> mDelegate;
 	void *mUserInfo;
 }

Sources/BXSocketReachabilityObserver.m

 //
 
 #import "BXSocketReachabilityObserver.h"
+#import "BXValidationLock.h"
 #import "BXLogger.h"
 #import <sys/socket.h>
 #import <arpa/inet.h>
 
 
+@interface BXSocketReachabilityObserver ()
+- (void) _networkStatusChanged: (SCNetworkReachabilityFlags) flags;
+@end
+
+
 
 static void
 NetworkStatusChanged (SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *observerPtr)
 {
 	BXSocketReachabilityObserver* observer = (BXSocketReachabilityObserver *) observerPtr;
-	[[observer delegate] socketReachabilityObserver: observer networkStatusChanged: flags];
+	[observer _networkStatusChanged: flags];
 }
 
 
 + (BOOL) getAddress: (struct sockaddr **) addressPtr forPeer: (BOOL) peerAddress ofSocket: (int) socket
 {
 	ExpectR (addressPtr, NO);
+	ExpectR (! *addressPtr, NO);
 
 	BOOL retval = NO;
 	const size_t size = SOCK_MAXADDRLEN;
 	struct sockaddr *peerAddress = NULL;
 	
 	if ([self getAddress: &address forPeer: NO  ofSocket: socket] &&
-		[self getAddress: &address forPeer: YES ofSocket: socket])
+		[self getAddress: &peerAddress forPeer: YES ofSocket: socket])
 	{
 		//We don't need to monitor UNIX internal protocols and SC functions seem to return
 		//bad values for them anyway.
 {
 	if ((self = [super init]))
 	{
+		mValidationLock = [[BXValidationLock alloc] init];
+		
 		// Apparently two SCNetworkReachabilities are needed to allow both
 		// asynchronous notification and thread safe synchronous checks:
 		//
 }
 
 
+- (void) _networkStatusChanged: (SCNetworkReachabilityFlags) flags
+{
+	if ([mValidationLock lockIfValid])
+	{
+		[[self delegate] socketReachabilityObserver: self networkStatusChanged: flags];
+		[mValidationLock unlock];
+	}
+}
+
+
 /**
  * \brief Install the observer into a run loop.
  */
 - (BOOL) install
 {
-	ExpectR (mRunLoop, NO);
-	ExpectR (mAsyncReachability, NO);
-	
-	SCNetworkReachabilityContext ctx = {0, self, NULL, NULL, NULL};
-	SCNetworkReachabilitySetCallback (mAsyncReachability, &NetworkStatusChanged, &ctx);
-	return (SCNetworkReachabilityScheduleWithRunLoop (mAsyncReachability, mRunLoop, kCFRunLoopCommonModes) ? YES : NO);
+	BOOL retval = NO;
+	@synchronized (self)
+	{
+		if (mAsyncReachability && mRunLoop)
+		{
+			SCNetworkReachabilityContext ctx = {0, self, NULL, NULL, NULL};
+			SCNetworkReachabilitySetCallback (mAsyncReachability, &NetworkStatusChanged, &ctx);
+			if (SCNetworkReachabilityScheduleWithRunLoop (mAsyncReachability, mRunLoop, kCFRunLoopCommonModes))
+				retval = YES;
+		}
+	}
+	return retval;
 }
 
 
  */
 - (void) invalidate
 {
+	[mValidationLock invalidate];
+	
 	if (mAsyncReachability && mRunLoop)
 		SCNetworkReachabilityUnscheduleFromRunLoop (mAsyncReachability, mRunLoop, kCFRunLoopCommonModes);
 	
 		mAsyncReachability = NULL;
 	}
 	
-	if (mSyncReachability)
-	{
-		CFRelease (mSyncReachability);
-		mSyncReachability = NULL;
-	}
-	
 	if (mRunLoop)
 	{
 		CFRelease (mRunLoop);
 		mRunLoop = NULL;
 	}
+	
+	@synchronized (self)
+	{
+		if (mSyncReachability)
+		{
+			CFRelease (mSyncReachability);
+			mSyncReachability = NULL;
+		}
+	}
 }
 
 

Sources/BXSystemEventNotifier.h

 
 #import <Foundation/Foundation.h>
 #import <BaseTen/BXExport.h>
+@class BXValidationLock;
 
 
 BX_INTERNAL NSString * const kBXSystemEventNotifierProcessWillExitNotification;
 
 @interface BXSystemEventNotifier : NSObject
 {
+	BXValidationLock *mValidationLock;
 }
 + (id) copyNotifier;
 - (void) install;

Sources/BXSystemEventNotifier.m

 #import "BXSystemEventNotifier.h"
 #import "BXIOKitSystemEventNotifier.h"
 #import "BXProbes.h"
+#import "BXValidationLock.h"
 
 #import "../BaseTenAppKit/Sources/BXAppKitSystemEventNotifier.h"
 Class BXAppKitSystemEventNotifierClass WEAK_IMPORT_ATTRIBUTE;
 }
 
 
+- (id) init
+{
+	if ([self class] == [BXSystemEventNotifier class])
+		[self doesNotRecognizeSelector: _cmd];
+	
+	if ((self = [super init]))
+	{
+		mValidationLock = [[BXValidationLock alloc] init];
+	}
+	return self;
+}
+
+
+- (void) dealloc
+{
+	[self invalidate];
+	[mValidationLock release];
+	[super dealloc];
+}
+
+
 - (void) install
 {
 }
 
 - (void) invalidate
 {
+	[mValidationLock invalidate];
 }
 
 
 - (void) processWillExit
 {
-	BASETEN_BEGIN_EXIT_PREPARATION ();
-	NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
-	[nc postNotificationName: kBXSystemEventNotifierProcessWillExitNotification object: self];
-	BASETEN_END_EXIT_PREPARATION ();
+	if ([mValidationLock lockIfValid])
+	{
+		BASETEN_BEGIN_EXIT_PREPARATION ();
+		NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+		[nc postNotificationName: kBXSystemEventNotifierProcessWillExitNotification object: self];
+		BASETEN_END_EXIT_PREPARATION ();
+		
+		[mValidationLock unlock];
+	}
 }
 
 
 - (void) systemWillSleep
 {
-	BASETEN_BEGIN_SLEEP_PREPARATION ();
-	NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
-	[nc postNotificationName: kBXSystemEventNotifierSystemWillSleepNotification object: self];
-	BASETEN_END_SLEEP_PREPARATION ();
+	if ([mValidationLock lockIfValid])
+	{
+		BASETEN_BEGIN_SLEEP_PREPARATION ();
+		NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+		[nc postNotificationName: kBXSystemEventNotifierSystemWillSleepNotification object: self];
+		BASETEN_END_SLEEP_PREPARATION ();
+		
+		[mValidationLock unlock];
+	}
 }
 
 
 - (void) systemDidWake
 {
-	BASETEN_BEGIN_WAKE_PREPARATION ();
-	NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
-	[nc postNotificationName: kBXSystemEventNotifierSystemDidWakeNotification object: self];
-	BASETEN_END_WAKE_PREPARATION ();
+	if ([mValidationLock lockIfValid])
+	{
+		BASETEN_BEGIN_WAKE_PREPARATION ();
+		NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+		[nc postNotificationName: kBXSystemEventNotifierSystemDidWakeNotification object: self];
+		BASETEN_END_WAKE_PREPARATION ();
+		
+		[mValidationLock unlock];
+	}
 }
 @end

Sources/BXValidationLock.h

+//
+// BXValidationLock.h
+// BaseTen
+//
+// Copyright (C) 2010 Marko Karppinen & Co. LLC.
+//
+// Before using this software, please review the available licensing options
+// by visiting http://www.karppinen.fi/baseten/licensing/ or by contacting
+// us at sales@karppinen.fi. Without an additional license, this software
+// may be distributed only in compliance with the GNU General Public License.
+//
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2.0,
+// as published by the Free Software Foundation.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+// $Id$
+//
+
+#import <Foundation/Foundation.h>
+#import <pthread.h>
+
+
+@interface BXValidationLock : NSObject
+{
+	pthread_rwlock_t mLock;
+	BOOL mIsValid;
+}
+- (BOOL) lockIfValid;
+- (void) unlock;
+- (void) invalidate;
+@end

Sources/BXValidationLock.m

+//
+// BXValidationLock.m
+// BaseTen
+//
+// Copyright (C) 2010 Marko Karppinen & Co. LLC.
+//
+// Before using this software, please review the available licensing options
+// by visiting http://www.karppinen.fi/baseten/licensing/ or by contacting
+// us at sales@karppinen.fi. Without an additional license, this software
+// may be distributed only in compliance with the GNU General Public License.
+//
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2.0,
+// as published by the Free Software Foundation.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+// $Id$
+//
+
+#import "BXValidationLock.h"
+#import "BXLogger.h"
+
+
+@implementation BXValidationLock
+- (id) init
+{
+	if ((self = [super init]))
+	{
+		Expect (0 == pthread_rwlock_init (&mLock, NULL));
+		mIsValid = YES;
+	}
+	return self;
+}
+
+
+- (void) finalize
+{
+	[self invalidate];
+	[super finalize];
+}
+
+
+- (void) dealloc
+{
+	[self invalidate];
+	[super dealloc];
+}
+
+
+- (BOOL) lockIfValid
+{
+	pthread_rwlock_rdlock (&mLock);
+	if (! mIsValid)
+		pthread_rwlock_unlock (&mLock);
+	
+	return mIsValid;
+}
+
+
+- (void) unlock
+{
+	pthread_rwlock_unlock (&mLock);
+}
+
+
+- (void) invalidate
+{
+	pthread_rwlock_wrlock (&mLock);
+	mIsValid = NO;
+	pthread_rwlock_unlock (&mLock);
+}
+@end

Sources/PGTSAsynchronousConnector.m

 	[self cancel];
 	[mConnectionError release];
 	[mConnectionDictionary release];
+	
+	[mHostResolver setDelegate: nil];
 	[mHostResolver release];
+	
 	[super dealloc];
 }
 
 - (void) finalize
 {
 	[self freeCFTypes];
-	[self cancel];
 	[super finalize];
 }
 

Sources/PGTSConnection.mm

 - (void) dealloc
 {
     [self disconnect];
+	[self _setConnector: nil];
+	
 	[mQueue release];
-	[self _setConnector: nil];
     [mMetadataContainer release];
-	[mSocketDescriptor release];
 	[mPGTypes release];
 	[super dealloc];
 }
 
 
-- (void) finalize
-{
-    [self disconnect];
-    [super finalize];
-}
-
-
 - (BOOL) connectUsingClass: (Class) connectorClass connectionDictionary: (NSDictionary *) connectionDictionary
 {
 	// FIXME: thread safety?
 	[self _setConnector: connector];
 	
 	[connector setConnection: mConnection]; //For resetting.
-	[connector setDelegate: self];
 	[connector setTraceFile: [mDelegate PGTSConnectionTraceFile: self]];
 	[[BXConnectionMonitor sharedInstance] clientDidStartConnectionAttempt: self];
 	BXLogDebug (@"Making %@ connect.", [connector class]);
 	        mConnection = NULL;
 		}
 	}
+	
+	[self _setSocketDescriptor: nil];
 }
 
 
 		if (mConnector != anObject)
 		{
 			[mConnector cancel];
+			[mConnector setDelegate: nil];
 			[mConnector release];
+			
 			mConnector = [anObject retain];
+			[mConnector setDelegate: self];
 		}
 	}
 }
 
 - (void) _setSocketDescriptor: (BXSocketDescriptor *) desc
 {
+	BXSocketDescriptor *oldDescriptor = nil;
 	@synchronized (self)
 	{
 		if (desc != mSocketDescriptor)
 		{
-			[mSocketDescriptor invalidate];
-			[mSocketDescriptor release];
+			oldDescriptor = mSocketDescriptor;
 			
 			mSocketDescriptor = [desc retain];
+			[mSocketDescriptor setDelegate: self];
 			[mSocketDescriptor install];
 		}
 	}
+	
+	[oldDescriptor invalidate];
+	[oldDescriptor setDelegate: nil];
+	[oldDescriptor release];
 }
 
 
 		while ((pgNotification = PQnotifies (mConnection)))
 		{
 			if (! notifications)
-				notifications = [NSMutableArray array];
+				notifications = [[NSMutableArray alloc] init];
 			
 			NSString* name = [NSString stringWithUTF8String: pgNotification->relname];
 			PGTSNotification* notification = [[PGTSNotification alloc] init];
 			[notification release];
 		}    
 	}
+
+	if ([notifications count])
+	{
+		id <PGTSConnectionDelegate> delegate = [self delegate];
+		for (PGTSNotification *notification in notifications)
+			[delegate PGTSConnection: self gotNotification: notification];
+	}
 	
-	for (PGTSNotification *notification in notifications)
-		[mDelegate PGTSConnection: self gotNotification: notification];
+	[notifications release];
 }
 
 
 		PQsetNoticeReceiver (connection, &NoticeReceiver, (void *) self);
 		
 		BXSocketDescriptor *desc = [BXSocketDescriptor copyDescriptorWithSocket: PQsocket (mConnection)];
-		[desc setDelegate: self];
 		[self _setSocketDescriptor: desc];
 		[desc release];
 		

UnitTests/Sources/FetchTests.m

 	STAssertNotNil (object, [error description]);
 	
     MKCAssertEqualObjects ([object primitiveValueForKey: @"id"], [NSNumber numberWithInt: 1]);
-    //if this is not nil, then another test has failed or the database is not in known state
+    //if this is not nil, then another test has failed or the database isn't clean.
     STAssertEqualObjects ([object valueForKey: @"value"], nil, @"Database is not in known state!");
 }