Revision: 15191
http://bibdesk.svn.sourceforge.net/bibdesk/?rev=15191&view=rev
Author: hofman
Date: 2009-04-25 23:37:45 +0000 (Sat, 25 Apr 2009)
Log Message:
-----------
Use condition lock to handle queued notifications for file search rather than a
mach port.
Modified Paths:
--------------
trunk/bibdesk/BDSKFileSearchIndex.h
trunk/bibdesk/BDSKFileSearchIndex.m
Modified: trunk/bibdesk/BDSKFileSearchIndex.h
===================================================================
--- trunk/bibdesk/BDSKFileSearchIndex.h 2009-04-25 22:05:41 UTC (rev 15190)
+++ trunk/bibdesk/BDSKFileSearchIndex.h 2009-04-25 23:37:45 UTC (rev 15191)
@@ -66,8 +66,7 @@
BDSKReadWriteLock *rwLock;
NSMutableArray *notificationQueue;
- NSLock *noteLock;
- NSMachPort *notificationPort;
+ NSConditionLock *noteLock;
NSThread *notificationThread;
NSConditionLock *setupLock;
BDSKSearchIndexFlags flags;
Modified: trunk/bibdesk/BDSKFileSearchIndex.m
===================================================================
--- trunk/bibdesk/BDSKFileSearchIndex.m 2009-04-25 22:05:41 UTC (rev 15190)
+++ trunk/bibdesk/BDSKFileSearchIndex.m 2009-04-25 23:37:45 UTC (rev 15191)
@@ -66,6 +66,9 @@
#define INDEX_THREAD_WORKING 3
#define INDEX_THREAD_DONE 4
+#define QUEUE_EMPTY 0
+#define QUEUE_HAS_NOTIFICATIONS 1
+
// increment if incompatible changes are introduced
#define CACHE_VERSION @"1"
@@ -94,6 +97,9 @@
delegate = nil;
lastUpdateTime = CFAbsoluteTimeGetCurrent();
+ notificationQueue = [[NSMutableArray alloc] init];
+ noteLock = [[NSConditionLock alloc] initWithCondition:QUEUE_EMPTY];
+
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
SEL handler = @selector(processNotification:);
[nc addObserver:self selector:handler
name:BDSKFileSearchIndexInfoChangedNotification object:aDocument];
@@ -130,7 +136,6 @@
identifierURLs = nil;
[rwLock unlock];
[rwLock release];
- [notificationPort release];
[notificationQueue release];
[noteLock release];
[signatures release];
@@ -140,16 +145,23 @@
[super dealloc];
}
+- (BOOL)shouldKeepRunning {
+ OSMemoryBarrier();
+ return flags.shouldKeepRunning == 1;
+}
+
// cancel is always sent from the main thread
- (void)cancelForDocumentURL:(NSURL *)documentURL
{
NSParameterAssert([NSThread isMainThread]);
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
- OSAtomicCompareAndSwap32Barrier(flags.shouldKeepRunning, 0,
&flags.shouldKeepRunning);
+ OSAtomicCompareAndSwap32Barrier(1, 0, &flags.shouldKeepRunning);
- // wake the thread up so the runloop will exit; shouldKeepRunning may have
already done that, so don't send if the port is already dead
- if ([notificationPort isValid])
- [notificationPort sendBeforeDate:[NSDate date] components:nil from:nil
reserved:0];
+ [noteLock lock];
+ [notificationQueue removeAllObjects];
+ // wake the thread if necessary
+ [noteLock unlockWithCondition:QUEUE_HAS_NOTIFICATIONS];
// wait until the thread exits, so we have exclusive access to the ivars
[setupLock lockWhenCondition:INDEX_THREAD_DONE];
@@ -324,7 +336,7 @@
{
BDSKASSERT([NSThread isMainThread]);
OSMemoryBarrier();
- if (flags.shouldKeepRunning == 1)
+ if ([self shouldKeepRunning])
[delegate searchIndexDidFinish:self];
}
@@ -423,8 +435,7 @@
// Use a local pool since initial indexing can use a fair amount of
memory, and it's not released until the thread's run loop starts
NSAutoreleasePool *pool = [NSAutoreleasePool new];
- OSMemoryBarrier();
- while(flags.shouldKeepRunning == 1 && (anObject = [enumerator
nextObject])) {
+ while([self shouldKeepRunning] && (anObject = [enumerator nextObject])) {
[self indexFileURLs:[NSSet setWithArray:[anObject
objectForKey:@"urls"]] forIdentifierURL:[anObject
objectForKey:@"identifierURL"]];
numberIndexed++;
@synchronized(self) {
@@ -436,7 +447,6 @@
if (0 == flags.updateScheduled)
[self performSelectorOnMainThread:@selector(searchIndexDidUpdate)
withObject:nil waitUntilDone:NO];
- OSMemoryBarrier();
}
// caller queues a final update
@@ -444,6 +454,95 @@
[pool release];
}
+#pragma mark Change notification handling
+
+- (void)processNotification:(NSNotification *)note
+{
+ BDSKASSERT([NSThread isMainThread]);
+ // get the search index info, don't use the note because we don't want to
retain the pubs
+ NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:[note
name], @"name",
+ [[note userInfo]
valueForKeyPath:@"pubs.searchIndexInfo"], @"searchIndexInfo", nil];
+ [noteLock lock];
+ [notificationQueue addObject:info];
+ [noteLock unlockWithCondition:QUEUE_HAS_NOTIFICATIONS];
+}
+
+- (NSDictionary *)newNotification
+{
+ NSDictionary *note = nil;
+ [noteLock lockWhenCondition:QUEUE_HAS_NOTIFICATIONS];
+ NSUInteger count = [notificationQueue count];
+ if (count > 0) {
+ note = [[notificationQueue objectAtIndex:0] retain];
+ [notificationQueue removeObjectAtIndex:0];
+ count--;
+ }
+ [noteLock unlockWithCondition:(count > 0 ? QUEUE_HAS_NOTIFICATIONS :
QUEUE_EMPTY)];
+ return note;
+}
+
+- (void)processDocAddItem:(NSArray *)searchIndexInfo
+{
+ BDSKASSERT([[NSThread currentThread] isEqual:notificationThread]);
+
+ BDSKPRECONDITION(searchIndexInfo);
+
+ // this will update the delegate when all is complete
+ [self indexFilesForItems:searchIndexInfo numberPreviouslyIndexed:0
totalCount:[searchIndexInfo count]];
+}
+
+- (void)processDocDelItem:(NSArray *)searchIndexInfo
+{
+ BDSKASSERT([[NSThread currentThread] isEqual:notificationThread]);
+
+ NSEnumerator *itemEnumerator = [searchIndexInfo objectEnumerator];
+ id anItem;
+
+ NSURL *identifierURL = nil;
+ NSSet *urlsToRemove;
+
+ while (anItem = [itemEnumerator nextObject]) {
+ identifierURL = [anItem valueForKey:@"identifierURL"];
+
+ [rwLock lockForReading];
+ urlsToRemove = [[[identifierURLs allKeysForObject:identifierURL] copy]
autorelease];
+ [rwLock unlock];
+
+ [self removeFileURLs:urlsToRemove forIdentifierURL:identifierURL];
+ }
+
+ if (0 == flags.updateScheduled)
+ [self performSelectorOnMainThread:@selector(searchIndexDidUpdate)
withObject:nil waitUntilDone:NO];
+}
+
+- (void)processSearchIndexInfoChanged:(NSArray *)searchIndexInfo
+{
+ BDSKASSERT([[NSThread currentThread] isEqual:notificationThread]);
+
+ NSDictionary *item = [searchIndexInfo lastObject];
+ NSURL *identifierURL = [item objectForKey:@"identifierURL"];
+
+ NSSet *newURLs = [[NSSet alloc] initWithArray:[item valueForKey:@"urls"]];
+ NSMutableSet *removedURLs;
+
+ [rwLock lockForReading];
+ removedURLs = [[identifierURLs allKeysForObject:identifierURL]
mutableCopy];
+ [rwLock unlock];
+ [removedURLs minusSet:newURLs];
+
+ if ([removedURLs count])
+ [self removeFileURLs:removedURLs forIdentifierURL:identifierURL];
+
+ if ([newURLs count])
+ [self indexFileURLs:newURLs forIdentifierURL:identifierURL];
+
+ [removedURLs release];
+ [newURLs release];
+
+ if (0 == flags.updateScheduled)
+ [self performSelectorOnMainThread:@selector(searchIndexDidUpdate)
withObject:nil waitUntilDone:NO];
+}
+
#pragma mark Thread initialization
static void addItemFunction(const void *value, void *context) {
@@ -498,8 +597,7 @@
// cached index, update identifierURLs and remove unlinked or invalid
indexed URLs
// set the identifierURLs map, so we can build search results
immediately; no problem if it contains URLs that were not indexed or are
replaced, we know these URLs should be added eventually
- OSMemoryBarrier();
- if (flags.shouldKeepRunning == 1) {
+ if ([self shouldKeepRunning]) {
[rwLock lockForWriting];
CFArrayApplyFunction((CFArrayRef)items, CFRangeMake(0,
totalObjectCount), addItemFunction, (void *)identifierURLs);
[rwLock unlock];
@@ -514,8 +612,7 @@
id anItem = nil;
// find URLs in the database that needs to be indexed, and URLs that
were indexeed but are not in the database anymore
- OSMemoryBarrier();
- while(flags.shouldKeepRunning == 1 && (anItem = [itemEnum
nextObject])) {
+ while([self shouldKeepRunning] && (anItem = [itemEnum nextObject])) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
@@ -549,12 +646,10 @@
}
[pool release];
- OSMemoryBarrier();
}
// remove URLs we could not find in the database
- OSMemoryBarrier();
- if (flags.shouldKeepRunning == 1 && [URLsToRemove count]) {
+ if ([self shouldKeepRunning] && [URLsToRemove count]) {
NSEnumerator *urlEnum = [URLsToRemove objectEnumerator];
NSURL *url;
while (url = [urlEnum nextObject])
@@ -571,8 +666,7 @@
}
// add items that were not yet indexed
- OSMemoryBarrier();
- if (flags.shouldKeepRunning == 1 && [items count]) {
+ if ([self shouldKeepRunning] && [items count]) {
[self indexFilesForItems:items numberPreviouslyIndexed:numberIndexed
totalCount:totalObjectCount];
}
@@ -591,13 +685,6 @@
// release at the end of this method, just before the thread exits
notificationThread = [[NSThread currentThread] retain];
- notificationPort = [[NSMachPort alloc] init];
- [notificationPort setDelegate:self];
- [[NSRunLoop currentRunLoop] addPort:notificationPort
forMode:NSDefaultRunLoopMode];
-
- notificationQueue = [[NSMutableArray alloc] initWithCapacity:5];
- noteLock = [[NSLock alloc] init];
-
[setupLock unlockWithCondition:INDEX_STARTUP_COMPLETE];
[setupLock lockWhenCondition:INDEX_THREAD_WORKING];
@@ -613,17 +700,25 @@
// run the current run loop until we get a cancel message, or else the
current thread/run loop will just go away when this function returns
@try{
- NSRunLoop *rl = [NSRunLoop currentRunLoop];
- BOOL keepRunning;
- NSDate *distantFuture = [NSDate distantFuture];
+ while ([self shouldKeepRunning]) {
+ // this blocks until a new note is available, or the index finishes
+ NSDictionary *note = [self newNotification];
+ if (note) {
+ NSString *name = [note valueForKey:@"name"];
+ NSArray *searchIndexInfo = [note
valueForKey:@"searchIndexInfo"];
+
+ // this is a background thread that can handle these
notifications
+ if ([name
isEqualToString:BDSKFileSearchIndexInfoChangedNotification])
+ [self processSearchIndexInfoChanged:searchIndexInfo];
+ else if ([name isEqualToString:BDSKDocAddItemNotification])
+ [self processDocAddItem:searchIndexInfo];
+ else if ([name isEqualToString:BDSKDocDelItemNotification])
+ [self processDocDelItem:searchIndexInfo];
+
+ [note release];
+ }
+ }
- do {
- [pool release];
- pool = [[NSAutoreleasePool alloc] init];
- // Running with beforeDate: distantFuture causes the runloop to
block indefinitely if shouldKeepRunning was set to 0 during the initial
indexing phase; invalidating and removing the port manually doesn't change
this. Hence, we need to check that flag before running the runloop, or use a
short limit date.
- OSMemoryBarrier();
- keepRunning = (flags.shouldKeepRunning == 1) && [rl
runMode:NSDefaultRunLoopMode beforeDate:distantFuture];
- } while(keepRunning);
}
@catch(id localException){
NSLog(@"Exception %@ raised in search index; exiting thread run
loop.", localException);
@@ -640,117 +735,8 @@
[notificationThread release];
notificationThread = nil;
- [notificationPort invalidate];
[setupLock unlockWithCondition:INDEX_THREAD_DONE];
}
}
-#pragma mark Change notification handling
-
-- (void)processNotification:(NSNotification *)note
-{
- BDSKASSERT([NSThread isMainThread]);
- // get the search index info, don't use the note because we don't want to
retain the pubs
- NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:[note
name], @"name",
- [[note userInfo]
valueForKeyPath:@"pubs.searchIndexInfo"], @"searchIndexInfo", nil];
- [noteLock lock];
- [notificationQueue addObject:info]; // Forward the notification to the
correct thread
- [noteLock unlock];
- [notificationPort sendBeforeDate:[NSDate date] components:nil from:nil
reserved:0];
-}
-
-- (void)handleDocAddItem:(NSArray *)searchIndexInfo
-{
- BDSKASSERT([[NSThread currentThread] isEqual:notificationThread]);
-
- BDSKPRECONDITION(searchIndexInfo);
-
- // this will update the delegate when all is complete
- [self indexFilesForItems:searchIndexInfo numberPreviouslyIndexed:0
totalCount:[searchIndexInfo count]];
-}
-
-- (void)handleDocDelItem:(NSArray *)searchIndexInfo
-{
- BDSKASSERT([[NSThread currentThread] isEqual:notificationThread]);
-
- NSEnumerator *itemEnumerator = [searchIndexInfo objectEnumerator];
- id anItem;
-
- NSURL *identifierURL = nil;
- NSSet *urlsToRemove;
-
- while (anItem = [itemEnumerator nextObject]) {
- identifierURL = [anItem valueForKey:@"identifierURL"];
-
- [rwLock lockForReading];
- urlsToRemove = [[[identifierURLs allKeysForObject:identifierURL] copy]
autorelease];
- [rwLock unlock];
-
- [self removeFileURLs:urlsToRemove forIdentifierURL:identifierURL];
- }
-
- if (0 == flags.updateScheduled)
- [self performSelectorOnMainThread:@selector(searchIndexDidUpdate)
withObject:nil waitUntilDone:NO];
-}
-
-- (void)handleSearchIndexInfoChanged:(NSArray *)searchIndexInfo
-{
- BDSKASSERT([[NSThread currentThread] isEqual:notificationThread]);
-
- NSDictionary *item = [searchIndexInfo lastObject];
- NSURL *identifierURL = [item objectForKey:@"identifierURL"];
-
- NSSet *newURLs = [[NSSet alloc] initWithArray:[item valueForKey:@"urls"]];
- NSMutableSet *removedURLs;
-
- [rwLock lockForReading];
- removedURLs = [[identifierURLs allKeysForObject:identifierURL]
mutableCopy];
- [rwLock unlock];
- [removedURLs minusSet:newURLs];
-
- if ([removedURLs count])
- [self removeFileURLs:removedURLs forIdentifierURL:identifierURL];
-
- if ([newURLs count])
- [self indexFileURLs:newURLs forIdentifierURL:identifierURL];
-
- [removedURLs release];
- [newURLs release];
-
- if (0 == flags.updateScheduled)
- [self performSelectorOnMainThread:@selector(searchIndexDidUpdate)
withObject:nil waitUntilDone:NO];
-}
-
-- (NSDictionary *)newNotification {
- NSDictionary *note = nil;
- [noteLock lock];
- if ([notificationQueue count]) {
- note = [[notificationQueue objectAtIndex:0] retain];
- [notificationQueue removeObjectAtIndex:0];
- }
- [noteLock unlock];
- return note;
-}
-
-- (void)handleMachMessage:(void *)msg
-{
- BDSKASSERT([NSThread isMainThread] == NO);
- NSDictionary *note;
-
- while (note = [self newNotification]) {
- NSString *name = [note valueForKey:@"name"];
- NSArray *searchIndexInfo = [note valueForKey:@"searchIndexInfo"];
- // this is a background thread that can handle these notifications
- if([name isEqualToString:BDSKFileSearchIndexInfoChangedNotification])
- [self handleSearchIndexInfoChanged:searchIndexInfo];
- else if([name isEqualToString:BDSKDocAddItemNotification])
- [self handleDocAddItem:searchIndexInfo];
- else if([name isEqualToString:BDSKDocDelItemNotification])
- [self handleDocDelItem:searchIndexInfo];
- else
- [NSException raise:NSInvalidArgumentException
format:@"notification %@ is not handled by %@", note, self];
- [note release];
- }
-}
-
@end
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
------------------------------------------------------------------------------
Crystal Reports - New Free Runtime and 30 Day Trial
Check out the new simplified licensign option that enables unlimited
royalty-free distribution of the report engine for externally facing
server and web deployment.
http://p.sf.net/sfu/businessobjects
_______________________________________________
Bibdesk-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bibdesk-commit