From 4bed0af868a76a3d82301a1733c6e0574dd9d0d4 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sun, 30 Oct 2022 16:48:59 -0700 Subject: [PATCH] Core Data Store: Handle concurrency properly All concurrency from other threads should pass through the viewContext's performBlock or performBlockAndWait functions, and no other way. So now, all access to Core Data is either happening on the main thread, or by using these code blocks, all of which will wait for their access to proceed. Signed-off-by: Christopher Snowhill --- Application/AppController.m | 9 - Playlist/PlaylistController.h | 2 - Playlist/PlaylistController.m | 164 ++++++------- Playlist/PlaylistEntry.m | 39 ++-- Playlist/PlaylistLoader.m | 44 ++-- Preferences/Preferences/PathSuggester.m | 6 - .../SandboxPathBehaviorController.m | 83 +++---- Spotlight/SpotlightPlaylistEntry.m | 80 +++---- Utils/SQLiteStore.m | 221 +++++++++--------- Utils/SandboxBroker.m | 158 ++++++------- 10 files changed, 361 insertions(+), 445 deletions(-) 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 {