From 730b41ece1a7bfc28f0fe8fd836bfa1ab4ac0e3b Mon Sep 17 00:00:00 2001 From: Bjorn Winckler Date: Thu, 27 Aug 2009 18:16:44 +0200 Subject: [PATCH] Avoid race condition (e.g. when closing windows) The app may become multithreaded e.g. due to the open panel being displayed. In this case connectionDidDie notifications may arrive outside the main thread, possibly leading to windows being closed simultaneously on multiple threads. This scenario could happen e.g. when quitting with multiple windows open. To avoid this situation performSelectorOnMainThread: is used instead of performSelector:. --- src/MacVim/MMAppController.m | 41 ++++++++++++++++++++++++++--------------- src/MacVim/MMVimController.m | 11 ++++++++--- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/MacVim/MMAppController.m b/src/MacVim/MMAppController.m index b60cfda5..2f2abc32 100644 --- a/src/MacVim/MMAppController.m +++ b/src/MacVim/MMAppController.m @@ -699,9 +699,10 @@ fsEventCallback(ConstFSEventStreamRef streamRef, return; } - [controller cleanup]; - + [controller retain]; [vimControllers removeObjectAtIndex:idx]; + [controller cleanup]; + [controller release]; if (![vimControllers count]) { // The last editor window just closed so restore the main menu back to @@ -1213,11 +1214,16 @@ fsEventCallback(ConstFSEventStreamRef streamRef, // (What if input arrives before the vim controller is added to the list of // controllers? This should not be a problem since the input isn't // processed immediately (see processInput:forIdentifier:).) + // Also, since the app may be multithreaded (e.g. as a result of showing + // the open panel) we have to ensure this call happens on the main thread, + // else there is a race condition that may lead to a crash. MMVimController *vc = [[MMVimController alloc] initWithBackend:proxy pid:pid]; - [self performSelector:@selector(addVimController:) - withObject:vc - afterDelay:0]; + [self performSelectorOnMainThread:@selector(addVimController:) + withObject:vc + waitUntilDone:NO + modes:[NSArray arrayWithObject: + NSDefaultRunLoopMode]]; [vc release]; @@ -1251,11 +1257,15 @@ fsEventCallback(ConstFSEventStreamRef streamRef, // NOTE: We must use "event tracking mode" as well as "default mode", // otherwise the input queue will not be processed e.g. during live // resizing. - [self performSelector:@selector(processInputQueues:) - withObject:nil - afterDelay:0 - inModes:[NSArray arrayWithObjects:NSDefaultRunLoopMode, - NSEventTrackingRunLoopMode, nil]]; + // Also, since the app may be multithreaded (e.g. as a result of showing + // the open panel) we have to ensure this call happens on the main thread, + // else there is a race condition that may lead to a crash. + [self performSelectorOnMainThread:@selector(processInputQueues:) + withObject:nil + waitUntilDone:NO + modes:[NSArray arrayWithObjects: + NSDefaultRunLoopMode, + NSEventTrackingRunLoopMode, nil]]; } - (NSArray *)serverList @@ -2270,11 +2280,12 @@ fsEventCallback(ConstFSEventStreamRef streamRef, // If new input arrived while we were processing it would have been // blocked so we have to schedule it to be processed again. if (processingFlag < 0) - [self performSelector:@selector(processInputQueues:) - withObject:nil - afterDelay:0 - inModes:[NSArray arrayWithObjects:NSDefaultRunLoopMode, - NSEventTrackingRunLoopMode, nil]]; + [self performSelectorOnMainThread:@selector(processInputQueues:) + withObject:nil + waitUntilDone:NO + modes:[NSArray arrayWithObjects: + NSDefaultRunLoopMode, + NSEventTrackingRunLoopMode, nil]]; processingFlag = 0; } diff --git a/src/MacVim/MMVimController.m b/src/MacVim/MMVimController.m index 36db8b1a..77344d80 100644 --- a/src/MacVim/MMVimController.m +++ b/src/MacVim/MMVimController.m @@ -1285,10 +1285,15 @@ static BOOL isUnsafeMessage(int msgid); // free objects that Cocoa is currently using (e.g. view objects). The // following call ensures that the vim controller is not released until the // run loop is back in the 'default' mode. + // Also, since the app may be multithreaded (e.g. as a result of showing + // the open panel) we have to ensure this call happens on the main thread, + // else there is a race condition that may lead to a crash. [[MMAppController sharedInstance] - performSelector:@selector(removeVimController:) - withObject:self - afterDelay:0]; + performSelectorOnMainThread:@selector(removeVimController:) + withObject:self + waitUntilDone:NO + modes:[NSArray arrayWithObject: + NSDefaultRunLoopMode]]; } // NSSavePanel delegate -- 2.11.4.GIT