Solution
1. Add to your project a file EOFThreadWorkaround.h containing the following:
// EOFThreadWorkaround.h
// Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
#import <EOControl/EOControl.h>
@interface EOObserverCenter (ThreadWorkaround)
// preventCertainExceptionsOnThreadExit -- Invoke this once, at startup, before doing any EOF operations, to work
// around certain problems with multi-threaded EOF applications.
+ (void) preventCertainExceptionsOnThreadExit;
// changeThreadCache -- Any thread which does EOF operations but which is NOT created with NSThread should
// invoke this just before exiting.
+ (void) changeThreadCache;
@end
2. Add to your project a file EOFThreadWorkaround.m containing the following:
// EOFThreadWorkaround.m
// Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
#import "EOFThreadWorkaround.h"
@implementation EOObserverCenter (ThreadWorkaround)
static NSConditionLock *threadFixLock = nil;
// Conditions for the above lock
#define CONDITION_VALID 1 // idle state
#define CONDITION_NEEDCHANGE 2 // need some other thread to change the cache for us
+ (void) _changeThreadCacheWhenNeeded;
{
[[NSAutoreleasePool alloc] init];
// Loop forever, waiting for other threads to signal that they need the cache changed.
// When they do, query the suppress count, which gets OUR thread into the cache.
// Since our thread stays valid forever, this makes the cache valid, important when
// other threads are exiting.
while (1)
{
[threadFixLock lockWhenCondition: CONDITION_NEEDCHANGE]; // wait 'til we're needed
[EOObserverCenter observerNotificationSuppressCount];
[threadFixLock unlockWithCondition: CONDITION_VALID]; // and tell the world we're done
}
}
+ (void) preventCertainExceptionsOnThreadExit;
{
static BOOL beenHere = NO;
if (beenHere) return; // do this only once
beenHere = YES;
// Create the lock to let any thread communicate with the cache-fixing thread
threadFixLock = [[NSConditionLock alloc] initWithCondition: CONDITION_VALID];
// Spawn the cache-fixing thread
[NSThread detachNewThreadSelector :@selector(_changeThreadCacheWhenNeeded) toTarget: self withObject: nil];
// When any thread is about to exit, change the cache.
[[NSNotificationCenter defaultCenter] addObserver: self
selector:@selector(changeThreadCache)
name:NSThreadWillExitNotification
object: nil];
}
+ (void) changeThreadCache;
{
// Grab the lock so we know the other thread isn't in the middle of things
// Then unlock it, to make it do its change-thing
[threadFixLock lockWhenCondition: CONDITION_VALID]; // wait 'til we can jump in
[threadFixLock unlockWithCondition: CONDITION_NEEDCHANGE]; // and tell helper thread to do its thing
// Wait for it to do its thing
// Then unlock it without changing things
[threadFixLock lockWhenCondition: CONDITION_VALID]; // wait for it to do its thing
[threadFixLock unlock];
}
@end
3. Change your application to include the following, invoking the method only once at startup (in the main() function or in your application's initialization):
#import "EOFThreadWorkaround.h"
...
[EOObserverCenter preventCertainExceptionsOnThreadExit]; // one-time code to set up workaround
4. If your application or any third-party libraries in your application spawn any threads that use EOF but do not exit with [NSThread exit], invoke the following method just before exiting the thread.
[EOObserverCenter changeThreadCache]; // make sure our thread isn't in EOF's cache
This step is critical to working around the problem -- step 3 covers only threads that exit with [NSThread exit], but you must make sure that every thread that uses EOF is corrected. This step handles threads that do not exit with NSThread's method.