[Playlist Loading] Process messages while loading

Process main queue messages by handling the loading in a background
queue, and sync it to the main thread periodically, while pausing to
wait for the results. This allows the file open dialog to return
immediately, and display loading progress on the status bar.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
swiftingly
Christopher Snowhill 2022-06-28 20:26:55 -07:00
parent f8d2837c4e
commit 802a86a3d8
2 changed files with 75 additions and 23 deletions

View File

@ -10,6 +10,8 @@
#import "PlaylistView.h" #import "PlaylistView.h"
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import <stdatomic.h>
@class PlaylistController; @class PlaylistController;
@class PlaybackController; @class PlaybackController;
@class PlaylistEntry; @class PlaylistEntry;
@ -30,6 +32,9 @@ typedef enum {
BOOL metadataLoadInProgress; BOOL metadataLoadInProgress;
NSMutableDictionary *queuedURLs; NSMutableDictionary *queuedURLs;
dispatch_queue_t loaderQueue;
atomic_int loaderQueueRefCount;
} }
- (void)initDefaults; - (void)initDefaults;

View File

@ -54,6 +54,9 @@ extern NSMutableDictionary<NSString *, AlbumArtwork *> *kArtworkDictionary;
[queue setMaxConcurrentOperationCount:8]; [queue setMaxConcurrentOperationCount:8];
queuedURLs = [[NSMutableDictionary alloc] init]; queuedURLs = [[NSMutableDictionary alloc] init];
loaderQueue = dispatch_queue_create("Playlist loader queue", DISPATCH_QUEUE_SERIAL);
atomic_init(&loaderQueueRefCount, 0);
} }
return self; return self;
@ -330,6 +333,34 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
} }
- (NSArray *)insertURLs:(NSArray *)urls atIndex:(NSInteger)index sort:(BOOL)sort { - (NSArray *)insertURLs:(NSArray *)urls atIndex:(NSInteger)index sort:(BOOL)sort {
int refCount;
do {
refCount = atomic_load_explicit(&loaderQueueRefCount, memory_order_relaxed);
if(refCount > 0) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
}
} while(refCount > 0);
__block NSArray *outurls = nil;
dispatch_async(loaderQueue, ^{
atomic_fetch_add(&self->loaderQueueRefCount, 1);
outurls = [self doInsertURLs:urls atIndex:index sort:sort];
atomic_fetch_sub(&self->loaderQueueRefCount, 1);
});
do {
refCount = atomic_load_explicit(&loaderQueueRefCount, memory_order_relaxed);
if(refCount > 0) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
}
} while(refCount > 0);
return outurls;
}
- (NSArray *)doInsertURLs:(NSArray *)urls atIndex:(NSInteger)index sort:(BOOL)sort {
NSMutableSet *uniqueURLs = [NSMutableSet set]; NSMutableSet *uniqueURLs = [NSMutableSet set];
NSMutableArray *expandedURLs = [NSMutableArray array]; NSMutableArray *expandedURLs = [NSMutableArray array];
@ -507,14 +538,18 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
progressstep = 100.0 / (double)(count); progressstep = 100.0 / (double)(count);
NSInteger i = 0; NSInteger i = 0;
NSMutableArray *entries = [NSMutableArray arrayWithCapacity:count]; __block NSMutableArray *entries = [NSMutableArray arrayWithCapacity:count];
for(NSURL *url in validURLs) { for(NSURL *url in validURLs) {
PlaylistEntry *pe = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:playlistController.persistentContainer.viewContext]; __block PlaylistEntry *pe;
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
pe = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:self->playlistController.persistentContainer.viewContext];
pe.url = url;
pe.index = index + i;
pe.rawTitle = [[url path] lastPathComponent];
pe.queuePosition = -1;
});
pe.url = url;
pe.index = index + i;
pe.rawTitle = [[url path] lastPathComponent];
pe.queuePosition = -1;
[entries addObject:pe]; [entries addObject:pe];
++i; ++i;
@ -527,11 +562,15 @@ 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"]) {
PlaylistEntry *pe = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:playlistController.persistentContainer.viewContext]; __block PlaylistEntry *pe;
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
pe = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistEntry" inManagedObjectContext:self->playlistController.persistentContainer.viewContext];
[pe setValuesForKeysWithDictionary:entry];
pe.index = index + i;
pe.queuePosition = -1;
});
[pe setValuesForKeysWithDictionary:entry];
pe.index = index + i;
pe.queuePosition = -1;
[entries addObject:pe]; [entries addObject:pe];
++i; ++i;
@ -542,26 +581,32 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
NSIndexSet *is = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, [entries count])]; NSIndexSet *is = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, [entries count])];
[playlistController insertObjects:entries atArrangedObjectIndexes:is]; dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
[self->playlistController insertObjects:entries atArrangedObjectIndexes:is];
});
if(xmlData && [[xmlData objectForKey:@"queue"] count]) { if(xmlData && [[xmlData objectForKey:@"queue"] count]) {
[playlistController emptyQueueList:self]; dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
[self->playlistController emptyQueueList:self];
i = 0; long i = 0;
for(NSNumber *index in [xmlData objectForKey:@"queue"]) { for(NSNumber *index in [xmlData objectForKey:@"queue"]) {
NSInteger indexVal = [index intValue] + j; NSInteger indexVal = [index intValue] + j;
PlaylistEntry *pe = [entries objectAtIndex:indexVal]; PlaylistEntry *pe = [entries objectAtIndex:indexVal];
pe.queuePosition = i; pe.queuePosition = i;
pe.queued = YES; pe.queued = YES;
[[playlistController queueList] addObject:pe]; [[self->playlistController queueList] addObject:pe];
++i; ++i;
} }
});
} }
// Clear the selection // Clear the selection
[playlistController setSelectionIndexes:[NSIndexSet indexSet]]; dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
[self->playlistController setSelectionIndexes:[NSIndexSet indexSet]];
});
{ {
NSArray *arrayFirst = @[[entries objectAtIndex:0]]; NSArray *arrayFirst = @[[entries objectAtIndex:0]];
@ -582,7 +627,9 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
if([arrayRest count]) if([arrayRest count])
[self performSelectorInBackground:@selector(loadInfoForEntries:) withObject:arrayRest]; [self performSelectorInBackground:@selector(loadInfoForEntries:) withObject:arrayRest];
else { else {
[playlistController commitPersistentStore]; dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
[self->playlistController commitPersistentStore];
});
[self completeProgress]; [self completeProgress];
} }
return entries; return entries;