Commits

Tuukka Norri committed ec8be9c

Bug fixes
- Fixed a bug in BXNetServiceConnector which caused the CFHost's run loop callback to be left pending even after the listener had been deallocated.
- Fixed a bug in BXSynchronizedArrayController which caused garbage to be left in an ivar.
- BXNetServiceConnector now handles the situation in which an application modal panel is being used and the host is specified in the initial database URI. (Fixes #175.)

  • Participants
  • Parent commits d3b2ba8

Comments (0)

Files changed (3)

File BaseTenAppKit/Sources/BXNetServiceConnector.h

 	enum BXNSConnectorCurrentPanel mCurrentPanel;
 	
 	CFHostRef mHost;
+	NSString* mRunLoopMode;
+	
 	NSString* mHostName;
 	NSInteger mPort;
 }

File BaseTenAppKit/Sources/BXNetServiceConnector.m

 @interface BXApplicationModalNSConnectorImplementation : BXNSConnectorImplementation <BXNSConnectorImplementation>
 {
 	BOOL mBegunSendingPeriodicEvents;
+	BOOL mHavePanel;
 }
 @end
 
 
 - (NSString *) runLoopMode
 {
-	return NSModalPanelRunLoopMode;
+	NSString* retval = (mHavePanel ? NSModalPanelRunLoopMode : NSDefaultRunLoopMode);
+	return retval;
 }
 
 - (void) presentError2: (NSError *) error callback: (SEL) selector
 {
 	[hostPanel makeKeyAndOrderFront: nil];
 	[hostPanel center];
+	mHavePanel = YES;
 	[NSApp runModalForWindow: hostPanel];
+	mHavePanel = NO;
 }
 
 - (void) displayHostPanel: (BXHostPanel *) hostPanel
 {
 	[authenticationPanel makeKeyAndOrderFront: nil];
 	[authenticationPanel center];
+	mHavePanel = YES;
 	[NSApp runModalForWindow: authenticationPanel];
+	mHavePanel = NO;
 }
 
 - (void) displayAuthenticationPanel: (BXAuthenticationPanel *) authenticationPanel
  * \ingroup baseten_appkit
  */
 @implementation BXNetServiceConnector
-- (void) dealloc
+- (void) removeHost
 {
 	if (mHost)
 	{
+		CFHostCancelInfoResolution (mHost, kCFHostReachability);
+		if (mRunLoopMode)
+			CFHostUnscheduleFromRunLoop (mHost, CFRunLoopGetCurrent (), (CFStringRef) mRunLoopMode);
 		CFRelease (mHost);
 		mHost = NULL;
-	}
+	}	
+}
+
+- (void) dealloc
+{	
+	[self removeHost];
+	[mRunLoopMode release];
 	
 	[mHostPanel release];
 	[mAuthenticationPanel release];
 
 - (void) finalize
 {
-	if (mHost)
-	{
-		CFRelease (mHost);
-		mHost = NULL;
-	}
-	
+	[self removeHost];
+	if (mRunLoopMode)
+		CFRelease (mRunLoopMode);
 	[super finalize];
 }
 
 	}
 }
 
+- (void) setRunLoopMode: (NSString *) mode
+{
+	if (mode != mRunLoopMode)
+	{
+		if (mRunLoopMode)
+			CFRelease (mRunLoopMode);
+		
+		if (mode)
+			CFRetain (mode);
+			
+		mRunLoopMode = mode;
+	}
+}
+
 #pragma mark Start here
 - (IBAction) connect: (id) sender
 {	
 {
 	if (mHost)
 	{
-		CFHostCancelInfoResolution (mHost, kCFHostReachability);
-		CFRelease (mHost);
-		mHost = nil;
+		[self removeHost];
+		[self setRunLoopMode: nil];
 	}
 	else
 	{
 
 - (void) checkHostReachability: (NSString *) name
 {
+	Boolean status = FALSE;
+	[self removeHost];
 	mHost = CFHostCreateWithName (CFAllocatorGetDefault (), (CFStringRef) name);
 	CFHostClientContext ctx = {
 		0,
 		NULL,
 		NULL
 	};
-	CFHostSetClient (mHost, &HostClientCallback, &ctx);
-	CFHostScheduleWithRunLoop (mHost, CFRunLoopGetCurrent (), (CFStringRef) [mConnectorImpl runLoopMode]);
+	status = CFHostSetClient (mHost, &HostClientCallback, &ctx);
+	
+	CFRunLoopRef rl = CFRunLoopGetCurrent ();
+	NSString* mode = [mConnectorImpl runLoopMode];
+	[self setRunLoopMode: mode];
+	CFHostScheduleWithRunLoop (mHost, rl, (CFStringRef) mode);
 	
 	CFStreamError error = {};
 	if (! CFHostStartInfoResolution (mHost, kCFHostReachability, &error))
 		
 		//FIXME: localization.
 		
-		if (mHost)
-		{
-			CFRelease (mHost);
-			mHost = nil;
-		}		
+		[self removeHost];
+		[self setRunLoopMode: nil];
 
 		[[self hostPanel] setMessage: message];
 		if (kBXNSConnectorHostPanel == mCurrentPanel)
 	}
 	else
 	{
-		if (mHost)
-		{
-			CFRelease (mHost);
-			mHost = nil;
-		}		
-		
+		[self removeHost];
+		[self setRunLoopMode: nil];
+
 		//Complete the database URI. If we're allowed to use the Keychain, try to fetch some credentials.
 		//If none are found, display the authentication panel. Otherwise connect.
 		//Don't use -setDatabaseURIInternal: because we just got a new host name.
-		NSURL* databaseURI = [mContext databaseURI];
-		databaseURI = [databaseURI BXURIForHost: mHostName
-										   port: (-1 == mPort ? nil : [NSNumber numberWithInteger: mPort])
-									   database: nil 
-									   username: nil 
-									   password: nil];
-		[mContext setDatabaseURI: databaseURI];
+		NSURL* oldURI = [mContext databaseURI];
+		NSURL* newURI = [oldURI BXURIForHost: mHostName
+										port: (-1 == mPort ? nil : [NSNumber numberWithInteger: mPort])
+									database: nil
+									username: nil
+									password: nil];
+		[mContext setDatabaseURI: newURI];
 				
 		if ([mContext usesKeychain])
             [mContext fetchPasswordFromKeychain];
 }
 
 - (void) endConnecting: (NSNotification *) notification
-{
+{	
 	if ([[notification name] isEqualToString: kBXConnectionSuccessfulNotification])
 	{
 		[self endPanelUnless: kBXNSConnectorNoPanel];

File BaseTenAppKit/Sources/BXSynchronizedArrayController.m

 - (void) prepareEntity
 {
 	NSError* error = nil;
+	[self setEntityDescription: nil];
 	BXEntityDescription* entityDescription = [databaseContext entityForTable: [self tableName] 
 																	inSchema: [self schemaName]
 																	   error: &error];
             //Also set the entity description, since the database URI has changed.
 			if (nil != [self tableName] && [databaseContext canGiveEntities])
 				[self prepareEntity];
-			else
-				[nc addObserver: self selector: @selector (gotDatabaseURI:) name: kBXGotDatabaseURINotification object: databaseContext];
+
+			[nc addObserver: self selector: @selector (gotDatabaseURI:) name: kBXGotDatabaseURINotification object: databaseContext];
 		}
     }
 }