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:.
This commit is contained in:
Bjorn Winckler
2009-08-27 18:16:44 +02:00
parent 7cae9e965c
commit d69e140c24
2 changed files with 34 additions and 18 deletions
+26 -15
View File
@@ -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;
}
+8 -3
View File
@@ -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