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 <kode54@gmail.com>
main
Christopher Snowhill 2022-10-30 16:48:59 -07:00
parent e3ae28369f
commit 4bed0af868
10 changed files with 361 additions and 445 deletions

View File

@ -223,12 +223,7 @@ static AppController *kAppController = nil;
request.predicate = predicate; request.predicate = predicate;
NSError *error = nil; NSError *error = nil;
[playlistController.persistentContainerLock lock];
NSArray *results = [playlistController.persistentContainer.viewContext executeFetchRequest:request error:&error]; NSArray *results = [playlistController.persistentContainer.viewContext executeFetchRequest:request error:&error];
if(results) {
results = [results copy];
}
[playlistController.persistentContainerLock unlock];
if(results && [results count] == 1) { if(results && [results count] == 1) {
PlaylistEntry *pe = results[0]; PlaylistEntry *pe = results[0];
@ -434,9 +429,7 @@ static AppController *kAppController = nil;
for(PlaylistEntry *pe in playlistController.arrangedObjects) { for(PlaylistEntry *pe in playlistController.arrangedObjects) {
if(pe.deLeted) { if(pe.deLeted) {
[playlistController.persistentContainerLock lock];
[moc deleteObject:pe]; [moc deleteObject:pe];
[playlistController.persistentContainerLock unlock];
continue; continue;
} }
if([artLeftovers objectForKey:pe.artHash]) { if([artLeftovers objectForKey:pe.artHash]) {
@ -444,11 +437,9 @@ static AppController *kAppController = nil;
} }
} }
[playlistController.persistentContainerLock lock];
for(NSString *key in artLeftovers) { for(NSString *key in artLeftovers) {
[moc deleteObject:[artLeftovers objectForKey:key]]; [moc deleteObject:[artLeftovers objectForKey:key]];
} }
[playlistController.persistentContainerLock unlock];
[playlistController commitPersistentStore]; [playlistController commitPersistentStore];

View File

@ -68,7 +68,6 @@ typedef NS_ENUM(NSInteger, URLOrigin) {
@property(retain) NSString *_Nullable totalTime; @property(retain) NSString *_Nullable totalTime;
@property(retain) NSString *_Nullable currentStatus; @property(retain) NSString *_Nullable currentStatus;
@property(strong, nonatomic, readonly) NSLock *_Nonnull persistentContainerLock;
@property(strong, nonatomic, readonly) NSPersistentContainer *_Nonnull persistentContainer; @property(strong, nonatomic, readonly) NSPersistentContainer *_Nonnull persistentContainer;
@property(strong, nonatomic, readonly) NSMutableDictionary<NSString *, AlbumArtwork *> *_Nonnull persistentArtStorage; @property(strong, nonatomic, readonly) NSMutableDictionary<NSString *, AlbumArtwork *> *_Nonnull persistentArtStorage;
@ -143,7 +142,6 @@ typedef NS_ENUM(NSInteger, URLOrigin) {
- (void)readShuffleListFromDataStore; - (void)readShuffleListFromDataStore;
+ (NSPersistentContainer *_Nonnull)sharedPersistentContainer; + (NSPersistentContainer *_Nonnull)sharedPersistentContainer;
+ (NSLock *_Nonnull)sharedPersistentContainerLock;
// reload metadata of selection // reload metadata of selection
- (IBAction)reloadTags:(id _Nullable)sender; - (IBAction)reloadTags:(id _Nullable)sender;

View File

@ -30,8 +30,6 @@
extern BOOL kAppControllerShuttingDown; extern BOOL kAppControllerShuttingDown;
NSLock *kPersistentContainerLock = nil;
NSPersistentContainer *kPersistentContainer = nil; NSPersistentContainer *kPersistentContainer = nil;
@implementation PlaylistController @implementation PlaylistController
@ -127,10 +125,6 @@ static void *playlistControllerContext = &playlistControllerContext;
}]; }];
kPersistentContainer = self.persistentContainer; kPersistentContainer = self.persistentContainer;
_persistentContainerLock = [[NSLock alloc] init];
kPersistentContainerLock = self.persistentContainerLock;
self.persistentContainer.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; self.persistentContainer.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
@ -144,10 +138,6 @@ static void *playlistControllerContext = &playlistControllerContext;
return kPersistentContainer; return kPersistentContainer;
} }
+ (NSLock *)sharedPersistentContainerLock {
return kPersistentContainerLock;
}
- (void)awakeFromNib { - (void)awakeFromNib {
[super awakeFromNib]; [super awakeFromNib];
@ -251,63 +241,46 @@ static void *playlistControllerContext = &playlistControllerContext;
} }
- (void)commitPersistentStore { - (void)commitPersistentStore {
NSError *error = nil; [self.persistentContainer.viewContext performBlockAndWait:^{
[self.persistentContainerLock lock]; NSError *error = nil;
[self.persistentContainer.viewContext save:&error]; [self.persistentContainer.viewContext save:&error];
[self.persistentContainerLock unlock]; if(error) {
if(error) { ALog(@"Error committing playlist storage: %@", [error localizedDescription]);
ALog(@"Error committing playlist storage: %@", [error localizedDescription]); }
} }];
} }
- (void)updatePlayCountForTrack:(PlaylistEntry *)pe { - (void)updatePlayCountForTrack:(PlaylistEntry *)pe {
if(pe.countAdded) return; if(pe.countAdded) return;
pe.countAdded = YES; pe.countAdded = YES;
PlayCount *pc = pe.playCountItem; __block PlayCount *pc = pe.playCountItem;
if(pc) { if(pc) {
[self.persistentContainerLock lock]; [self.persistentContainer.viewContext performBlockAndWait:^{
pc.count += 1; pc.count += 1;
pc.lastPlayed = [NSDate date]; pc.lastPlayed = [NSDate date];
[self.persistentContainerLock unlock]; }];
} else { } else {
[self.persistentContainerLock lock]; [self.persistentContainer.viewContext performBlockAndWait:^{
pc = [NSEntityDescription insertNewObjectForEntityForName:@"PlayCount" inManagedObjectContext:self.persistentContainer.viewContext]; pc = [NSEntityDescription insertNewObjectForEntityForName:@"PlayCount" inManagedObjectContext:self.persistentContainer.viewContext];
pc.count = 1; pc.count = 1;
pc.firstSeen = pc.lastPlayed = [NSDate date]; pc.firstSeen = pc.lastPlayed = [NSDate date];
pc.album = pe.album; pc.album = pe.album;
pc.artist = pe.artist; pc.artist = pe.artist;
pc.title = pe.title; pc.title = pe.title;
pc.filename = pe.filenameFragment; pc.filename = pe.filenameFragment;
[self.persistentContainerLock unlock]; }];
} }
[self commitPersistentStore]; [self commitPersistentStore];
} }
- (void)firstSawTrack:(PlaylistEntry *)pe { - (void)firstSawTrack:(PlaylistEntry *)pe {
PlayCount *pc = pe.playCountItem; __block PlayCount *pc = pe.playCountItem;
if(!pc) { if(!pc) {
[self.persistentContainerLock lock]; [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 unlock];
}
}
- (void)ratingUpdatedWithEntry:(PlaylistEntry *)pe rating:(CGFloat)rating {
if(pe && !pe.deLeted) {
PlayCount *pc = pe.playCountItem;
if(!pc) {
[self.persistentContainerLock lock];
pc = [NSEntityDescription insertNewObjectForEntityForName:@"PlayCount" inManagedObjectContext:self.persistentContainer.viewContext]; pc = [NSEntityDescription insertNewObjectForEntityForName:@"PlayCount" inManagedObjectContext:self.persistentContainer.viewContext];
pc.count = 0; pc.count = 0;
pc.firstSeen = [NSDate date]; pc.firstSeen = [NSDate date];
@ -315,12 +288,29 @@ static void *playlistControllerContext = &playlistControllerContext;
pc.artist = pe.artist; pc.artist = pe.artist;
pc.title = pe.title; pc.title = pe.title;
pc.filename = pe.filenameFragment; 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]; [self.persistentContainer.viewContext performBlockAndWait:^{
pc.rating = rating; pc.rating = rating;
[self.persistentContainerLock unlock]; }];
[self commitPersistentStore]; [self commitPersistentStore];
} }
@ -330,7 +320,9 @@ static void *playlistControllerContext = &playlistControllerContext;
PlayCount *pc = pe.playCountItem; PlayCount *pc = pe.playCountItem;
if(pc) { if(pc) {
pc.count = 0; [self.persistentContainer.viewContext performBlockAndWait:^{
pc.count = 0;
}];
} }
} }
@ -338,7 +330,9 @@ static void *playlistControllerContext = &playlistControllerContext;
PlayCount *pc = pe.playCountItem; PlayCount *pc = pe.playCountItem;
if(pc) { if(pc) {
pc.rating = 0; [self.persistentContainer.viewContext performBlockAndWait:^{
pc.rating = 0;
}];
} }
} }
@ -1380,19 +1374,16 @@ static void *playlistControllerContext = &playlistControllerContext;
request.predicate = predicate; request.predicate = predicate;
request.sortDescriptors = @[sortDescriptor]; request.sortDescriptors = @[sortDescriptor];
NSError *error = nil; [self.persistentContainer.viewContext performBlockAndWait:^{
[self.persistentContainerLock lock]; NSError *error = nil;
NSArray *results = [self.persistentContainer.viewContext executeFetchRequest:request error:&error]; NSArray *results = [self.persistentContainer.viewContext executeFetchRequest:request error:&error];
if(results) {
results = [results copy];
}
[self.persistentContainerLock unlock];
if(results && [results count] > 0) { if(results && [results count] > 0) {
[queueList removeAllObjects]; [queueList removeAllObjects];
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [results count])]; NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [results count])];
[queueList insertObjects:results atIndexes:indexSet]; [queueList insertObjects:results atIndexes:indexSet];
} }
}];
} }
- (void)readShuffleListFromDataStore { - (void)readShuffleListFromDataStore {
@ -1404,19 +1395,16 @@ static void *playlistControllerContext = &playlistControllerContext;
request.predicate = predicate; request.predicate = predicate;
request.sortDescriptors = @[sortDescriptor]; request.sortDescriptors = @[sortDescriptor];
NSError *error = nil; [self.persistentContainer.viewContext performBlockAndWait:^{
[self.persistentContainerLock lock]; NSError *error = nil;
NSArray *results = [self.persistentContainer.viewContext executeFetchRequest:request error:&error]; NSArray *results = [self.persistentContainer.viewContext executeFetchRequest:request error:&error];
if(results) {
results = [results copy];
}
[self.persistentContainerLock unlock];
if(results && [results count] > 0) { if(results && [results count] > 0) {
[shuffleList removeAllObjects]; [shuffleList removeAllObjects];
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [results count])]; NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [results count])];
[shuffleList insertObjects:results atIndexes:indexSet]; [shuffleList insertObjects:results atIndexes:indexSet];
} }
}];
} }
- (void)addShuffledListToFront { - (void)addShuffledListToFront {
@ -1946,16 +1934,18 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
} }
- (BOOL)pathSuggesterEmpty { - (BOOL)pathSuggesterEmpty {
BOOL rval; __block BOOL rval;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"]; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"];
NSError *error = nil; [self.persistentContainer.viewContext performBlockAndWait:^{
[self.persistentContainerLock lock]; NSError *error = nil;
NSArray *results = [self.persistentContainer.viewContext executeFetchRequest:request error:&error]; NSArray *results = [self.persistentContainer.viewContext executeFetchRequest:request error:&error];
if(!results || [results count] < 1) rval = YES; if(!results || [results count] < 1)
else rval = NO; rval = YES;
[self.persistentContainerLock unlock]; else
rval = NO;
}];
return rval; return rval;
} }

View File

@ -16,7 +16,6 @@
#import "SHA256Digest.h" #import "SHA256Digest.h"
#import "SecondsFormatter.h" #import "SecondsFormatter.h"
extern NSLock *kPersistentContainerLock;
extern NSPersistentContainer *kPersistentContainer; extern NSPersistentContainer *kPersistentContainer;
extern NSMutableDictionary<NSString *, AlbumArtwork *> *kArtworkDictionary; extern NSMutableDictionary<NSString *, AlbumArtwork *> *kArtworkDictionary;
@ -364,11 +363,9 @@ extern NSMutableDictionary<NSString *, AlbumArtwork *> *kArtworkDictionary;
self.artHash = imageCacheTag; self.artHash = imageCacheTag;
if(![kArtworkDictionary objectForKey:imageCacheTag]) { if(![kArtworkDictionary objectForKey:imageCacheTag]) {
[kPersistentContainerLock lock];
AlbumArtwork *art = [NSEntityDescription insertNewObjectForEntityForName:@"AlbumArtwork" inManagedObjectContext:kPersistentContainer.viewContext]; AlbumArtwork *art = [NSEntityDescription insertNewObjectForEntityForName:@"AlbumArtwork" inManagedObjectContext:kPersistentContainer.viewContext];
art.artHash = imageCacheTag; art.artHash = imageCacheTag;
art.artData = albumArtInternal; art.artData = albumArtInternal;
[kPersistentContainerLock unlock];
[kArtworkDictionary setObject:art forKey:imageCacheTag]; [kArtworkDictionary setObject:art forKey:imageCacheTag];
} }
@ -585,30 +582,30 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path) {
NSCompoundPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[albumPredicate, artistPredicate, titlePredicate]]; NSCompoundPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[albumPredicate, artistPredicate, titlePredicate]];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"PlayCount"]; __block PlayCount *item = nil;
request.predicate = predicate;
NSError *error = nil; [kPersistentContainer.viewContext performBlockAndWait:^{
[kPersistentContainerLock lock]; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"PlayCount"];
NSArray *results = [kPersistentContainer.viewContext executeFetchRequest:request error:&error]; request.predicate = predicate;
if(!results || [results count] < 1) { NSError *error = nil;
NSPredicate *filenamePredicate = [NSPredicate predicateWithFormat:@"filename == %@", self.filenameFragment]; NSArray *results = [kPersistentContainer.viewContext executeFetchRequest:request error:&error];
request = [NSFetchRequest fetchRequestWithEntityName:@"PlayCount"]; if(!results || [results count] < 1) {
request.predicate = filenamePredicate; NSPredicate *filenamePredicate = [NSPredicate predicateWithFormat:@"filename == %@", self.filenameFragment];
results = [kPersistentContainer.viewContext executeFetchRequest:request error:&error]; request = [NSFetchRequest fetchRequestWithEntityName:@"PlayCount"];
} request.predicate = filenamePredicate;
if(results) {
results = [results copy];
}
[kPersistentContainerLock unlock];
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; @dynamic playCount;

View File

@ -565,15 +565,13 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
__block NSMutableArray *entries = [NSMutableArray arrayWithCapacity:count]; __block NSMutableArray *entries = [NSMutableArray arrayWithCapacity:count];
for(NSURL *url in validURLs) { for(NSURL *url in validURLs) {
__block PlaylistEntry *pe; __block PlaylistEntry *pe;
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{ dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
[self->playlistController.persistentContainerLock lock];
pe = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:self->playlistController.persistentContainer.viewContext]; pe = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:self->playlistController.persistentContainer.viewContext];
pe.url = url; pe.url = url;
pe.index = index + i; pe.index = index + i;
pe.rawTitle = [[url path] lastPathComponent]; pe.rawTitle = [[url path] lastPathComponent];
pe.queuePosition = -1; pe.queuePosition = -1;
[self->playlistController.persistentContainerLock unlock];
}); });
[entries addObject:pe]; [entries addObject:pe];
@ -589,14 +587,12 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
if(xmlData) { if(xmlData) {
for(NSDictionary *entry in [xmlData objectForKey:@"entries"]) { for(NSDictionary *entry in [xmlData objectForKey:@"entries"]) {
__block PlaylistEntry *pe; __block PlaylistEntry *pe;
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{ dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
[self->playlistController.persistentContainerLock lock];
pe = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:self->playlistController.persistentContainer.viewContext]; pe = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:self->playlistController.persistentContainer.viewContext];
[pe setValuesForKeysWithDictionary:entry]; [pe setValuesForKeysWithDictionary:entry];
pe.index = index + i; pe.index = index + i;
pe.queuePosition = -1; pe.queuePosition = -1;
[self->playlistController.persistentContainerLock unlock];
}); });
[entries addObject:pe]; [entries addObject:pe];
@ -791,25 +787,22 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path);
NSCompoundPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[deletedPredicate, hasUrlPredicate]]; NSCompoundPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[deletedPredicate, hasUrlPredicate]];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"PlaylistEntry"]; [moc performBlockAndWait:^{
request.predicate = predicate; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"PlaylistEntry"];
request.predicate = predicate;
NSError *error; NSError *error;
[playlistController.persistentContainerLock lock]; NSArray *results = [moc executeFetchRequest:request error:&error];
NSArray *results = [moc executeFetchRequest:request error:&error];
if(results) {
results = [results copy];
}
[playlistController.persistentContainerLock unlock];
if(results && [results count] > 0) { if(results && [results count] > 0) {
for(PlaylistEntry *pe in results) { for(PlaylistEntry *pe in results) {
NSMutableArray *entrySet = [uniquePathsEntries objectForKey:pe.urlString]; NSMutableArray *entrySet = [uniquePathsEntries objectForKey:pe.urlString];
if(entrySet) { if(entrySet) {
[entrySet addObject:pe]; [entrySet addObject:pe];
}
} }
} }
} }];
for(size_t i = 0, j = [outArray count]; i < j; i += 2) { for(size_t i = 0, j = [outArray count]; i < j; i += 2) {
__block NSString *entryKey = [outArray objectAtIndex:i]; __block NSString *entryKey = [outArray objectAtIndex:i];
@ -927,10 +920,8 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path);
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"AlbumArtwork"]; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"AlbumArtwork"];
NSError *error = nil; NSError *error = nil;
[playlistController.persistentContainerLock lock];
NSArray *results = [moc executeFetchRequest:request error:&error]; NSArray *results = [moc executeFetchRequest:request error:&error];
if(!results) { if(!results) {
[playlistController.persistentContainerLock unlock];
ALog(@"Error fetching AlbumArtwork objects: %@\n%@", [error localizedDescription], [error userInfo]); ALog(@"Error fetching AlbumArtwork objects: %@\n%@", [error localizedDescription], [error userInfo]);
abort(); abort();
} }
@ -938,7 +929,6 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path);
for(AlbumArtwork *art in results) { for(AlbumArtwork *art in results) {
[kArtworkDictionary setObject:art forKey:art.artHash]; [kArtworkDictionary setObject:art forKey:art.artHash];
} }
[playlistController.persistentContainerLock unlock];
request = [NSFetchRequest fetchRequestWithEntityName:@"PlaylistEntry"]; request = [NSFetchRequest fetchRequestWithEntityName:@"PlaylistEntry"];
@ -946,30 +936,24 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path);
request.sortDescriptors = @[sortDescriptor]; request.sortDescriptors = @[sortDescriptor];
[playlistController.persistentContainerLock lock];
results = [moc executeFetchRequest:request error:&error]; results = [moc executeFetchRequest:request error:&error];
if(!results) { if(!results) {
[playlistController.persistentContainerLock unlock];
ALog(@"Error fetching PlaylistEntry objects: %@\n%@", [error localizedDescription], [error userInfo]); ALog(@"Error fetching PlaylistEntry objects: %@\n%@", [error localizedDescription], [error userInfo]);
abort(); abort();
} }
if([results count] == 0) { if([results count] == 0) {
[playlistController.persistentContainerLock unlock];
return NO; return NO;
} }
NSMutableArray *resultsCopy = [results mutableCopy]; NSMutableArray *resultsCopy = [results mutableCopy];
[playlistController.persistentContainerLock unlock];
NSMutableIndexSet *pruneSet = [[NSMutableIndexSet alloc] init]; NSMutableIndexSet *pruneSet = [[NSMutableIndexSet alloc] init];
NSUInteger index = 0; NSUInteger index = 0;
for(PlaylistEntry *pe in resultsCopy) { for(PlaylistEntry *pe in resultsCopy) {
if(pe.deLeted || !pe.urlString || [pe.urlString length] < 1) { if(pe.deLeted || !pe.urlString || [pe.urlString length] < 1) {
[pruneSet addIndex:index]; [pruneSet addIndex:index];
[playlistController.persistentContainerLock lock];
[moc deleteObject:pe]; [moc deleteObject:pe];
[playlistController.persistentContainerLock unlock];
} }
++index; ++index;
} }

View File

@ -55,7 +55,6 @@
[pathsList removeObjects:[pathsList arrangedObjects]]; [pathsList removeObjects:[pathsList arrangedObjects]];
NSLock *lock = [NSClassFromString(@"PlaylistController") sharedPersistentContainerLock];
NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer]; NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer];
NSPredicate *hasUrlPredicate = [NSPredicate predicateWithFormat:@"urlString != nil && urlString != %@", @""]; NSPredicate *hasUrlPredicate = [NSPredicate predicateWithFormat:@"urlString != nil && urlString != %@", @""];
@ -67,12 +66,7 @@
request.predicate = predicate; request.predicate = predicate;
NSError *error = nil; NSError *error = nil;
[lock lock];
NSArray *results = [pc.viewContext executeFetchRequest:request error:&error]; NSArray *results = [pc.viewContext executeFetchRequest:request error:&error];
if(results) {
results = [results copy];
}
[lock unlock];
if(!results || [results count] < 1) return; if(!results || [results count] < 1) return;

View File

@ -36,18 +36,12 @@
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path" ascending:YES]; NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path" ascending:YES];
[self setSortDescriptors:@[sortDescriptor]]; [self setSortDescriptors:@[sortDescriptor]];
NSLock *lock = [NSClassFromString(@"PlaylistController") sharedPersistentContainerLock];
NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer]; NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"]; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"];
NSError *error = nil; NSError *error = nil;
[lock lock];
NSArray *results = [pc.viewContext executeFetchRequest:request error:&error]; NSArray *results = [pc.viewContext executeFetchRequest:request error:&error];
if(results) {
results = [results copy];
}
[lock unlock];
if(results && [results count] > 0) { if(results && [results count] > 0) {
for(SandboxToken *token in results) { for(SandboxToken *token in results) {
@ -67,31 +61,31 @@
return; return;
} }
NSLock *lock = [NSClassFromString(@"PlaylistController") sharedPersistentContainerLock];
NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer]; NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer];
[lock lock]; [pc.viewContext performBlockAndWait:^{
SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext]; SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext];
if(token) { if(token) {
token.path = [url path]; NSError *err = nil;
token.bookmark = bookmark; token.path = [url path];
[pc.viewContext save:&err]; token.bookmark = bookmark;
[lock unlock]; [pc.viewContext save:&err];
if(err) { if(err) {
ALog(@"Error saving bookmark: %@", [err localizedDescription]); ALog(@"Error saving bookmark: %@", [err localizedDescription]);
} else { } else {
[self addObject:@{ @"path": [url path], @"valid": NSLocalizedPrefString(@"ValidYes"), @"isFolder": @(token.folder), @"token": token }]; [self addObject:@{@"path": [url path],
[NSClassFromString(@"SandboxBroker") cleanupFolderAccess]; @"valid": NSLocalizedPrefString(@"ValidYes"),
[self refresh]; @"isFolder": @(token.folder),
@"token": token}];
[NSClassFromString(@"SandboxBroker") cleanupFolderAccess];
[self refresh];
}
} }
} else { }];
[lock unlock];
}
} }
- (void)removeToken:(id)token { - (void)removeToken:(id)token {
NSLock *lock = [NSClassFromString(@"PlaylistController") sharedPersistentContainerLock];
NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer]; NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer];
NSArray *objects = [[self arrangedObjects] copy]; NSArray *objects = [[self arrangedObjects] copy];
@ -101,46 +95,45 @@
for(NSDictionary *obj in objects) { for(NSDictionary *obj in objects) {
if([[obj objectForKey:@"token"] isEqualTo:token]) { if([[obj objectForKey:@"token"] isEqualTo:token]) {
[self removeObject:obj]; [self removeObject:obj];
[lock lock]; [pc.viewContext performBlockAndWait:^{
[pc.viewContext deleteObject:token]; [pc.viewContext deleteObject:token];
[lock unlock]; }];
updated = YES; updated = YES;
break; break;
} }
} }
if(updated) { if(updated) {
NSError *error; [pc.viewContext performBlockAndWait:^{
[lock lock]; NSError *error;
[pc.viewContext save:&error]; [pc.viewContext save:&error];
[lock unlock]; if(error) {
if(error) { ALog(@"Error deleting bookmark: %@", [error localizedDescription]);
ALog(@"Error deleting bookmark: %@", [error localizedDescription]); }
} }];
} }
} }
- (void)removeStaleEntries { - (void)removeStaleEntries {
BOOL updated = NO; BOOL updated = NO;
NSLock *lock = [NSClassFromString(@"PlaylistController") sharedPersistentContainerLock];
NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer]; NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer];
for(NSDictionary *entry in [[self arrangedObjects] copy]) { for(NSDictionary *entry in [[self arrangedObjects] copy]) {
if([[entry objectForKey:@"valid"] isEqualToString:NSLocalizedPrefString(@"ValidNo")]) { if([[entry objectForKey:@"valid"] isEqualToString:NSLocalizedPrefString(@"ValidNo")]) {
[self removeObject:entry]; [self removeObject:entry];
[lock lock]; [pc.viewContext performBlockAndWait:^{
[pc.viewContext deleteObject:[entry objectForKey:@"token"]]; [pc.viewContext deleteObject:[entry objectForKey:@"token"]];
[lock unlock]; }];
updated = YES; updated = YES;
} }
} }
if(updated) { if(updated) {
NSError *error; [pc.viewContext performBlockAndWait:^{
[lock lock]; NSError *error;
[pc.viewContext save:&error]; [pc.viewContext save:&error];
[lock unlock]; if(error) {
if(error) { ALog(@"Error saving after removing stale bookmarks: %@", [error localizedDescription]);
ALog(@"Error saving after removing stale bookmarks: %@", [error localizedDescription]); }
} }];
} }
} }

View File

@ -13,7 +13,6 @@
// with format (entryKey, transformerName) // with format (entryKey, transformerName)
static NSDictionary *importKeys; static NSDictionary *importKeys;
extern NSLock *kPersistentContainerLock;
extern NSPersistentContainer *kPersistentContainer; extern NSPersistentContainer *kPersistentContainer;
@implementation SpotlightPlaylistEntry @implementation SpotlightPlaylistEntry
@ -38,53 +37,54 @@ extern NSPersistentContainer *kPersistentContainer;
} }
+ (PlaylistEntry *)playlistEntryWithMetadataItem:(NSMetadataItem *)metadataItem { + (PlaylistEntry *)playlistEntryWithMetadataItem:(NSMetadataItem *)metadataItem {
[kPersistentContainerLock lock]; __block PlaylistEntry *entry = nil;
PlaylistEntry *entry = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:kPersistentContainer.viewContext]; [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 // loop through the keys we want to extract
for(NSString *mdKey in importKeys) { for(NSString *mdKey in importKeys) {
if(![metadataItem valueForAttribute:mdKey]) continue; if(![metadataItem valueForAttribute:mdKey]) continue;
id importTarget = [importKeys objectForKey:mdKey]; id importTarget = [importKeys objectForKey:mdKey];
// Just copy the object from metadata // Just copy the object from metadata
if([importTarget isKindOfClass:[NSString class]]) { if([importTarget isKindOfClass:[NSString class]]) {
if([importTarget isEqualToString:@"length"]) { if([importTarget isEqualToString:@"length"]) {
// fake it // fake it
NSNumber *number = [metadataItem valueForAttribute:mdKey]; NSNumber *number = [metadataItem valueForAttribute:mdKey];
[dict setValue:@(44100.0) forKey:@"samplerate"]; [dict setValue:@(44100.0) forKey:@"samplerate"];
[dict setValue:@(44100.0 * [number doubleValue]) forKey:@"totalFrames"]; [dict setValue:@(44100.0 * [number doubleValue]) forKey:@"totalFrames"];
} else { } else {
[dict setValue:[metadataItem valueForAttribute:mdKey] [dict setValue:[metadataItem valueForAttribute:mdKey]
forKey:importTarget]; 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"]; NSURL *url = [dict objectForKey:@"url"];
[dict removeObjectForKey:@"url"]; [dict removeObjectForKey:@"url"];
entry.url = url; entry.url = url;
[entry setMetadata:dict]; [entry setMetadata:dict];
[kPersistentContainerLock unlock]; }];
return entry; return entry;
} }

View File

@ -14,7 +14,6 @@
#import "SHA256Digest.h" #import "SHA256Digest.h"
extern NSLock *kPersistentContainerLock;
extern NSPersistentContainer *kPersistentContainer; extern NSPersistentContainer *kPersistentContainer;
NSString *getDatabasePath(void) { NSString *getDatabasePath(void) {
@ -1444,114 +1443,112 @@ static SQLiteStore *g_sharedStore = nil;
#endif #endif
- (PlaylistEntry *_Nonnull)getTrack:(int64_t)trackId { - (PlaylistEntry *_Nonnull)getTrack:(int64_t)trackId {
[kPersistentContainerLock lock]; __block PlaylistEntry *entry = nil;
PlaylistEntry *entry = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:kPersistentContainer.viewContext]; [kPersistentContainer.viewContext performBlockAndWait:^{
entry = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:kPersistentContainer.viewContext];
if(trackId < 0) { if(trackId < 0) {
entry.error = YES; entry.error = YES;
entry.errorMessage = NSLocalizedString(@"ErrorInvalidTrackId", @""); entry.errorMessage = NSLocalizedString(@"ErrorInvalidTrackId", @"");
entry.deLeted = YES; entry.deLeted = YES;
[kPersistentContainerLock unlock]; return;
return entry; }
}
sqlite3_stmt *st = stmt[stmt_select_track_data]; sqlite3_stmt *st = stmt[stmt_select_track_data];
if(sqlite3_reset(st) || if(sqlite3_reset(st) ||
sqlite3_bind_int64(st, select_track_data_in_id, trackId)) { sqlite3_bind_int64(st, select_track_data_in_id, trackId)) {
entry.error = YES; entry.error = YES;
entry.errorMessage = NSLocalizedString(@"ErrorSqliteProblem", @""); entry.errorMessage = NSLocalizedString(@"ErrorSqliteProblem", @"");
entry.deLeted = YES; entry.deLeted = YES;
[kPersistentContainerLock unlock]; return;
return entry; }
}
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); 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; return entry;
} }
@ -1846,7 +1843,7 @@ static SQLiteStore *g_sharedStore = nil;
} }
- (PlaylistEntry *)playlistGetItem:(int64_t)index { - (PlaylistEntry *)playlistGetItem:(int64_t)index {
PlaylistEntry *entry = nil; __block PlaylistEntry *entry = nil;
sqlite3_stmt *st = stmt[stmt_select_playlist]; 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); int64_t entryId = sqlite3_column_int64(st, select_playlist_out_entry_id);
entry = [self getTrack:trackId]; entry = [self getTrack:trackId];
if(!entry.deLeted && !entry.error) { if(!entry.deLeted && !entry.error) {
[kPersistentContainerLock lock]; [kPersistentContainer.viewContext performBlockAndWait:^{
entry.index = index; entry.index = index;
entry.entryId = entryId; entry.entryId = entryId;
[kPersistentContainerLock unlock]; }];
} else { } else {
[kPersistentContainerLock lock]; [kPersistentContainer.viewContext performBlockAndWait:^{
[kPersistentContainer.viewContext deleteObject:entry]; [kPersistentContainer.viewContext deleteObject:entry];
[kPersistentContainerLock unlock]; entry = nil;
entry = nil; }];
} }
} }

View File

@ -92,10 +92,6 @@ static SandboxBroker *kSharedSandboxBroker = nil;
return kSharedSandboxBroker; return kSharedSandboxBroker;
} }
+ (NSLock *)sharedPersistentContainerLock {
return [NSClassFromString(@"PlaylistController") sharedPersistentContainerLock];
}
+ (NSPersistentContainer *)sharedPersistentContainer { + (NSPersistentContainer *)sharedPersistentContainer {
return [NSClassFromString(@"PlaylistController") sharedPersistentContainer]; return [NSClassFromString(@"PlaylistController") sharedPersistentContainer];
} }
@ -151,57 +147,49 @@ static SandboxBroker *kSharedSandboxBroker = nil;
} }
- (SandboxEntry *)recursivePathTest:(NSURL *)url { - (SandboxEntry *)recursivePathTest:(NSURL *)url {
SandboxEntry *ret = nil; __block SandboxEntry *ret = nil;
NSLock *lock = [SandboxBroker sharedPersistentContainerLock];
NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer]; NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer];
NSPredicate *folderPredicate = [NSPredicate predicateWithFormat:@"folder == NO"]; NSPredicate *folderPredicate = [NSPredicate predicateWithFormat:@"folder == NO"];
NSPredicate *filePredicate = [NSPredicate predicateWithFormat:@"path == %@", [url path]]; NSPredicate *filePredicate = [NSPredicate predicateWithFormat:@"path == %@", [url path]];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[folderPredicate, filePredicate]];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"]; [pc.viewContext performBlockAndWait:^{
request.predicate = predicate; NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[folderPredicate, filePredicate]];
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]];
}
if(!ret) { NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"];
predicate = [NSPredicate predicateWithFormat:@"folder == YES"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path.length" ascending:NO];
request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"];
request.sortDescriptors = @[sortDescriptor];
request.predicate = predicate; request.predicate = predicate;
error = nil; NSError *error = nil;
[lock lock]; NSArray *results = [pc.viewContext executeFetchRequest:request error:&error];
results = [pc.viewContext executeFetchRequest:request error:&error];
if(results) {
results = [results copy];
}
[lock unlock];
if(results && [results count] > 0) { if(results && [results count] > 0) {
for(SandboxToken *token in results) { ret = [[SandboxEntry alloc] initWithToken:results[0]];
if(token.path && [SandboxBroker isPath:url aSubdirectoryOf:[NSURL fileURLWithPath:token.path]]) { }
SandboxEntry *entry = [[SandboxEntry alloc] initWithToken:token];
ret = entry; if(!ret) {
break; 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) { if(ret) {
BOOL isStale; BOOL isStale;
@ -212,7 +200,9 @@ static SandboxBroker *kSharedSandboxBroker = nil;
return nil; return nil;
} }
ret.secureUrl = secureUrl; [pc.viewContext performBlockAndWait:^{
ret.secureUrl = secureUrl;
}];
return ret; return ret;
} }
@ -253,22 +243,17 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
return; return;
} }
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{ NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer];
NSLock *lock = [SandboxBroker sharedPersistentContainerLock];
NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer];
[lock lock]; [pc.viewContext performBlockAndWait:^{
SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext]; SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext];
if(token) { if(token) {
token.path = [folderUrl path]; token.path = [folderUrl path];
token.bookmark = bookmark; token.bookmark = bookmark;
[lock unlock];
[SandboxBroker cleanupFolderAccess]; [SandboxBroker cleanupFolderAccess];
} else {
[lock unlock];
} }
}); }];
} }
} }
} }
@ -303,11 +288,9 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
return; return;
} }
NSLock *lock = [SandboxBroker sharedPersistentContainerLock];
NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer]; NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer];
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{ [pc.viewContext performBlockAndWait:^{
[lock lock];
SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext]; SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext];
if(token) { if(token) {
@ -315,8 +298,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
token.bookmark = bookmark; token.bookmark = bookmark;
token.folder = NO; token.folder = NO;
} }
[lock unlock]; }];
});
} }
} }
} }
@ -372,19 +354,14 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
return; return;
} }
NSLock *lock = [SandboxBroker sharedPersistentContainerLock];
NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer]; NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer];
[lock lock];
SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext]; SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext];
if(token) { if(token) {
token.path = [folderUrl path]; token.path = [folderUrl path];
token.bookmark = bookmark; token.bookmark = bookmark;
[lock unlock];
[SandboxBroker cleanupFolderAccess]; [SandboxBroker cleanupFolderAccess];
} else {
[lock unlock];
} }
} }
}); });
@ -393,52 +370,47 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
} }
+ (void)cleanupFolderAccess { + (void)cleanupFolderAccess {
NSLock *lock = [SandboxBroker sharedPersistentContainerLock];
NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer]; NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path.length" ascending:YES]; NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path.length" ascending:YES];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"]; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"];
request.sortDescriptors = @[sortDescriptor]; request.sortDescriptors = @[sortDescriptor];
NSError *error = nil; [pc.viewContext performBlockAndWait:^{
NSMutableArray *resultsCopy = nil; NSError *error = nil;
[lock lock]; NSArray *results = [pc.viewContext executeFetchRequest:request error:&error];
NSArray *results = [pc.viewContext executeFetchRequest:request error:&error]; NSMutableArray *resultsCopy = nil;
if(results) { if(results) {
resultsCopy = [results mutableCopy]; resultsCopy = [results mutableCopy];
} }
[lock unlock];
BOOL isUpdated = NO; BOOL isUpdated = NO;
if(resultsCopy && [resultsCopy count]) { if(resultsCopy && [resultsCopy count]) {
for(NSUInteger i = 0; i < [resultsCopy count] - 1; ++i) { for(NSUInteger i = 0; i < [resultsCopy count] - 1; ++i) {
SandboxToken *token = resultsCopy[i]; SandboxToken *token = resultsCopy[i];
NSURL *url = [NSURL fileURLWithPath:token.path]; NSURL *url = [NSURL fileURLWithPath:token.path];
for(NSUInteger j = i + 1; j < [resultsCopy count];) { for(NSUInteger j = i + 1; j < [resultsCopy count];) {
SandboxToken *compareToken = resultsCopy[j]; SandboxToken *compareToken = resultsCopy[j];
if([SandboxBroker isPath:[NSURL fileURLWithPath:compareToken.path] aSubdirectoryOf:url]) { if([SandboxBroker isPath:[NSURL fileURLWithPath:compareToken.path] aSubdirectoryOf:url]) {
[lock lock]; [pc.viewContext deleteObject:compareToken];
[pc.viewContext deleteObject:compareToken]; isUpdated = YES;
[lock unlock]; [resultsCopy removeObjectAtIndex:j];
isUpdated = YES; } else {
[resultsCopy removeObjectAtIndex:j]; ++j;
} else { }
++j;
} }
} }
} }
}
if(isUpdated) { if(isUpdated) {
NSError *error; NSError *error;
[lock lock]; [pc.viewContext save:&error];
[pc.viewContext save:&error]; if(error) {
[lock unlock]; ALog(@"Error saving data: %@", [error localizedDescription]);
if(error) { }
ALog(@"Error saving data: %@", [error localizedDescription]);
} }
} }];
} }
- (const void *)beginFolderAccess:(NSURL *)fileUrl { - (const void *)beginFolderAccess:(NSURL *)fileUrl {