[vlc-commits] [Git][videolan/vlc][master] 2 commits: macosx: Prevent superseded updates to caches from overwriting newer state
Felix Paul Kühne (@fkuehne)
gitlab at videolan.org
Tue Oct 6 15:22:01 UTC 2026
Felix Paul Kühne pushed to branch master at VideoLAN / VLC
Commits:
460b4747 by Claudio Cambra at 2026-10-06T16:30:01+02:00
macosx: Prevent superseded updates to caches from overwriting newer state
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
04d555a9 by Claudio Cambra at 2026-10-06T16:30:01+02:00
macosx: Copy media array once when updating media titles
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
1 changed file:
- modules/gui/macosx/library/VLCLibraryModel.m
Changes:
=====================================
modules/gui/macosx/library/VLCLibraryModel.m
=====================================
@@ -107,9 +107,12 @@ NSString * const VLCLibraryModelDiscoveryFailed = @"VLCLibraryModelDiscoveryFail
dispatch_queue_t _groupCacheModificationQueue;
dispatch_queue_t _mediaTitlesCacheModificationQueue;
NSCountedSet<NSString *> *_mediaTitleCounts;
+ NSMutableDictionary<NSValue *, NSObject *> *_cacheUpdateTokens;
+ NSMutableSet<NSValue *> *_pendingCacheUpdates;
}
@property (readwrite) NSArray<VLCMediaLibraryFolderObserver *> *folderObservers;
+ at property (atomic) NSUInteger cacheGeneration;
@property (readwrite, nonatomic) NSArray *cachedAudioMedia;
@property (readwrite, nonatomic) NSArray *cachedArtists;
@@ -333,6 +336,8 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
_groupCacheModificationQueue = dispatch_queue_create("groupCacheModificationQueue", DISPATCH_QUEUE_CONCURRENT);
_mediaTitlesCacheModificationQueue = dispatch_queue_create("mediaTitlesCacheModificationQueue", DISPATCH_QUEUE_CONCURRENT);
_mediaTitleCounts = [NSCountedSet new];
+ _cacheUpdateTokens = NSMutableDictionary.dictionary;
+ _pendingCacheUpdates = NSMutableSet.set;
_defaultNotificationCenter = NSNotificationCenter.defaultCenter;
[_defaultNotificationCenter addObserver:self
@@ -479,6 +484,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)setCachedAudioMedia:(NSArray *)cachedAudioMedia
{
+ [self invalidateCacheUpdateForGetter:@selector(cachedAudioMedia)];
NSArray * const media = [cachedAudioMedia copy];
dispatch_barrier_async(_mediaItemCacheModificationQueue, ^{
[self replaceMediaTitlesFromMedia:self->_cachedAudioMedia withMedia:media];
@@ -488,6 +494,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)setCachedVideoMedia:(NSArray *)cachedVideoMedia
{
+ [self invalidateCacheUpdateForGetter:@selector(cachedVideoMedia)];
NSArray * const media = [cachedVideoMedia copy];
dispatch_barrier_async(_mediaItemCacheModificationQueue, ^{
[self replaceMediaTitlesFromMedia:self->_cachedVideoMedia withMedia:media];
@@ -497,6 +504,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)setCachedRecentMedia:(NSArray *)cachedRecentMedia
{
+ [self invalidateCacheUpdateForGetter:@selector(cachedRecentMedia)];
dispatch_barrier_async(_mediaItemCacheModificationQueue, ^{
self->_cachedRecentMedia = [cachedRecentMedia copy];
});
@@ -504,6 +512,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)setCachedRecentAudioMedia:(NSArray *)cachedRecentAudioMedia
{
+ [self invalidateCacheUpdateForGetter:@selector(cachedRecentAudioMedia)];
dispatch_barrier_async(_mediaItemCacheModificationQueue, ^{
self->_cachedRecentAudioMedia = [cachedRecentAudioMedia copy];
});
@@ -511,6 +520,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)setCachedListOfShows:(NSArray *)cachedListOfShows
{
+ [self invalidateCacheUpdateForGetter:@selector(cachedListOfShows)];
dispatch_barrier_async(_mediaItemCacheModificationQueue, ^{
self->_cachedListOfShows = [cachedListOfShows copy];
});
@@ -518,6 +528,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)setCachedListOfMovies:(NSArray *)cachedListOfMovies
{
+ [self invalidateCacheUpdateForGetter:@selector(cachedListOfMovies)];
dispatch_barrier_async(_mediaItemCacheModificationQueue, ^{
self->_cachedListOfMovies = [cachedListOfMovies copy];
});
@@ -525,6 +536,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)setCachedListOfMonitoredFolders:(NSArray *)cachedListOfMonitoredFolders
{
+ [self invalidateCacheUpdateForGetter:@selector(cachedListOfMonitoredFolders)];
dispatch_barrier_async(_mediaItemCacheModificationQueue, ^{
self->_cachedListOfMonitoredFolders = [cachedListOfMonitoredFolders copy];
});
@@ -532,6 +544,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)setCachedAlbums:(NSArray *)cachedAlbums
{
+ [self invalidateCacheUpdateForGetter:@selector(cachedAlbums)];
dispatch_barrier_async(_albumCacheModificationQueue, ^{
self->_cachedAlbums = [cachedAlbums copy];
});
@@ -539,6 +552,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)setCachedArtists:(NSArray *)cachedArtists
{
+ [self invalidateCacheUpdateForGetter:@selector(cachedArtists)];
dispatch_barrier_async(_artistCacheModificationQueue, ^{
self->_cachedArtists = [cachedArtists copy];
});
@@ -546,6 +560,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)setCachedGenres:(NSArray *)cachedGenres
{
+ [self invalidateCacheUpdateForGetter:@selector(cachedGenres)];
dispatch_barrier_async(_genreCacheModificationQueue, ^{
self->_cachedGenres = [cachedGenres copy];
});
@@ -553,6 +568,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)setCachedListOfGroups:(NSArray *)cachedListOfGroups
{
+ [self invalidateCacheUpdateForGetter:@selector(cachedListOfGroups)];
dispatch_barrier_async(_groupCacheModificationQueue, ^{
self->_cachedListOfGroups = [cachedListOfGroups copy];
});
@@ -560,6 +576,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)setCachedMediaTitles:(NSArray *)cachedMediaTitles
{
+ [self invalidateCacheUpdateForGetter:@selector(cachedMediaTitles)];
dispatch_barrier_async(_mediaTitlesCacheModificationQueue, ^{
self->_cachedMediaTitles = [cachedMediaTitles copy];
});
@@ -588,28 +605,134 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)performAfterCacheWritesOnQueue:(dispatch_queue_t)queue
block:(dispatch_block_t)block
{
+ const NSUInteger generation = self.cacheGeneration;
// The sentinel runs after all cache writes already queued on this queue.
dispatch_barrier_async(queue, ^{
- dispatch_async(dispatch_get_main_queue(), block);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (generation == self.cacheGeneration) {
+ block();
+ }
+ });
+ });
+}
+
+- (NSObject *)beginCacheUpdateForGetter:(SEL)getter
+{
+ NSObject * const token = NSObject.new;
+ @synchronized (_cacheUpdateTokens) {
+ _cacheUpdateTokens[[NSValue valueWithPointer:getter]] = token;
+ [_pendingCacheUpdates addObject:[NSValue valueWithPointer:getter]];
+ }
+ return token;
+}
+
+- (BOOL)isCurrentCacheUpdate:(NSObject *)token forGetter:(SEL)getter
+{
+ @synchronized (_cacheUpdateTokens) {
+ return _cacheUpdateTokens[[NSValue valueWithPointer:getter]] == token;
+ }
+}
+
+- (BOOL)invalidateCacheUpdateForGetter:(SEL)getter
+{
+ @synchronized (_cacheUpdateTokens) {
+ NSValue * const key = [NSValue valueWithPointer:getter];
+ const BOOL pending = [_pendingCacheUpdates containsObject:key];
+ [_cacheUpdateTokens removeObjectForKey:key];
+ [_pendingCacheUpdates removeObject:key];
+ return pending;
+ }
+}
+
+- (void)performCacheMutationForGetters:(const SEL *)getters
+ count:(NSUInteger)count
+ block:(dispatch_block_t)block
+{
+ @synchronized (_cacheUpdateTokens) {
+ BOOL pending[count];
+ for (NSUInteger index = 0; index < count; index++) {
+ pending[index] = [self invalidateCacheUpdateForGetter:getters[index]];
+ }
+ block();
+
+ // Incremental changes cannot replace a pending membership/order query.
+ // Reconcile again unless the mutation already started a newer query.
+ for (NSUInteger index = 0; index < count; index++) {
+ const SEL getter = getters[index];
+ if (!pending[index] || [_pendingCacheUpdates containsObject:[NSValue valueWithPointer:getter]]) {
+ continue;
+ }
+ if (getter == @selector(cachedAudioMedia))
+ [self resetCachedListOfAudioMedia];
+ else if (getter == @selector(cachedVideoMedia))
+ [self resetCachedListOfVideoMedia];
+ else if (getter == @selector(cachedRecentAudioMedia))
+ [self resetCachedListOfRecentAudioMedia];
+ else if (getter == @selector(cachedRecentMedia))
+ [self resetCachedListOfRecentMedia];
+ else if (getter == @selector(cachedArtists))
+ [self resetCachedListOfArtists];
+ else if (getter == @selector(cachedAlbums))
+ [self resetCachedListOfAlbums];
+ else if (getter == @selector(cachedGenres))
+ [self resetCachedListOfGenres];
+ else if (getter == @selector(cachedListOfGroups))
+ [self resetCachedListOfGroups];
+ else if (getter == @selector(cachedListOfShows))
+ [self resetCachedListOfShows];
+ }
+ }
+}
+
+- (void)performCacheMutationForGetter:(SEL)getter block:(dispatch_block_t)block
+{
+ SEL const getters[] = { getter };
+ [self performCacheMutationForGetters:getters count:1 block:block];
+}
+
+- (void)performCacheWriteForGeneration:(NSUInteger)generation
+ token:(NSObject *)token
+ getter:(SEL)getter
+ onQueue:(dispatch_queue_t)queue
+ block:(dispatch_block_t)block
+{
+ dispatch_barrier_async(queue, ^{
+ // Validate at the write, not when the background query finishes.
+ @synchronized (self->_cacheUpdateTokens) {
+ if (generation == self.cacheGeneration && [self isCurrentCacheUpdate:token forGetter:getter]) {
+ [self->_pendingCacheUpdates removeObject:[NSValue valueWithPointer:getter]];
+ block();
+ }
+ }
});
}
- (void)resetCachedListOfAudioMedia
{
+ const NSUInteger generation = self.cacheGeneration;
+ NSObject * const token = [self beginCacheUpdateForGetter:@selector(cachedAudioMedia)];
+ NSString * const filterString = [self.filterString copy];
+ const vlc_ml_query_params_t capturedParams = [self queryParams];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
- const vlc_ml_query_params_t queryParams = [self queryParams];
+ vlc_ml_query_params_t queryParams = capturedParams;
+ queryParams.psz_pattern = filterString.length > 0 ? filterString.UTF8String : NULL;
vlc_ml_media_list_t * const p_media_list = vlc_ml_list_audio_media(self->_p_mediaLibrary, &queryParams);
NSArray * const mediaArray = [NSArray arrayFromVlcMediaList:p_media_list];
if (mediaArray == nil) {
return;
}
vlc_ml_media_list_release(p_media_list);
- dispatch_async(dispatch_get_main_queue(), ^{
- self.cachedAudioMedia = mediaArray;
+ [self performCacheWriteForGeneration:generation
+ token:token
+ getter:@selector(cachedAudioMedia)
+ onQueue:self->_mediaItemCacheModificationQueue
+ block:^{
+ [self replaceMediaTitlesFromMedia:self->_cachedAudioMedia withMedia:mediaArray];
+ self->_cachedAudioMedia = mediaArray;
[self performAfterCacheWritesOnQueue:self->_mediaItemCacheModificationQueue block:^{
[self.changeDelegate notifyChange:VLCLibraryModelAudioMediaListReset withObject:self];
}];
- });
+ }];
});
}
@@ -635,8 +758,13 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)resetCachedListOfArtists
{
+ const NSUInteger generation = self.cacheGeneration;
+ NSObject * const token = [self beginCacheUpdateForGetter:@selector(cachedArtists)];
+ NSString * const filterString = [self.filterString copy];
+ const vlc_ml_query_params_t capturedParams = [self queryParams];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
- const vlc_ml_query_params_t queryParams = [self queryParams];
+ vlc_ml_query_params_t queryParams = capturedParams;
+ queryParams.psz_pattern = filterString.length > 0 ? filterString.UTF8String : NULL;
vlc_ml_artist_list_t * const p_artist_list = vlc_ml_list_artists(self->_p_mediaLibrary, &queryParams, YES);
const size_t numberOfArtists = p_artist_list->i_nb_items;
NSMutableArray * const mutableArtistArray = [[NSMutableArray alloc] initWithCapacity:numberOfArtists];
@@ -651,12 +779,19 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
vlc_ml_artist_list_release(p_artist_list);
- dispatch_async(dispatch_get_main_queue(), ^{
- self.cachedArtists = mutableArtistArray.copy;
+ [self performCacheWriteForGeneration:generation
+ token:token
+ getter:@selector(cachedArtists)
+ onQueue:self->_artistCacheModificationQueue
+ block:^{
+ self->_cachedArtists = mutableArtistArray.copy;
[self performAfterCacheWritesOnQueue:self->_artistCacheModificationQueue block:^{
+ if (![self isCurrentCacheUpdate:token forGetter:@selector(cachedArtists)]) {
+ return;
+ }
[self.changeDelegate notifyChange:VLCLibraryModelArtistListReset withObject:self];
}];
- });
+ }];
});
}
@@ -682,8 +817,13 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)resetCachedListOfAlbums
{
+ const NSUInteger generation = self.cacheGeneration;
+ NSObject * const token = [self beginCacheUpdateForGetter:@selector(cachedAlbums)];
+ NSString * const filterString = [self.filterString copy];
+ const vlc_ml_query_params_t capturedParams = [self queryParams];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
- const vlc_ml_query_params_t queryParams = [self queryParams];
+ vlc_ml_query_params_t queryParams = capturedParams;
+ queryParams.psz_pattern = filterString.length > 0 ? filterString.UTF8String : NULL;
vlc_ml_album_list_t * const p_album_list = vlc_ml_list_albums(self->_p_mediaLibrary, &queryParams);
const size_t numberOfAlbums = p_album_list->i_nb_items;
NSMutableArray * const mutableAlbumArray = [[NSMutableArray alloc] initWithCapacity:numberOfAlbums];
@@ -695,12 +835,19 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
vlc_ml_album_list_release(p_album_list);
- dispatch_async(dispatch_get_main_queue(), ^{
- self.cachedAlbums = mutableAlbumArray.copy;
+ [self performCacheWriteForGeneration:generation
+ token:token
+ getter:@selector(cachedAlbums)
+ onQueue:self->_albumCacheModificationQueue
+ block:^{
+ self->_cachedAlbums = mutableAlbumArray.copy;
[self performAfterCacheWritesOnQueue:self->_albumCacheModificationQueue block:^{
+ if (![self isCurrentCacheUpdate:token forGetter:@selector(cachedAlbums)]) {
+ return;
+ }
[self.changeDelegate notifyChange:VLCLibraryModelAlbumListReset withObject:self];
}];
- });
+ }];
});
}
@@ -726,8 +873,13 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)resetCachedListOfGenres
{
+ const NSUInteger generation = self.cacheGeneration;
+ NSObject * const token = [self beginCacheUpdateForGetter:@selector(cachedGenres)];
+ NSString * const filterString = [self.filterString copy];
+ const vlc_ml_query_params_t capturedParams = [self queryParams];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
- const vlc_ml_query_params_t queryParams = [self queryParams];
+ vlc_ml_query_params_t queryParams = capturedParams;
+ queryParams.psz_pattern = filterString.length > 0 ? filterString.UTF8String : NULL;
vlc_ml_genre_list_t * const p_genre_list = vlc_ml_list_genres(self->_p_mediaLibrary, &queryParams);
const size_t numberOfGenres = p_genre_list->i_nb_items;
NSMutableArray * const mutableGenreArray = [[NSMutableArray alloc] initWithCapacity:numberOfGenres];
@@ -739,12 +891,19 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
vlc_ml_genre_list_release(p_genre_list);
- dispatch_async(dispatch_get_main_queue(), ^{
- self.cachedGenres = mutableGenreArray.copy;
+ [self performCacheWriteForGeneration:generation
+ token:token
+ getter:@selector(cachedGenres)
+ onQueue:self->_genreCacheModificationQueue
+ block:^{
+ self->_cachedGenres = mutableGenreArray.copy;
[self performAfterCacheWritesOnQueue:self->_genreCacheModificationQueue block:^{
+ if (![self isCurrentCacheUpdate:token forGetter:@selector(cachedGenres)]) {
+ return;
+ }
[self.changeDelegate notifyChange:VLCLibraryModelGenreListReset withObject:self];
}];
- });
+ }];
});
}
@@ -771,8 +930,13 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)resetCachedListOfVideoMedia
{
+ const NSUInteger generation = self.cacheGeneration;
+ NSObject * const token = [self beginCacheUpdateForGetter:@selector(cachedVideoMedia)];
+ NSString * const filterString = [self.filterString copy];
+ const vlc_ml_query_params_t capturedParams = [self queryParams];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
- const vlc_ml_query_params_t queryParameters = [self queryParams];
+ vlc_ml_query_params_t queryParameters = capturedParams;
+ queryParameters.psz_pattern = filterString.length > 0 ? filterString.UTF8String : NULL;
vlc_ml_media_list_t *p_media_list = vlc_ml_list_video_media(self->_p_mediaLibrary, &queryParameters);
if (p_media_list == NULL) {
return;
@@ -785,12 +949,18 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
}
}
vlc_ml_media_list_release(p_media_list);
- dispatch_async(dispatch_get_main_queue(), ^{
- self.cachedVideoMedia = [mutableArray copy];
+ [self performCacheWriteForGeneration:generation
+ token:token
+ getter:@selector(cachedVideoMedia)
+ onQueue:self->_mediaItemCacheModificationQueue
+ block:^{
+ NSArray<VLCMediaLibraryMediaItem *> * const media = mutableArray.copy;
+ [self replaceMediaTitlesFromMedia:self->_cachedVideoMedia withMedia:media];
+ self->_cachedVideoMedia = media;
[self performAfterCacheWritesOnQueue:self->_mediaItemCacheModificationQueue block:^{
[self.changeDelegate notifyChange:VLCLibraryModelVideoMediaListReset withObject:self];
}];
- });
+ }];
});
}
@@ -883,10 +1053,11 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
withCountLimit:(size_t)countLimit
withCompletion:(void (^)(NSArray *recentMediaArray))completionHandler
{
+ NSString * const filterString = [self.filterString copy];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
vlc_ml_query_params_t queryParameters = vlc_ml_query_params_create();
queryParameters.i_nbResults = countLimit;
- queryParameters.psz_pattern = self->_filterString.length > 0 ? self->_filterString.UTF8String : NULL;
+ queryParameters.psz_pattern = filterString.length > 0 ? filterString.UTF8String : NULL;
// we don't set the sorting criteria here as they are not applicable to history
vlc_ml_media_list_t *p_media_list = NULL;
if (type == VLC_ML_MEDIA_TYPE_VIDEO)
@@ -915,12 +1086,20 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)resetCachedListOfRecentMedia
{
+ const NSUInteger generation = self.cacheGeneration;
+ NSObject * const token = [self beginCacheUpdateForGetter:@selector(cachedRecentMedia)];
[self getListOfRecentMediaOfType:VLC_ML_MEDIA_TYPE_VIDEO
withCountLimit:_recentMediaLimit
withCompletion:^(NSArray * const mediaArray) {
- self.cachedRecentMedia = mediaArray;
- [self performAfterCacheWritesOnQueue:self->_mediaItemCacheModificationQueue block:^{
- [self.changeDelegate notifyChange:VLCLibraryModelRecentsMediaListReset withObject:self];
+ [self performCacheWriteForGeneration:generation
+ token:token
+ getter:@selector(cachedRecentMedia)
+ onQueue:self->_mediaItemCacheModificationQueue
+ block:^{
+ self->_cachedRecentMedia = mediaArray;
+ [self performAfterCacheWritesOnQueue:self->_mediaItemCacheModificationQueue block:^{
+ [self.changeDelegate notifyChange:VLCLibraryModelRecentsMediaListReset withObject:self];
+ }];
}];
}];
}
@@ -950,12 +1129,20 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)resetCachedListOfRecentAudioMedia
{
+ const NSUInteger generation = self.cacheGeneration;
+ NSObject * const token = [self beginCacheUpdateForGetter:@selector(cachedRecentAudioMedia)];
[self getListOfRecentMediaOfType:VLC_ML_MEDIA_TYPE_AUDIO
withCountLimit:_recentAudioMediaLimit
withCompletion:^(NSArray * const mediaArray) {
- self.cachedRecentAudioMedia = mediaArray;
- [self performAfterCacheWritesOnQueue:self->_mediaItemCacheModificationQueue block:^{
- [self.changeDelegate notifyChange:VLCLibraryModelRecentAudioMediaListReset withObject:self];
+ [self performCacheWriteForGeneration:generation
+ token:token
+ getter:@selector(cachedRecentAudioMedia)
+ onQueue:self->_mediaItemCacheModificationQueue
+ block:^{
+ self->_cachedRecentAudioMedia = mediaArray;
+ [self performAfterCacheWritesOnQueue:self->_mediaItemCacheModificationQueue block:^{
+ [self.changeDelegate notifyChange:VLCLibraryModelRecentAudioMediaListReset withObject:self];
+ }];
}];
}];
}
@@ -982,6 +1169,8 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)resetCachedListOfShows
{
+ const NSUInteger generation = self.cacheGeneration;
+ NSObject * const token = [self beginCacheUpdateForGetter:@selector(cachedListOfShows)];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
vlc_ml_show_list_t * const p_show_list = vlc_ml_list_shows(self->_p_mediaLibrary, NULL);
if (p_show_list == NULL) {
@@ -997,17 +1186,23 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
}
}
vlc_ml_show_list_release(p_show_list);
- dispatch_async(dispatch_get_main_queue(), ^{
- self.cachedListOfShows = mutableArray.copy;
+ [self performCacheWriteForGeneration:generation
+ token:token
+ getter:@selector(cachedListOfShows)
+ onQueue:self->_mediaItemCacheModificationQueue
+ block:^{
+ self->_cachedListOfShows = mutableArray.copy;
[self performAfterCacheWritesOnQueue:self->_mediaItemCacheModificationQueue block:^{
[self.changeDelegate notifyChange:VLCLibraryModelListOfShowsReset withObject:self];
}];
- });
+ }];
});
}
- (void)resetCachedListOfMovies
{
+ const NSUInteger generation = self.cacheGeneration;
+ NSObject * const token = [self beginCacheUpdateForGetter:@selector(cachedListOfMovies)];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
vlc_ml_media_list_t * const p_movie_list = vlc_ml_list_movies(self->_p_mediaLibrary, NULL);
if (p_movie_list == NULL) {
@@ -1026,13 +1221,17 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
}
}
vlc_ml_media_list_release(p_movie_list);
- dispatch_async(dispatch_get_main_queue(), ^{
- self.cachedListOfMovies = mutableArray.copy;
+ [self performCacheWriteForGeneration:generation
+ token:token
+ getter:@selector(cachedListOfMovies)
+ onQueue:self->_mediaItemCacheModificationQueue
+ block:^{
+ self->_cachedListOfMovies = mutableArray.copy;
[self performAfterCacheWritesOnQueue:self->_mediaItemCacheModificationQueue block:^{
[self.changeDelegate notifyChange:VLCLibraryModelListOfMoviesReset withObject:self];
[NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelListOfMoviesReset object:self];
}];
- });
+ }];
});
}
@@ -1098,8 +1297,13 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)resetCachedListOfGroups
{
+ const NSUInteger generation = self.cacheGeneration;
+ NSObject * const token = [self beginCacheUpdateForGetter:@selector(cachedListOfGroups)];
+ NSString * const filterString = [self.filterString copy];
+ const vlc_ml_query_params_t capturedParams = [self queryParams];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
- const vlc_ml_query_params_t queryParams = [self queryParams];
+ vlc_ml_query_params_t queryParams = capturedParams;
+ queryParams.psz_pattern = filterString.length > 0 ? filterString.UTF8String : NULL;
vlc_ml_group_list_t * const p_group_list =
vlc_ml_list_groups(self->_p_mediaLibrary, &queryParams);
if (p_group_list == NULL) {
@@ -1115,12 +1319,16 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
}
}
vlc_ml_group_list_release(p_group_list);
- dispatch_async(dispatch_get_main_queue(), ^{
- self.cachedListOfGroups = mutableArray.copy;
+ [self performCacheWriteForGeneration:generation
+ token:token
+ getter:@selector(cachedListOfGroups)
+ onQueue:self->_groupCacheModificationQueue
+ block:^{
+ self->_cachedListOfGroups = mutableArray.copy;
[self performAfterCacheWritesOnQueue:self->_groupCacheModificationQueue block:^{
[self.changeDelegate notifyChange:VLCLibraryModelListOfGroupsReset withObject:self];
}];
- });
+ }];
});
}
@@ -1174,6 +1382,8 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)resetCachedListOfMonitoredFolders
{
+ const NSUInteger generation = self.cacheGeneration;
+ NSObject * const token = [self beginCacheUpdateForGetter:@selector(cachedListOfMonitoredFolders)];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
vlc_ml_folder_list_t *pp_entrypoints = vlc_ml_list_entry_points(self->_p_mediaLibrary, NULL);
if (pp_entrypoints == NULL) {
@@ -1194,7 +1404,11 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
vlc_ml_folder_list_release(pp_entrypoints);
- dispatch_async(dispatch_get_main_queue(), ^{
+ [self performCacheWriteForGeneration:generation
+ token:token
+ getter:@selector(cachedListOfMonitoredFolders)
+ onQueue:self->_mediaItemCacheModificationQueue
+ block:^{
// Stop old observers before creating new ones to avoid duplicate
// FSEvent streams for the same paths
self.folderObservers = nil;
@@ -1210,12 +1424,12 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
}
}
- self.cachedListOfMonitoredFolders = mutableArray.copy;
+ self->_cachedListOfMonitoredFolders = mutableArray.copy;
self.folderObservers = mutableObservers.copy;
[self performAfterCacheWritesOnQueue:self->_mediaItemCacheModificationQueue block:^{
[self.changeDelegate notifyChange:VLCLibraryModelListOfMonitoredFoldersUpdated withObject:self];
}];
- });
+ }];
});
}
@@ -1311,6 +1525,11 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)dropCaches
{
+ @synchronized (_cacheUpdateTokens) {
+ self.cacheGeneration++;
+ [_cacheUpdateTokens removeAllObjects];
+ [_pendingCacheUpdates removeAllObjects];
+ }
dispatch_queue_t const cacheQueues[] = {
_mediaItemCacheModificationQueue,
_albumCacheModificationQueue,
@@ -1366,7 +1585,11 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
const NSUInteger
))action
{
+ const NSUInteger generation = self.cacheGeneration;
dispatch_barrier_async(_mediaItemCacheModificationQueue, ^{
+ if (generation != self.cacheGeneration) {
+ return;
+ }
BOOL (^idCheckBlock)(VLCMediaLibraryMediaItem * const, const NSUInteger, BOOL * const) = ^BOOL(VLCMediaLibraryMediaItem * const mediaItem, const NSUInteger __unused idx, BOOL * const __unused stop) {
NSAssert(mediaItem != nil, @"Cache list should not contain nil media items");
return mediaItem.libraryID == libraryId;
@@ -1401,12 +1624,15 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
NSMutableArray<VLCMediaLibraryShow *> * const showsMutable =
showIndex == NSNotFound ? nil : cachedShows.mutableCopy;
- action(videoMutable, videoIndex, recentsMutable, recentsIndex, showsMutable, showIndex, episodeIndex);
- // Item handlers adjust title counts individually inside this barrier.
- self->_cachedVideoMedia = videoMutable.copy;
- self.cachedRecentMedia = recentsMutable.copy;
- if (showsMutable)
- self.cachedListOfShows = showsMutable.copy;
+ SEL const getters[] = { @selector(cachedVideoMedia), @selector(cachedRecentMedia), @selector(cachedListOfShows) };
+ [self performCacheMutationForGetters:getters count:showsMutable != nil ? 3 : 2 block:^{
+ action(videoMutable, videoIndex, recentsMutable, recentsIndex, showsMutable, showIndex, episodeIndex);
+ // Item handlers adjust title counts individually inside this barrier.
+ self->_cachedVideoMedia = videoMutable.copy;
+ self.cachedRecentMedia = recentsMutable.copy;
+ if (showsMutable)
+ self.cachedListOfShows = showsMutable.copy;
+ }];
return;
}
@@ -1422,9 +1648,12 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
NSMutableArray<VLCMediaLibraryMediaItem *> * const recentAudiosMutable = cachedRecentAudios.mutableCopy;
NSMutableArray<VLCMediaLibraryMediaItem *> * const audioMutable = cachedAudios.mutableCopy;
- action(audioMutable, audioIndex, recentAudiosMutable, recentAudiosIndex, nil, NSNotFound, NSNotFound);
- self->_cachedAudioMedia = audioMutable.copy;
- self.cachedRecentAudioMedia = recentAudiosMutable.copy;
+ SEL const getters[] = { @selector(cachedAudioMedia), @selector(cachedRecentAudioMedia) };
+ [self performCacheMutationForGetters:getters count:2 block:^{
+ action(audioMutable, audioIndex, recentAudiosMutable, recentAudiosIndex, nil, NSNotFound, NSNotFound);
+ self->_cachedAudioMedia = audioMutable.copy;
+ self.cachedRecentAudioMedia = recentAudiosMutable.copy;
+ }];
return;
}
@@ -1612,6 +1841,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)updateAudioGroupItem:(const id<VLCMediaLibraryAudioGroupProtocol>)audioGroupItem
usingGetter:(NSArray *(^)(void))cacheGetter
+ forCache:(SEL)getter
usingSetter:(const SEL)setterSelector
usingQueue:(const dispatch_queue_t)queue
withNotificationName:(const NSNotificationName)notificationName
@@ -1638,9 +1868,11 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
NSMutableArray * const mutableAudioGroupCache = [cache mutableCopy];
[mutableAudioGroupCache replaceObjectAtIndex:audioGroupIndex withObject:audioGroupItem];
- const IMP cacheSetterImp = [self methodForSelector:setterSelector];
- void (*cacheSetterFunction)(id, SEL, NSArray *) = (void *)cacheSetterImp;
- cacheSetterFunction(self, setterSelector, mutableAudioGroupCache.copy);
+ [self performCacheMutationForGetter:getter block:^{
+ const IMP cacheSetterImp = [self methodForSelector:setterSelector];
+ void (*cacheSetterFunction)(id, SEL, NSArray *) = (void *)cacheSetterImp;
+ cacheSetterFunction(self, setterSelector, mutableAudioGroupCache.copy);
+ }];
[self performAfterCacheWritesOnQueue:queue block:^{
[self.changeDelegate notifyChange:notificationName withObject:audioGroupItem];
@@ -1650,6 +1882,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
- (void)deleteAudioGroupItemWithId:(const int64_t)itemId
usingGetter:(NSArray *(^)(void))cacheGetter
+ forCache:(SEL)getter
usingSetter:(const SEL)setterSelector
usingQueue:(const dispatch_queue_t)queue
withNotificationName:(const NSNotificationName)notificationName
@@ -1677,9 +1910,11 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
NSMutableArray * const mutableAudioGroupCache = [cache mutableCopy];
[mutableAudioGroupCache removeObjectAtIndex:audioGroupIndex];
- const IMP cacheSetterImp = [self methodForSelector:setterSelector];
- void (*cacheSetterFunction)(id, SEL, NSArray *) = (void *)cacheSetterImp;
- cacheSetterFunction(self, setterSelector, mutableAudioGroupCache.copy);
+ [self performCacheMutationForGetter:getter block:^{
+ const IMP cacheSetterImp = [self methodForSelector:setterSelector];
+ void (*cacheSetterFunction)(id, SEL, NSArray *) = (void *)cacheSetterImp;
+ cacheSetterFunction(self, setterSelector, mutableAudioGroupCache.copy);
+ }];
[self performAfterCacheWritesOnQueue:queue block:^{
[self.changeDelegate notifyChange:notificationName withObject:audioGroupItem];
@@ -1701,6 +1936,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
[self updateAudioGroupItem:album
usingGetter:^NSArray *{ return self->_cachedAlbums; }
+ forCache:@selector(cachedAlbums)
usingSetter:@selector(setCachedAlbums:)
usingQueue:_albumCacheModificationQueue
withNotificationName:VLCLibraryModelAlbumUpdated];
@@ -1714,6 +1950,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
[self deleteAudioGroupItemWithId:itemId
usingGetter:^NSArray *{ return self->_cachedAlbums; }
+ forCache:@selector(cachedAlbums)
usingSetter:@selector(setCachedAlbums:)
usingQueue:_albumCacheModificationQueue
withNotificationName:VLCLibraryModelAlbumDeleted];
@@ -1733,6 +1970,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
[self updateAudioGroupItem:artist
usingGetter:^NSArray *{ return self->_cachedArtists; }
+ forCache:@selector(cachedArtists)
usingSetter:@selector(setCachedArtists:)
usingQueue:_artistCacheModificationQueue
withNotificationName:VLCLibraryModelArtistUpdated];
@@ -1746,6 +1984,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
[self deleteAudioGroupItemWithId:itemId
usingGetter:^NSArray *{ return self->_cachedArtists; }
+ forCache:@selector(cachedArtists)
usingSetter:@selector(setCachedArtists:)
usingQueue:_artistCacheModificationQueue
withNotificationName:VLCLibraryModelArtistDeleted];
@@ -1765,6 +2004,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
[self updateAudioGroupItem:genre
usingGetter:^NSArray *{ return self->_cachedGenres; }
+ forCache:@selector(cachedGenres)
usingSetter:@selector(setCachedGenres:)
usingQueue:_genreCacheModificationQueue
withNotificationName:VLCLibraryModelGenreUpdated];
@@ -1778,6 +2018,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
[self deleteAudioGroupItemWithId:itemId
usingGetter:^NSArray *{ return self->_cachedGenres; }
+ forCache:@selector(cachedGenres)
usingSetter:@selector(setCachedGenres:)
usingQueue:_genreCacheModificationQueue
withNotificationName:VLCLibraryModelGenreDeleted];
@@ -1811,7 +2052,9 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
NSMutableArray * const mutableGroups = cachedGroups.mutableCopy;
[mutableGroups removeObjectAtIndex:groupIdx];
- self.cachedListOfGroups = mutableGroups.copy;
+ [self performCacheMutationForGetter:@selector(cachedListOfGroups) block:^{
+ self.cachedListOfGroups = mutableGroups.copy;
+ }];
[self performAfterCacheWritesOnQueue:self->_groupCacheModificationQueue block:^{
[self->_defaultNotificationCenter postNotificationName:VLCLibraryModelGroupDeleted
@@ -1852,7 +2095,9 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
NSMutableArray * const mutableGroups = cachedGroups.mutableCopy;
[mutableGroups replaceObjectAtIndex:groupIdx withObject:group];
- self.cachedListOfGroups = mutableGroups.copy;
+ [self performCacheMutationForGetter:@selector(cachedListOfGroups) block:^{
+ self.cachedListOfGroups = mutableGroups.copy;
+ }];
[self performAfterCacheWritesOnQueue:self->_groupCacheModificationQueue block:^{
[self->_defaultNotificationCenter postNotificationName:VLCLibraryModelGroupUpdated
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1733d58c49cc5869c41ad295f29a98c830708fac...04d555a9d391f009d4f510f508fef58c39bdf810
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1733d58c49cc5869c41ad295f29a98c830708fac...04d555a9d391f009d4f510f508fef58c39bdf810
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help
More information about the vlc-commits
mailing list