diff --git a/Application/AppController.m b/Application/AppController.m index 7046de862..2291287f2 100644 --- a/Application/AppController.m +++ b/Application/AppController.m @@ -223,12 +223,7 @@ static AppController *kAppController = nil; request.predicate = predicate; NSError *error = nil; - [playlistController.persistentContainerLock lock]; NSArray *results = [playlistController.persistentContainer.viewContext executeFetchRequest:request error:&error]; - if(results) { - results = [results copy]; - } - [playlistController.persistentContainerLock unlock]; if(results && [results count] == 1) { PlaylistEntry *pe = results[0]; @@ -434,9 +429,7 @@ static AppController *kAppController = nil; for(PlaylistEntry *pe in playlistController.arrangedObjects) { if(pe.deLeted) { - [playlistController.persistentContainerLock lock]; [moc deleteObject:pe]; - [playlistController.persistentContainerLock unlock]; continue; } if([artLeftovers objectForKey:pe.artHash]) { @@ -444,11 +437,9 @@ static AppController *kAppController = nil; } } - [playlistController.persistentContainerLock lock]; for(NSString *key in artLeftovers) { [moc deleteObject:[artLeftovers objectForKey:key]]; } - [playlistController.persistentContainerLock unlock]; [playlistController commitPersistentStore]; diff --git a/Playlist/PlaylistController.h b/Playlist/PlaylistController.h index 0c659a53f..9ece4863f 100644 --- a/Playlist/PlaylistController.h +++ b/Playlist/PlaylistController.h @@ -68,7 +68,6 @@ typedef NS_ENUM(NSInteger, URLOrigin) { @property(retain) NSString *_Nullable totalTime; @property(retain) NSString *_Nullable currentStatus; -@property(strong, nonatomic, readonly) NSLock *_Nonnull persistentContainerLock; @property(strong, nonatomic, readonly) NSPersistentContainer *_Nonnull persistentContainer; @property(strong, nonatomic, readonly) NSMutableDictionary *_Nonnull persistentArtStorage; @@ -143,7 +142,6 @@ typedef NS_ENUM(NSInteger, URLOrigin) { - (void)readShuffleListFromDataStore; + (NSPersistentContainer *_Nonnull)sharedPersistentContainer; -+ (NSLock *_Nonnull)sharedPersistentContainerLock; // reload metadata of selection - (IBAction)reloadTags:(id _Nullable)sender; diff --git a/Playlist/PlaylistController.m b/Playlist/PlaylistController.m index a2a63bc73..9306f0419 100644 --- a/Playlist/PlaylistController.m +++ b/Playlist/PlaylistController.m @@ -30,8 +30,6 @@ extern BOOL kAppControllerShuttingDown; -NSLock *kPersistentContainerLock = nil; - NSPersistentContainer *kPersistentContainer = nil; @implementation PlaylistController @@ -127,10 +125,6 @@ static void *playlistControllerContext = &playlistControllerContext; }]; kPersistentContainer = self.persistentContainer; - - _persistentContainerLock = [[NSLock alloc] init]; - - kPersistentContainerLock = self.persistentContainerLock; self.persistentContainer.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; @@ -144,10 +138,6 @@ static void *playlistControllerContext = &playlistControllerContext; return kPersistentContainer; } -+ (NSLock *)sharedPersistentContainerLock { - return kPersistentContainerLock; -} - - (void)awakeFromNib { [super awakeFromNib]; @@ -251,63 +241,46 @@ static void *playlistControllerContext = &playlistControllerContext; } - (void)commitPersistentStore { - NSError *error = nil; - [self.persistentContainerLock lock]; - [self.persistentContainer.viewContext save:&error]; - [self.persistentContainerLock unlock]; - if(error) { - ALog(@"Error committing playlist storage: %@", [error localizedDescription]); - } + [self.persistentContainer.viewContext performBlockAndWait:^{ + NSError *error = nil; + [self.persistentContainer.viewContext save:&error]; + if(error) { + ALog(@"Error committing playlist storage: %@", [error localizedDescription]); + } + }]; } - (void)updatePlayCountForTrack:(PlaylistEntry *)pe { if(pe.countAdded) return; pe.countAdded = YES; - PlayCount *pc = pe.playCountItem; + __block PlayCount *pc = pe.playCountItem; if(pc) { - [self.persistentContainerLock lock]; - pc.count += 1; - pc.lastPlayed = [NSDate date]; - [self.persistentContainerLock unlock]; + [self.persistentContainer.viewContext performBlockAndWait:^{ + pc.count += 1; + pc.lastPlayed = [NSDate date]; + }]; } else { - [self.persistentContainerLock lock]; - pc = [NSEntityDescription insertNewObjectForEntityForName:@"PlayCount" inManagedObjectContext:self.persistentContainer.viewContext]; - pc.count = 1; - pc.firstSeen = pc.lastPlayed = [NSDate date]; - pc.album = pe.album; - pc.artist = pe.artist; - pc.title = pe.title; - pc.filename = pe.filenameFragment; - [self.persistentContainerLock unlock]; + [self.persistentContainer.viewContext performBlockAndWait:^{ + pc = [NSEntityDescription insertNewObjectForEntityForName:@"PlayCount" inManagedObjectContext:self.persistentContainer.viewContext]; + pc.count = 1; + pc.firstSeen = pc.lastPlayed = [NSDate date]; + pc.album = pe.album; + pc.artist = pe.artist; + pc.title = pe.title; + pc.filename = pe.filenameFragment; + }]; } [self commitPersistentStore]; } - (void)firstSawTrack:(PlaylistEntry *)pe { - PlayCount *pc = pe.playCountItem; + __block PlayCount *pc = pe.playCountItem; if(!pc) { - [self.persistentContainerLock lock]; - pc = [NSEntityDescription insertNewObjectForEntityForName:@"PlayCount" inManagedObjectContext:self.persistentContainer.viewContext]; - pc.count = 0; - pc.firstSeen = [NSDate date]; - pc.album = pe.album; - pc.artist = pe.artist; - pc.title = pe.title; - pc.filename = pe.filenameFragment; - [self.persistentContainerLock unlock]; - } -} - -- (void)ratingUpdatedWithEntry:(PlaylistEntry *)pe rating:(CGFloat)rating { - if(pe && !pe.deLeted) { - PlayCount *pc = pe.playCountItem; - - if(!pc) { - [self.persistentContainerLock lock]; + [self.persistentContainer.viewContext performBlockAndWait:^{ pc = [NSEntityDescription insertNewObjectForEntityForName:@"PlayCount" inManagedObjectContext:self.persistentContainer.viewContext]; pc.count = 0; pc.firstSeen = [NSDate date]; @@ -315,12 +288,29 @@ static void *playlistControllerContext = &playlistControllerContext; pc.artist = pe.artist; pc.title = pe.title; pc.filename = pe.filenameFragment; - [self.persistentContainerLock unlock]; + }]; + } +} + +- (void)ratingUpdatedWithEntry:(PlaylistEntry *)pe rating:(CGFloat)rating { + if(pe && !pe.deLeted) { + __block PlayCount *pc = pe.playCountItem; + + if(!pc) { + [self.persistentContainer.viewContext performBlockAndWait:^{ + pc = [NSEntityDescription insertNewObjectForEntityForName:@"PlayCount" inManagedObjectContext:self.persistentContainer.viewContext]; + pc.count = 0; + pc.firstSeen = [NSDate date]; + pc.album = pe.album; + pc.artist = pe.artist; + pc.title = pe.title; + pc.filename = pe.filenameFragment; + }]; } - [self.persistentContainerLock lock]; - pc.rating = rating; - [self.persistentContainerLock unlock]; + [self.persistentContainer.viewContext performBlockAndWait:^{ + pc.rating = rating; + }]; [self commitPersistentStore]; } @@ -330,7 +320,9 @@ static void *playlistControllerContext = &playlistControllerContext; PlayCount *pc = pe.playCountItem; if(pc) { - pc.count = 0; + [self.persistentContainer.viewContext performBlockAndWait:^{ + pc.count = 0; + }]; } } @@ -338,7 +330,9 @@ static void *playlistControllerContext = &playlistControllerContext; PlayCount *pc = pe.playCountItem; if(pc) { - pc.rating = 0; + [self.persistentContainer.viewContext performBlockAndWait:^{ + pc.rating = 0; + }]; } } @@ -1380,19 +1374,16 @@ static void *playlistControllerContext = &playlistControllerContext; request.predicate = predicate; request.sortDescriptors = @[sortDescriptor]; - NSError *error = nil; - [self.persistentContainerLock lock]; - NSArray *results = [self.persistentContainer.viewContext executeFetchRequest:request error:&error]; - if(results) { - results = [results copy]; - } - [self.persistentContainerLock unlock]; + [self.persistentContainer.viewContext performBlockAndWait:^{ + NSError *error = nil; + NSArray *results = [self.persistentContainer.viewContext executeFetchRequest:request error:&error]; - if(results && [results count] > 0) { - [queueList removeAllObjects]; - NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [results count])]; - [queueList insertObjects:results atIndexes:indexSet]; - } + if(results && [results count] > 0) { + [queueList removeAllObjects]; + NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [results count])]; + [queueList insertObjects:results atIndexes:indexSet]; + } + }]; } - (void)readShuffleListFromDataStore { @@ -1404,19 +1395,16 @@ static void *playlistControllerContext = &playlistControllerContext; request.predicate = predicate; request.sortDescriptors = @[sortDescriptor]; - NSError *error = nil; - [self.persistentContainerLock lock]; - NSArray *results = [self.persistentContainer.viewContext executeFetchRequest:request error:&error]; - if(results) { - results = [results copy]; - } - [self.persistentContainerLock unlock]; + [self.persistentContainer.viewContext performBlockAndWait:^{ + NSError *error = nil; + NSArray *results = [self.persistentContainer.viewContext executeFetchRequest:request error:&error]; - if(results && [results count] > 0) { - [shuffleList removeAllObjects]; - NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [results count])]; - [shuffleList insertObjects:results atIndexes:indexSet]; - } + if(results && [results count] > 0) { + [shuffleList removeAllObjects]; + NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [results count])]; + [shuffleList insertObjects:results atIndexes:indexSet]; + } + }]; } - (void)addShuffledListToFront { @@ -1946,16 +1934,18 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc } - (BOOL)pathSuggesterEmpty { - BOOL rval; + __block BOOL rval; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"]; - NSError *error = nil; - [self.persistentContainerLock lock]; - NSArray *results = [self.persistentContainer.viewContext executeFetchRequest:request error:&error]; - if(!results || [results count] < 1) rval = YES; - else rval = NO; - [self.persistentContainerLock unlock]; + [self.persistentContainer.viewContext performBlockAndWait:^{ + NSError *error = nil; + NSArray *results = [self.persistentContainer.viewContext executeFetchRequest:request error:&error]; + if(!results || [results count] < 1) + rval = YES; + else + rval = NO; + }]; return rval; } diff --git a/Playlist/PlaylistEntry.m b/Playlist/PlaylistEntry.m index 3199bb3c5..3b1a10d67 100644 --- a/Playlist/PlaylistEntry.m +++ b/Playlist/PlaylistEntry.m @@ -16,7 +16,6 @@ #import "SHA256Digest.h" #import "SecondsFormatter.h" -extern NSLock *kPersistentContainerLock; extern NSPersistentContainer *kPersistentContainer; extern NSMutableDictionary *kArtworkDictionary; @@ -364,11 +363,9 @@ extern NSMutableDictionary *kArtworkDictionary; self.artHash = imageCacheTag; if(![kArtworkDictionary objectForKey:imageCacheTag]) { - [kPersistentContainerLock lock]; AlbumArtwork *art = [NSEntityDescription insertNewObjectForEntityForName:@"AlbumArtwork" inManagedObjectContext:kPersistentContainer.viewContext]; art.artHash = imageCacheTag; art.artData = albumArtInternal; - [kPersistentContainerLock unlock]; [kArtworkDictionary setObject:art forKey:imageCacheTag]; } @@ -585,30 +582,30 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path) { NSCompoundPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[albumPredicate, artistPredicate, titlePredicate]]; - NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"PlayCount"]; - request.predicate = predicate; + __block PlayCount *item = nil; - NSError *error = nil; - [kPersistentContainerLock lock]; - NSArray *results = [kPersistentContainer.viewContext executeFetchRequest:request error:&error]; + [kPersistentContainer.viewContext performBlockAndWait:^{ + NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"PlayCount"]; + request.predicate = predicate; - if(!results || [results count] < 1) { - NSPredicate *filenamePredicate = [NSPredicate predicateWithFormat:@"filename == %@", self.filenameFragment]; + NSError *error = nil; + NSArray *results = [kPersistentContainer.viewContext executeFetchRequest:request error:&error]; - request = [NSFetchRequest fetchRequestWithEntityName:@"PlayCount"]; - request.predicate = filenamePredicate; + if(!results || [results count] < 1) { + NSPredicate *filenamePredicate = [NSPredicate predicateWithFormat:@"filename == %@", self.filenameFragment]; - results = [kPersistentContainer.viewContext executeFetchRequest:request error:&error]; - } - - if(results) { - results = [results copy]; - } - [kPersistentContainerLock unlock]; + request = [NSFetchRequest fetchRequestWithEntityName:@"PlayCount"]; + request.predicate = filenamePredicate; - if(!results || [results count] < 1) return nil; + results = [kPersistentContainer.viewContext executeFetchRequest:request error:&error]; + } - return results[0]; + if(!results || [results count] < 1) return; + + item = results[0]; + }]; + + return item; } @dynamic playCount; diff --git a/Playlist/PlaylistLoader.m b/Playlist/PlaylistLoader.m index 811e24532..65c77d010 100644 --- a/Playlist/PlaylistLoader.m +++ b/Playlist/PlaylistLoader.m @@ -565,15 +565,13 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc __block NSMutableArray *entries = [NSMutableArray arrayWithCapacity:count]; for(NSURL *url in validURLs) { __block PlaylistEntry *pe; - + dispatch_sync_reentrant(dispatch_get_main_queue(), ^{ - [self->playlistController.persistentContainerLock lock]; pe = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:self->playlistController.persistentContainer.viewContext]; pe.url = url; pe.index = index + i; pe.rawTitle = [[url path] lastPathComponent]; pe.queuePosition = -1; - [self->playlistController.persistentContainerLock unlock]; }); [entries addObject:pe]; @@ -589,14 +587,12 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc if(xmlData) { for(NSDictionary *entry in [xmlData objectForKey:@"entries"]) { __block PlaylistEntry *pe; - + dispatch_sync_reentrant(dispatch_get_main_queue(), ^{ - [self->playlistController.persistentContainerLock lock]; pe = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:self->playlistController.persistentContainer.viewContext]; [pe setValuesForKeysWithDictionary:entry]; pe.index = index + i; pe.queuePosition = -1; - [self->playlistController.persistentContainerLock unlock]; }); [entries addObject:pe]; @@ -791,25 +787,22 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path); NSCompoundPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[deletedPredicate, hasUrlPredicate]]; - NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"PlaylistEntry"]; - request.predicate = predicate; + [moc performBlockAndWait:^{ + NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"PlaylistEntry"]; + request.predicate = predicate; - NSError *error; - [playlistController.persistentContainerLock lock]; - NSArray *results = [moc executeFetchRequest:request error:&error]; - if(results) { - results = [results copy]; - } - [playlistController.persistentContainerLock unlock]; + NSError *error; + NSArray *results = [moc executeFetchRequest:request error:&error]; - if(results && [results count] > 0) { - for(PlaylistEntry *pe in results) { - NSMutableArray *entrySet = [uniquePathsEntries objectForKey:pe.urlString]; - if(entrySet) { - [entrySet addObject:pe]; + if(results && [results count] > 0) { + for(PlaylistEntry *pe in results) { + NSMutableArray *entrySet = [uniquePathsEntries objectForKey:pe.urlString]; + if(entrySet) { + [entrySet addObject:pe]; + } } } - } + }]; for(size_t i = 0, j = [outArray count]; i < j; i += 2) { __block NSString *entryKey = [outArray objectAtIndex:i]; @@ -927,10 +920,8 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path); NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"AlbumArtwork"]; NSError *error = nil; - [playlistController.persistentContainerLock lock]; NSArray *results = [moc executeFetchRequest:request error:&error]; if(!results) { - [playlistController.persistentContainerLock unlock]; ALog(@"Error fetching AlbumArtwork objects: %@\n%@", [error localizedDescription], [error userInfo]); abort(); } @@ -938,7 +929,6 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path); for(AlbumArtwork *art in results) { [kArtworkDictionary setObject:art forKey:art.artHash]; } - [playlistController.persistentContainerLock unlock]; request = [NSFetchRequest fetchRequestWithEntityName:@"PlaylistEntry"]; @@ -946,30 +936,24 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path); request.sortDescriptors = @[sortDescriptor]; - [playlistController.persistentContainerLock lock]; results = [moc executeFetchRequest:request error:&error]; if(!results) { - [playlistController.persistentContainerLock unlock]; ALog(@"Error fetching PlaylistEntry objects: %@\n%@", [error localizedDescription], [error userInfo]); abort(); } if([results count] == 0) { - [playlistController.persistentContainerLock unlock]; return NO; } NSMutableArray *resultsCopy = [results mutableCopy]; - [playlistController.persistentContainerLock unlock]; NSMutableIndexSet *pruneSet = [[NSMutableIndexSet alloc] init]; NSUInteger index = 0; for(PlaylistEntry *pe in resultsCopy) { if(pe.deLeted || !pe.urlString || [pe.urlString length] < 1) { [pruneSet addIndex:index]; - [playlistController.persistentContainerLock lock]; [moc deleteObject:pe]; - [playlistController.persistentContainerLock unlock]; } ++index; } diff --git a/Preferences/Preferences/PathSuggester.m b/Preferences/Preferences/PathSuggester.m index 128d20cec..08e556c87 100644 --- a/Preferences/Preferences/PathSuggester.m +++ b/Preferences/Preferences/PathSuggester.m @@ -55,7 +55,6 @@ [pathsList removeObjects:[pathsList arrangedObjects]]; - NSLock *lock = [NSClassFromString(@"PlaylistController") sharedPersistentContainerLock]; NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer]; NSPredicate *hasUrlPredicate = [NSPredicate predicateWithFormat:@"urlString != nil && urlString != %@", @""]; @@ -67,12 +66,7 @@ request.predicate = predicate; NSError *error = nil; - [lock lock]; NSArray *results = [pc.viewContext executeFetchRequest:request error:&error]; - if(results) { - results = [results copy]; - } - [lock unlock]; if(!results || [results count] < 1) return; diff --git a/Preferences/Preferences/SandboxPathBehaviorController.m b/Preferences/Preferences/SandboxPathBehaviorController.m index d9310f151..498ab0b8c 100644 --- a/Preferences/Preferences/SandboxPathBehaviorController.m +++ b/Preferences/Preferences/SandboxPathBehaviorController.m @@ -36,18 +36,12 @@ NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path" ascending:YES]; [self setSortDescriptors:@[sortDescriptor]]; - NSLock *lock = [NSClassFromString(@"PlaylistController") sharedPersistentContainerLock]; NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer]; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"]; NSError *error = nil; - [lock lock]; NSArray *results = [pc.viewContext executeFetchRequest:request error:&error]; - if(results) { - results = [results copy]; - } - [lock unlock]; if(results && [results count] > 0) { for(SandboxToken *token in results) { @@ -67,31 +61,31 @@ return; } - NSLock *lock = [NSClassFromString(@"PlaylistController") sharedPersistentContainerLock]; NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer]; - [lock lock]; - SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext]; + [pc.viewContext performBlockAndWait:^{ + SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext]; - if(token) { - token.path = [url path]; - token.bookmark = bookmark; - [pc.viewContext save:&err]; - [lock unlock]; - if(err) { - ALog(@"Error saving bookmark: %@", [err localizedDescription]); - } else { - [self addObject:@{ @"path": [url path], @"valid": NSLocalizedPrefString(@"ValidYes"), @"isFolder": @(token.folder), @"token": token }]; - [NSClassFromString(@"SandboxBroker") cleanupFolderAccess]; - [self refresh]; + if(token) { + NSError *err = nil; + token.path = [url path]; + token.bookmark = bookmark; + [pc.viewContext save:&err]; + if(err) { + ALog(@"Error saving bookmark: %@", [err localizedDescription]); + } else { + [self addObject:@{@"path": [url path], + @"valid": NSLocalizedPrefString(@"ValidYes"), + @"isFolder": @(token.folder), + @"token": token}]; + [NSClassFromString(@"SandboxBroker") cleanupFolderAccess]; + [self refresh]; + } } - } else { - [lock unlock]; - } + }]; } - (void)removeToken:(id)token { - NSLock *lock = [NSClassFromString(@"PlaylistController") sharedPersistentContainerLock]; NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer]; NSArray *objects = [[self arrangedObjects] copy]; @@ -101,46 +95,45 @@ for(NSDictionary *obj in objects) { if([[obj objectForKey:@"token"] isEqualTo:token]) { [self removeObject:obj]; - [lock lock]; - [pc.viewContext deleteObject:token]; - [lock unlock]; + [pc.viewContext performBlockAndWait:^{ + [pc.viewContext deleteObject:token]; + }]; updated = YES; break; } } if(updated) { - NSError *error; - [lock lock]; - [pc.viewContext save:&error]; - [lock unlock]; - if(error) { - ALog(@"Error deleting bookmark: %@", [error localizedDescription]); - } + [pc.viewContext performBlockAndWait:^{ + NSError *error; + [pc.viewContext save:&error]; + if(error) { + ALog(@"Error deleting bookmark: %@", [error localizedDescription]); + } + }]; } } - (void)removeStaleEntries { BOOL updated = NO; - NSLock *lock = [NSClassFromString(@"PlaylistController") sharedPersistentContainerLock]; NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer]; for(NSDictionary *entry in [[self arrangedObjects] copy]) { if([[entry objectForKey:@"valid"] isEqualToString:NSLocalizedPrefString(@"ValidNo")]) { [self removeObject:entry]; - [lock lock]; - [pc.viewContext deleteObject:[entry objectForKey:@"token"]]; - [lock unlock]; + [pc.viewContext performBlockAndWait:^{ + [pc.viewContext deleteObject:[entry objectForKey:@"token"]]; + }]; updated = YES; } } if(updated) { - NSError *error; - [lock lock]; - [pc.viewContext save:&error]; - [lock unlock]; - if(error) { - ALog(@"Error saving after removing stale bookmarks: %@", [error localizedDescription]); - } + [pc.viewContext performBlockAndWait:^{ + NSError *error; + [pc.viewContext save:&error]; + if(error) { + ALog(@"Error saving after removing stale bookmarks: %@", [error localizedDescription]); + } + }]; } } diff --git a/Spotlight/SpotlightPlaylistEntry.m b/Spotlight/SpotlightPlaylistEntry.m index 33f63d8e1..0be53707a 100644 --- a/Spotlight/SpotlightPlaylistEntry.m +++ b/Spotlight/SpotlightPlaylistEntry.m @@ -13,7 +13,6 @@ // with format (entryKey, transformerName) static NSDictionary *importKeys; -extern NSLock *kPersistentContainerLock; extern NSPersistentContainer *kPersistentContainer; @implementation SpotlightPlaylistEntry @@ -38,53 +37,54 @@ extern NSPersistentContainer *kPersistentContainer; } + (PlaylistEntry *)playlistEntryWithMetadataItem:(NSMetadataItem *)metadataItem { - [kPersistentContainerLock lock]; - PlaylistEntry *entry = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:kPersistentContainer.viewContext]; + __block PlaylistEntry *entry = nil; + [kPersistentContainer.viewContext performBlockAndWait:^{ + entry = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:kPersistentContainer.viewContext]; - entry.deLeted = YES; + entry.deLeted = YES; - NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; - // loop through the keys we want to extract - for(NSString *mdKey in importKeys) { - if(![metadataItem valueForAttribute:mdKey]) continue; - id importTarget = [importKeys objectForKey:mdKey]; - // Just copy the object from metadata - if([importTarget isKindOfClass:[NSString class]]) { - if([importTarget isEqualToString:@"length"]) { - // fake it - NSNumber *number = [metadataItem valueForAttribute:mdKey]; - [dict setValue:@(44100.0) forKey:@"samplerate"]; - [dict setValue:@(44100.0 * [number doubleValue]) forKey:@"totalFrames"]; - } else { - [dict setValue:[metadataItem valueForAttribute:mdKey] - forKey:importTarget]; + // loop through the keys we want to extract + for(NSString *mdKey in importKeys) { + if(![metadataItem valueForAttribute:mdKey]) continue; + id importTarget = [importKeys objectForKey:mdKey]; + // Just copy the object from metadata + if([importTarget isKindOfClass:[NSString class]]) { + if([importTarget isEqualToString:@"length"]) { + // fake it + NSNumber *number = [metadataItem valueForAttribute:mdKey]; + [dict setValue:@(44100.0) forKey:@"samplerate"]; + [dict setValue:@(44100.0 * [number doubleValue]) forKey:@"totalFrames"]; + } else { + [dict setValue:[metadataItem valueForAttribute:mdKey] + forKey:importTarget]; + } + } + // Transform the value in metadata before copying it in + else if([importTarget isKindOfClass:[NSArray class]]) { + NSString *importKey = [importTarget objectAtIndex:0]; + NSValueTransformer *transformer = + [NSValueTransformer valueTransformerForName:[importTarget objectAtIndex:1]]; + id transformedValue = [transformer transformedValue: + [metadataItem valueForAttribute:mdKey]]; + [dict setValue:transformedValue forKey:importKey]; + } + // The importKeys dictionary contains something strange... + else { + NSString *errString = + [NSString stringWithFormat:@"ERROR: Could not import key %@", mdKey]; + NSAssert(NO, errString); } } - // Transform the value in metadata before copying it in - else if([importTarget isKindOfClass:[NSArray class]]) { - NSString *importKey = [importTarget objectAtIndex:0]; - NSValueTransformer *transformer = - [NSValueTransformer valueTransformerForName:[importTarget objectAtIndex:1]]; - id transformedValue = [transformer transformedValue: - [metadataItem valueForAttribute:mdKey]]; - [dict setValue:transformedValue forKey:importKey]; - } - // The importKeys dictionary contains something strange... - else { - NSString *errString = - [NSString stringWithFormat:@"ERROR: Could not import key %@", mdKey]; - NSAssert(NO, errString); - } - } - NSURL *url = [dict objectForKey:@"url"]; - [dict removeObjectForKey:@"url"]; + NSURL *url = [dict objectForKey:@"url"]; + [dict removeObjectForKey:@"url"]; - entry.url = url; + entry.url = url; - [entry setMetadata:dict]; - [kPersistentContainerLock unlock]; + [entry setMetadata:dict]; + }]; return entry; } diff --git a/Utils/SQLiteStore.m b/Utils/SQLiteStore.m index bc7c2ff3c..9d640641a 100644 --- a/Utils/SQLiteStore.m +++ b/Utils/SQLiteStore.m @@ -14,7 +14,6 @@ #import "SHA256Digest.h" -extern NSLock *kPersistentContainerLock; extern NSPersistentContainer *kPersistentContainer; NSString *getDatabasePath(void) { @@ -1444,114 +1443,112 @@ static SQLiteStore *g_sharedStore = nil; #endif - (PlaylistEntry *_Nonnull)getTrack:(int64_t)trackId { - [kPersistentContainerLock lock]; - PlaylistEntry *entry = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:kPersistentContainer.viewContext]; + __block PlaylistEntry *entry = nil; + [kPersistentContainer.viewContext performBlockAndWait:^{ + entry = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:kPersistentContainer.viewContext]; - if(trackId < 0) { - entry.error = YES; - entry.errorMessage = NSLocalizedString(@"ErrorInvalidTrackId", @""); - entry.deLeted = YES; - [kPersistentContainerLock unlock]; - return entry; - } + if(trackId < 0) { + entry.error = YES; + entry.errorMessage = NSLocalizedString(@"ErrorInvalidTrackId", @""); + entry.deLeted = YES; + return; + } - sqlite3_stmt *st = stmt[stmt_select_track_data]; + sqlite3_stmt *st = stmt[stmt_select_track_data]; - if(sqlite3_reset(st) || - sqlite3_bind_int64(st, select_track_data_in_id, trackId)) { - entry.error = YES; - entry.errorMessage = NSLocalizedString(@"ErrorSqliteProblem", @""); - entry.deLeted = YES; - [kPersistentContainerLock unlock]; - return entry; - } + if(sqlite3_reset(st) || + sqlite3_bind_int64(st, select_track_data_in_id, trackId)) { + entry.error = YES; + entry.errorMessage = NSLocalizedString(@"ErrorSqliteProblem", @""); + entry.deLeted = YES; + return; + } - int rc = sqlite3_step(st); + int rc = sqlite3_step(st); + + if(rc != SQLITE_ROW && rc != SQLITE_DONE) { + sqlite3_reset(st); + entry.error = YES; + entry.errorMessage = NSLocalizedString(@"ErrorSqliteProblem", @""); + entry.deLeted = YES; + return; + } + + if(rc == SQLITE_ROW) { + int64_t urlId = sqlite3_column_int64(st, select_track_data_out_url_id); + int64_t artId = sqlite3_column_int64(st, select_track_data_out_art_id); + int64_t albumId = sqlite3_column_int64(st, select_track_data_out_album_id); + int64_t albumartistId = sqlite3_column_int64(st, select_track_data_out_albumartist_id); + int64_t artistId = sqlite3_column_int64(st, select_track_data_out_artist_id); + int64_t titleId = sqlite3_column_int64(st, select_track_data_out_title_id); + int64_t genreId = sqlite3_column_int64(st, select_track_data_out_genre_id); + int64_t codecId = sqlite3_column_int64(st, select_track_data_out_codec_id); + int64_t cuesheetId = sqlite3_column_int64(st, select_track_data_out_cuesheet_id); + int64_t encodingId = sqlite3_column_int64(st, select_track_data_out_encoding_id); + int64_t trackNr = sqlite3_column_int64(st, select_track_data_out_track); + int64_t year = sqlite3_column_int64(st, select_track_data_out_year); + int64_t unsignedFmt = sqlite3_column_int64(st, select_track_data_out_unsigned); + int64_t bitrate = sqlite3_column_int64(st, select_track_data_out_bitrate); + double samplerate = sqlite3_column_double(st, select_track_data_out_samplerate); + int64_t bitspersample = sqlite3_column_int64(st, select_track_data_out_bitspersample); + int64_t channels = sqlite3_column_int64(st, select_track_data_out_channels); + int64_t channelConfig = sqlite3_column_int64(st, select_track_data_out_channelconfig); + int64_t endianId = sqlite3_column_int64(st, select_track_data_out_endian_id); + int64_t floatingpoint = sqlite3_column_int64(st, select_track_data_out_floatingpoint); + int64_t totalframes = sqlite3_column_int64(st, select_track_data_out_totalframes); + int64_t metadataloaded = sqlite3_column_int64(st, select_track_data_out_metadataloaded); + int64_t seekable = sqlite3_column_int64(st, select_track_data_out_seekable); + double volume = sqlite3_column_double(st, select_track_data_out_volume); + double replaygainalbumgain = sqlite3_column_double(st, select_track_data_out_replaygainalbumgain); + double replaygainalbumpeak = sqlite3_column_double(st, select_track_data_out_replaygainalbumpeak); + double replaygaintrackgain = sqlite3_column_double(st, select_track_data_out_replaygaintrackgain); + double replaygaintrackpeak = sqlite3_column_double(st, select_track_data_out_replaygaintrackpeak); + + uint64_t discNr = ((uint64_t)trackNr) >> 32; + trackNr &= (1UL << 32) - 1; + + entry.url = urlForPath([self getString:urlId]); + + entry.album = [self getString:albumId]; + entry.albumartist = [self getString:albumartistId]; + entry.artist = [self getString:artistId]; + entry.rawTitle = [self getString:titleId]; + entry.genre = [self getString:genreId]; + entry.codec = [self getString:codecId]; + entry.cuesheet = [self getString:cuesheetId]; + entry.encoding = [self getString:encodingId]; + entry.track = (int32_t)trackNr; + entry.disc = (int32_t)discNr; + entry.year = (int32_t)year; + entry.unSigned = !!unsignedFmt; + entry.bitrate = (int32_t)bitrate; + entry.sampleRate = samplerate; + entry.bitsPerSample = (int32_t)bitspersample; + entry.channels = (int32_t)channels; + entry.channelConfig = (uint32_t)channelConfig; + entry.endian = [self getString:endianId]; + entry.floatingPoint = !!floatingpoint; + entry.totalFrames = totalframes; + entry.seekable = !!seekable; + entry.volume = volume; + entry.replayGainAlbumGain = replaygainalbumgain; + entry.replayGainAlbumPeak = replaygainalbumpeak; + entry.replayGainTrackGain = replaygaintrackgain; + entry.replayGainTrackPeak = replaygaintrackpeak; + + entry.albumArtInternal = [self getArt:artId]; + + entry.metadataLoaded = !!metadataloaded; + + entry.dbIndex = trackId; + } else { + entry.error = YES; + entry.errorMessage = NSLocalizedString(@"ErrorTrackMissing", @""); + entry.deLeted = YES; + } - if(rc != SQLITE_ROW && rc != SQLITE_DONE) { sqlite3_reset(st); - entry.error = YES; - entry.errorMessage = NSLocalizedString(@"ErrorSqliteProblem", @""); - entry.deLeted = YES; - [kPersistentContainerLock unlock]; - return entry; - } - - if(rc == SQLITE_ROW) { - int64_t urlId = sqlite3_column_int64(st, select_track_data_out_url_id); - int64_t artId = sqlite3_column_int64(st, select_track_data_out_art_id); - int64_t albumId = sqlite3_column_int64(st, select_track_data_out_album_id); - int64_t albumartistId = sqlite3_column_int64(st, select_track_data_out_albumartist_id); - int64_t artistId = sqlite3_column_int64(st, select_track_data_out_artist_id); - int64_t titleId = sqlite3_column_int64(st, select_track_data_out_title_id); - int64_t genreId = sqlite3_column_int64(st, select_track_data_out_genre_id); - int64_t codecId = sqlite3_column_int64(st, select_track_data_out_codec_id); - int64_t cuesheetId = sqlite3_column_int64(st, select_track_data_out_cuesheet_id); - int64_t encodingId = sqlite3_column_int64(st, select_track_data_out_encoding_id); - int64_t trackNr = sqlite3_column_int64(st, select_track_data_out_track); - int64_t year = sqlite3_column_int64(st, select_track_data_out_year); - int64_t unsignedFmt = sqlite3_column_int64(st, select_track_data_out_unsigned); - int64_t bitrate = sqlite3_column_int64(st, select_track_data_out_bitrate); - double samplerate = sqlite3_column_double(st, select_track_data_out_samplerate); - int64_t bitspersample = sqlite3_column_int64(st, select_track_data_out_bitspersample); - int64_t channels = sqlite3_column_int64(st, select_track_data_out_channels); - int64_t channelConfig = sqlite3_column_int64(st, select_track_data_out_channelconfig); - int64_t endianId = sqlite3_column_int64(st, select_track_data_out_endian_id); - int64_t floatingpoint = sqlite3_column_int64(st, select_track_data_out_floatingpoint); - int64_t totalframes = sqlite3_column_int64(st, select_track_data_out_totalframes); - int64_t metadataloaded = sqlite3_column_int64(st, select_track_data_out_metadataloaded); - int64_t seekable = sqlite3_column_int64(st, select_track_data_out_seekable); - double volume = sqlite3_column_double(st, select_track_data_out_volume); - double replaygainalbumgain = sqlite3_column_double(st, select_track_data_out_replaygainalbumgain); - double replaygainalbumpeak = sqlite3_column_double(st, select_track_data_out_replaygainalbumpeak); - double replaygaintrackgain = sqlite3_column_double(st, select_track_data_out_replaygaintrackgain); - double replaygaintrackpeak = sqlite3_column_double(st, select_track_data_out_replaygaintrackpeak); - - uint64_t discNr = ((uint64_t)trackNr) >> 32; - trackNr &= (1UL << 32) - 1; - - entry.url = urlForPath([self getString:urlId]); - - entry.album = [self getString:albumId]; - entry.albumartist = [self getString:albumartistId]; - entry.artist = [self getString:artistId]; - entry.rawTitle = [self getString:titleId]; - entry.genre = [self getString:genreId]; - entry.codec = [self getString:codecId]; - entry.cuesheet = [self getString:cuesheetId]; - entry.encoding = [self getString:encodingId]; - entry.track = (int32_t)trackNr; - entry.disc = (int32_t)discNr; - entry.year = (int32_t)year; - entry.unSigned = !!unsignedFmt; - entry.bitrate = (int32_t)bitrate; - entry.sampleRate = samplerate; - entry.bitsPerSample = (int32_t)bitspersample; - entry.channels = (int32_t)channels; - entry.channelConfig = (uint32_t)channelConfig; - entry.endian = [self getString:endianId]; - entry.floatingPoint = !!floatingpoint; - entry.totalFrames = totalframes; - entry.seekable = !!seekable; - entry.volume = volume; - entry.replayGainAlbumGain = replaygainalbumgain; - entry.replayGainAlbumPeak = replaygainalbumpeak; - entry.replayGainTrackGain = replaygaintrackgain; - entry.replayGainTrackPeak = replaygaintrackpeak; - - entry.albumArtInternal = [self getArt:artId]; - - entry.metadataLoaded = !!metadataloaded; - - entry.dbIndex = trackId; - } else { - entry.error = YES; - entry.errorMessage = NSLocalizedString(@"ErrorTrackMissing", @""); - entry.deLeted = YES; - } - [kPersistentContainerLock unlock]; - - sqlite3_reset(st); + }]; return entry; } @@ -1846,7 +1843,7 @@ static SQLiteStore *g_sharedStore = nil; } - (PlaylistEntry *)playlistGetItem:(int64_t)index { - PlaylistEntry *entry = nil; + __block PlaylistEntry *entry = nil; sqlite3_stmt *st = stmt[stmt_select_playlist]; @@ -1866,15 +1863,15 @@ static SQLiteStore *g_sharedStore = nil; int64_t entryId = sqlite3_column_int64(st, select_playlist_out_entry_id); entry = [self getTrack:trackId]; if(!entry.deLeted && !entry.error) { - [kPersistentContainerLock lock]; - entry.index = index; - entry.entryId = entryId; - [kPersistentContainerLock unlock]; + [kPersistentContainer.viewContext performBlockAndWait:^{ + entry.index = index; + entry.entryId = entryId; + }]; } else { - [kPersistentContainerLock lock]; - [kPersistentContainer.viewContext deleteObject:entry]; - [kPersistentContainerLock unlock]; - entry = nil; + [kPersistentContainer.viewContext performBlockAndWait:^{ + [kPersistentContainer.viewContext deleteObject:entry]; + entry = nil; + }]; } } diff --git a/Utils/SandboxBroker.m b/Utils/SandboxBroker.m index 4a60c89ad..f7cde5beb 100644 --- a/Utils/SandboxBroker.m +++ b/Utils/SandboxBroker.m @@ -92,10 +92,6 @@ static SandboxBroker *kSharedSandboxBroker = nil; return kSharedSandboxBroker; } -+ (NSLock *)sharedPersistentContainerLock { - return [NSClassFromString(@"PlaylistController") sharedPersistentContainerLock]; -} - + (NSPersistentContainer *)sharedPersistentContainer { return [NSClassFromString(@"PlaylistController") sharedPersistentContainer]; } @@ -151,57 +147,49 @@ static SandboxBroker *kSharedSandboxBroker = nil; } - (SandboxEntry *)recursivePathTest:(NSURL *)url { - SandboxEntry *ret = nil; + __block SandboxEntry *ret = nil; - NSLock *lock = [SandboxBroker sharedPersistentContainerLock]; NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer]; NSPredicate *folderPredicate = [NSPredicate predicateWithFormat:@"folder == NO"]; NSPredicate *filePredicate = [NSPredicate predicateWithFormat:@"path == %@", [url path]]; - NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[folderPredicate, filePredicate]]; - NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"]; - request.predicate = predicate; - - NSError *error = nil; - [lock lock]; - NSArray *results = [pc.viewContext executeFetchRequest:request error:&error]; - if(results) { - results = [results copy]; - } - [lock unlock]; - if(results && [results count] > 0) { - ret = [[SandboxEntry alloc] initWithToken:results[0]]; - } + [pc.viewContext performBlockAndWait:^{ + NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[folderPredicate, filePredicate]]; - if(!ret) { - predicate = [NSPredicate predicateWithFormat:@"folder == YES"]; - - NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path.length" ascending:NO]; - - request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"]; - request.sortDescriptors = @[sortDescriptor]; + NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"]; request.predicate = predicate; - error = nil; - [lock lock]; - results = [pc.viewContext executeFetchRequest:request error:&error]; - if(results) { - results = [results copy]; - } - [lock unlock]; - + NSError *error = nil; + NSArray *results = [pc.viewContext executeFetchRequest:request error:&error]; if(results && [results count] > 0) { - for(SandboxToken *token in results) { - if(token.path && [SandboxBroker isPath:url aSubdirectoryOf:[NSURL fileURLWithPath:token.path]]) { - SandboxEntry *entry = [[SandboxEntry alloc] initWithToken:token]; + ret = [[SandboxEntry alloc] initWithToken:results[0]]; + } - ret = entry; - break; + if(!ret) { + predicate = [NSPredicate predicateWithFormat:@"folder == YES"]; + + NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path.length" ascending:NO]; + + request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"]; + request.sortDescriptors = @[sortDescriptor]; + request.predicate = predicate; + + error = nil; + results = [pc.viewContext executeFetchRequest:request error:&error]; + + if(results && [results count] > 0) { + for(SandboxToken *token in results) { + if(token.path && [SandboxBroker isPath:url aSubdirectoryOf:[NSURL fileURLWithPath:token.path]]) { + SandboxEntry *entry = [[SandboxEntry alloc] initWithToken:token]; + + ret = entry; + break; + } } } } - } + }]; if(ret) { BOOL isStale; @@ -212,7 +200,9 @@ static SandboxBroker *kSharedSandboxBroker = nil; return nil; } - ret.secureUrl = secureUrl; + [pc.viewContext performBlockAndWait:^{ + ret.secureUrl = secureUrl; + }]; return ret; } @@ -253,22 +243,17 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc return; } - dispatch_sync_reentrant(dispatch_get_main_queue(), ^{ - NSLock *lock = [SandboxBroker sharedPersistentContainerLock]; - NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer]; + NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer]; - [lock lock]; + [pc.viewContext performBlockAndWait:^{ SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext]; if(token) { token.path = [folderUrl path]; token.bookmark = bookmark; - [lock unlock]; [SandboxBroker cleanupFolderAccess]; - } else { - [lock unlock]; } - }); + }]; } } } @@ -303,11 +288,9 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc return; } - NSLock *lock = [SandboxBroker sharedPersistentContainerLock]; NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer]; - dispatch_sync_reentrant(dispatch_get_main_queue(), ^{ - [lock lock]; + [pc.viewContext performBlockAndWait:^{ SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext]; if(token) { @@ -315,8 +298,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc token.bookmark = bookmark; token.folder = NO; } - [lock unlock]; - }); + }]; } } } @@ -372,19 +354,14 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc return; } - NSLock *lock = [SandboxBroker sharedPersistentContainerLock]; NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer]; - [lock lock]; SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext]; if(token) { token.path = [folderUrl path]; token.bookmark = bookmark; - [lock unlock]; [SandboxBroker cleanupFolderAccess]; - } else { - [lock unlock]; } } }); @@ -393,52 +370,47 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc } + (void)cleanupFolderAccess { - NSLock *lock = [SandboxBroker sharedPersistentContainerLock]; NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer]; NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path.length" ascending:YES]; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"]; request.sortDescriptors = @[sortDescriptor]; - NSError *error = nil; - NSMutableArray *resultsCopy = nil; - [lock lock]; - NSArray *results = [pc.viewContext executeFetchRequest:request error:&error]; - if(results) { - resultsCopy = [results mutableCopy]; - } - [lock unlock]; + [pc.viewContext performBlockAndWait:^{ + NSError *error = nil; + NSArray *results = [pc.viewContext executeFetchRequest:request error:&error]; + NSMutableArray *resultsCopy = nil; + if(results) { + resultsCopy = [results mutableCopy]; + } - BOOL isUpdated = NO; + BOOL isUpdated = NO; - if(resultsCopy && [resultsCopy count]) { - for(NSUInteger i = 0; i < [resultsCopy count] - 1; ++i) { - SandboxToken *token = resultsCopy[i]; - NSURL *url = [NSURL fileURLWithPath:token.path]; - for(NSUInteger j = i + 1; j < [resultsCopy count];) { - SandboxToken *compareToken = resultsCopy[j]; - if([SandboxBroker isPath:[NSURL fileURLWithPath:compareToken.path] aSubdirectoryOf:url]) { - [lock lock]; - [pc.viewContext deleteObject:compareToken]; - [lock unlock]; - isUpdated = YES; - [resultsCopy removeObjectAtIndex:j]; - } else { - ++j; + if(resultsCopy && [resultsCopy count]) { + for(NSUInteger i = 0; i < [resultsCopy count] - 1; ++i) { + SandboxToken *token = resultsCopy[i]; + NSURL *url = [NSURL fileURLWithPath:token.path]; + for(NSUInteger j = i + 1; j < [resultsCopy count];) { + SandboxToken *compareToken = resultsCopy[j]; + if([SandboxBroker isPath:[NSURL fileURLWithPath:compareToken.path] aSubdirectoryOf:url]) { + [pc.viewContext deleteObject:compareToken]; + isUpdated = YES; + [resultsCopy removeObjectAtIndex:j]; + } else { + ++j; + } } } } - } - if(isUpdated) { - NSError *error; - [lock lock]; - [pc.viewContext save:&error]; - [lock unlock]; - if(error) { - ALog(@"Error saving data: %@", [error localizedDescription]); + if(isUpdated) { + NSError *error; + [pc.viewContext save:&error]; + if(error) { + ALog(@"Error saving data: %@", [error localizedDescription]); + } } - } + }]; } - (const void *)beginFolderAccess:(NSURL *)fileUrl {