[vlc-commits] [Git][videolan/vlc][master] 5 commits: macosx: add missing autorelease pool in media tree callback

Steve Lhomme (@robUx4) gitlab at videolan.org
Wed Sep 2 08:19:33 UTC 2026



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
63e66e47 by Alexandre Janniaux at 2026-09-02T06:40:21+00:00
macosx: add missing autorelease pool in media tree callback

cb_preparse_ended() is invoked from the preparser thread notifying the
media tree, which has no autorelease pool in place. It builds its user
info with a dictionary literal, which actually uses autoreferences and
thus returns an autoreleased object.

ARC only balances the caller side reference, it does not guarantee that
the value bypasses the pool. When it does not apply, the object is
autoreleased without pools to accumulate it, and it can accumulate to
the thread TLS until it exits.

This can be observed with OBJC_DEBUG_MISSING_POOLS=YES, tested on an
example runnable in darling:

    objc[235]: MISSING POOLS: (0x7d978fafe000) Object 0x7d978f904130 of
    class ProbeItem autoreleased with no pool in place - just leaking -
    break on objc_autoreleaseNoPool() to debug

Wrap the body into an @autoreleasepool block, like the other core
callbacks of this interface do.

- - - - -
04eab785 by Alexandre Janniaux at 2026-09-02T06:40:21+00:00
macosx: add missing autorelease pool in media library callback

libraryCallback() is invoked from a media library thread, which has no
autorelease pool in place. The events it dispatches directly build
autoreleased objects via [Class fooWithXxx] calls and strings are
converted using toNSStr which uses autoreleased strings via calls to
+[NSString stringWithUTF8String:].

ARC only balances the caller side reference, it does not guarantee that
the value bypasses the pool. When it does not apply, the object is
autoreleased with no pool in place at the thread destruction. In
addition, the memory accumulate as the callback function is called.

Wrap the event dispatch into an @autoreleasepool block.

- - - - -
e7b3b71d by Alexandre Janniaux at 2026-09-02T06:40:21+00:00
macosx: add missing autorelease pool in thumbnailer callback

vlcThumbnailerToFilesOnEnded() is invoked from a preparser thread, which
has no autorelease pool in place, and it delegates to the
thumbnailRequest callback delegate and the UI completion handler which
might generate autoreleased references.

Wrap the call into an @autoreleasepool block. The request release stays
outside of it since it does not involve Objective-C objects.

- - - - -
889b0215 by Alexandre Janniaux at 2026-09-02T06:40:21+00:00
macosx: add missing autorelease pool in play queue callbacks

cb_playlist_items_reset() and cb_playlist_items_added() are invoked from
the playlist thread, which has no autorelease pool configured. Both are
using autoreleased referenced constructors for their list item as they
call +[NSMutableArray arrayWithCapacity:] and toNSStr().

ARC only balances the caller side reference, it does not guarantee that
the value bypasses the pool. When it does not apply, the objects are
accumulated in thread TLS and destroyed only at the thread termination.

Wrap into an @autoreleasepool block.

- - - - -
f1750769 by Alexandre Janniaux at 2026-09-02T06:40:21+00:00
macosx: document that toNSStr returns an autoreleased string

A lot of users of toNSStr() are not properly using @autoreleasepool so
mention it explicitly in the documentation.

- - - - -


5 changed files:

- modules/gui/macosx/extensions/NSString+Helpers.h
- modules/gui/macosx/library/VLCLibraryImageCache.m
- modules/gui/macosx/library/VLCLibraryModel.m
- modules/gui/macosx/library/media-source/VLCMediaSource.m
- modules/gui/macosx/playqueue/VLCPlayQueueController.m


Changes:

=====================================
modules/gui/macosx/extensions/NSString+Helpers.h
=====================================
@@ -60,6 +60,12 @@ extern NSString *const kVLCMediaVideoTSFolder;
 extern NSString *const kVLCMediaBDMVFolder;
 extern NSString *const kVLCMediaUnknown;
 
+/**
+ * Convert an UTF-8 C string to a NSString.
+ *
+ * The returned string is autoreleased, so callers must take care of
+ * setting up an appropriate @autoreleasepool block where suitable.
+ */
 NSString *toNSStr(const char *str);
 
 /**


=====================================
modules/gui/macosx/library/VLCLibraryImageCache.m
=====================================
@@ -71,11 +71,13 @@ static void vlcThumbnailerToFilesOnEnded(vlc_preparser_req *req,
                                          size_t resultCount,
                                          void *data)
 {
-    VLCThumbnailRequest * const request = (__bridge VLCThumbnailRequest *)data;
-    [request.imageCache thumbnailRequest:request
-                     didFinishWithStatus:status
-                                 results:results
-                             resultCount:resultCount];
+    @autoreleasepool {
+        VLCThumbnailRequest * const request = (__bridge VLCThumbnailRequest *)data;
+        [request.imageCache thumbnailRequest:request
+                         didFinishWithStatus:status
+                                     results:results
+                                 resultCount:resultCount];
+    }
     vlc_preparser_req_Release(req);
 }
 


=====================================
modules/gui/macosx/library/VLCLibraryModel.m
=====================================
@@ -157,127 +157,129 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
         return;
     }
 
-    switch(p_event->i_type)
-    {
-        case VLC_ML_EVENT_MEDIA_ADDED:
-            [libraryModel handleMediaItemAddedEvent:p_event];
-            break;
-        case VLC_ML_EVENT_MEDIA_UPDATED:
-            [libraryModel handleMediaItemUpdateEvent:p_event];
-            break;
-        case VLC_ML_EVENT_MEDIA_DELETED:
-            [libraryModel handleMediaItemDeletionEvent:p_event];
-            break;
-        case VLC_ML_EVENT_MEDIA_THUMBNAIL_GENERATED:
-            if (p_event->media_thumbnail_generated.b_success) {
-                VLCMediaLibraryMediaItem *mediaItem = [[VLCMediaLibraryMediaItem alloc] initWithMediaItem:(struct vlc_ml_media_t *)p_event->media_thumbnail_generated.p_media];
-                if (mediaItem == nil) {
-                    return;
+    @autoreleasepool {
+        switch(p_event->i_type)
+        {
+            case VLC_ML_EVENT_MEDIA_ADDED:
+                [libraryModel handleMediaItemAddedEvent:p_event];
+                break;
+            case VLC_ML_EVENT_MEDIA_UPDATED:
+                [libraryModel handleMediaItemUpdateEvent:p_event];
+                break;
+            case VLC_ML_EVENT_MEDIA_DELETED:
+                [libraryModel handleMediaItemDeletionEvent:p_event];
+                break;
+            case VLC_ML_EVENT_MEDIA_THUMBNAIL_GENERATED:
+                if (p_event->media_thumbnail_generated.b_success) {
+                    VLCMediaLibraryMediaItem *mediaItem = [[VLCMediaLibraryMediaItem alloc] initWithMediaItem:(struct vlc_ml_media_t *)p_event->media_thumbnail_generated.p_media];
+                    if (mediaItem == nil) {
+                        return;
+                    }
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        [libraryModel mediaItemThumbnailGenerated:mediaItem];
+                    });
                 }
+                break;
+            case VLC_ML_EVENT_ARTIST_ADDED:
+                [libraryModel resetCachedListOfArtists];
+                break;
+            case VLC_ML_EVENT_ARTIST_UPDATED:
+                [libraryModel handleArtistUpdateEvent:p_event];
+                break;
+            case VLC_ML_EVENT_ARTIST_DELETED:
+                [libraryModel handleArtistDeletionEvent:p_event];
+                break;
+            case VLC_ML_EVENT_ALBUM_ADDED:
+                [libraryModel resetCachedListOfAlbums];
+                break;
+            case VLC_ML_EVENT_ALBUM_UPDATED:
+                [libraryModel handleAlbumUpdateEvent:p_event];
+                break;
+            case VLC_ML_EVENT_ALBUM_DELETED:
+                [libraryModel handleAlbumDeletionEvent:p_event];
+                break;
+            case VLC_ML_EVENT_GENRE_ADDED:
+                [libraryModel resetCachedListOfGenres];
+                break;
+            case VLC_ML_EVENT_GENRE_UPDATED:
+                [libraryModel handleGenreUpdateEvent:p_event];
+                break;
+            case VLC_ML_EVENT_GENRE_DELETED:
+                [libraryModel handleGenreDeletionEvent:p_event];
+                break;
+            case VLC_ML_EVENT_GROUP_ADDED:
+                [libraryModel resetCachedListOfGroups];
+                break;
+            case VLC_ML_EVENT_GROUP_UPDATED:
+                [libraryModel handleGroupUpdateEvent:p_event];
+                break;
+            case VLC_ML_EVENT_GROUP_DELETED:
+                [libraryModel handleGroupDeletionEvent:p_event];
+                break;
+            case VLC_ML_EVENT_PLAYLIST_ADDED:
+                [libraryModel handlePlaylistAddedEvent:p_event];
+                break;
+            case VLC_ML_EVENT_PLAYLIST_UPDATED:
+                [libraryModel handlePlaylistUpdateEvent:p_event];
+                break;
+            case VLC_ML_EVENT_PLAYLIST_DELETED:
+                [libraryModel handlePlaylistDeletionEvent:p_event];
+                break;
+            case VLC_ML_EVENT_FOLDER_ADDED:
+            case VLC_ML_EVENT_FOLDER_UPDATED:
+            case VLC_ML_EVENT_FOLDER_DELETED:
+                [libraryModel resetCachedListOfMonitoredFolders];
+                break;
+            case VLC_ML_EVENT_HISTORY_CHANGED:
+                [libraryModel resetCachedListOfRecentMedia];
+                [libraryModel resetCachedListOfRecentAudioMedia];
+                break;
+            case VLC_ML_EVENT_FAVORITES_CHANGED:
+            {
                 dispatch_async(dispatch_get_main_queue(), ^{
-                    [libraryModel mediaItemThumbnailGenerated:mediaItem];
+                    [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteAudioMediaListReset object:libraryModel];
+                    [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteVideoMediaListReset object:libraryModel];
+                    [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteAlbumsListReset object:libraryModel];
+                    [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteArtistsListReset object:libraryModel];
+                    [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteGenresListReset object:libraryModel];
                 });
+                break;
             }
-            break;
-        case VLC_ML_EVENT_ARTIST_ADDED:
-            [libraryModel resetCachedListOfArtists];
-            break;
-        case VLC_ML_EVENT_ARTIST_UPDATED:
-            [libraryModel handleArtistUpdateEvent:p_event];
-            break;
-        case VLC_ML_EVENT_ARTIST_DELETED:
-            [libraryModel handleArtistDeletionEvent:p_event];
-            break;
-        case VLC_ML_EVENT_ALBUM_ADDED:
-            [libraryModel resetCachedListOfAlbums];
-            break;
-        case VLC_ML_EVENT_ALBUM_UPDATED:
-            [libraryModel handleAlbumUpdateEvent:p_event];
-            break;
-        case VLC_ML_EVENT_ALBUM_DELETED:
-            [libraryModel handleAlbumDeletionEvent:p_event];
-            break;
-        case VLC_ML_EVENT_GENRE_ADDED:
-            [libraryModel resetCachedListOfGenres];
-            break;
-        case VLC_ML_EVENT_GENRE_UPDATED:
-            [libraryModel handleGenreUpdateEvent:p_event];
-            break;
-        case VLC_ML_EVENT_GENRE_DELETED:
-            [libraryModel handleGenreDeletionEvent:p_event];
-            break;
-        case VLC_ML_EVENT_GROUP_ADDED:
-            [libraryModel resetCachedListOfGroups];
-            break;
-        case VLC_ML_EVENT_GROUP_UPDATED:
-            [libraryModel handleGroupUpdateEvent:p_event];
-            break;
-        case VLC_ML_EVENT_GROUP_DELETED:
-            [libraryModel handleGroupDeletionEvent:p_event];
-            break;
-        case VLC_ML_EVENT_PLAYLIST_ADDED:
-            [libraryModel handlePlaylistAddedEvent:p_event];
-            break;
-        case VLC_ML_EVENT_PLAYLIST_UPDATED:
-            [libraryModel handlePlaylistUpdateEvent:p_event];
-            break;
-        case VLC_ML_EVENT_PLAYLIST_DELETED:
-            [libraryModel handlePlaylistDeletionEvent:p_event];
-            break;
-        case VLC_ML_EVENT_FOLDER_ADDED:
-        case VLC_ML_EVENT_FOLDER_UPDATED:
-        case VLC_ML_EVENT_FOLDER_DELETED:
-            [libraryModel resetCachedListOfMonitoredFolders];
-            break;
-        case VLC_ML_EVENT_HISTORY_CHANGED:
-            [libraryModel resetCachedListOfRecentMedia];
-            [libraryModel resetCachedListOfRecentAudioMedia];
-            break;
-        case VLC_ML_EVENT_FAVORITES_CHANGED:
-        {
-            dispatch_async(dispatch_get_main_queue(), ^{
-                [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteAudioMediaListReset object:libraryModel];
-                [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteVideoMediaListReset object:libraryModel];
-                [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteAlbumsListReset object:libraryModel];
-                [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteArtistsListReset object:libraryModel];
-                [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteGenresListReset object:libraryModel];
-            });
-            break;
-        }
-        case VLC_ML_EVENT_DISCOVERY_STARTED:
-            dispatch_async(dispatch_get_main_queue(), ^{
-                NSNotificationCenter * const defaultCenter = NSNotificationCenter.defaultCenter;
-                [defaultCenter postNotificationName:VLCLibraryModelDiscoveryStarted object:nil];
-            });
-            break;
-        case VLC_ML_EVENT_DISCOVERY_PROGRESS:
-        {
-            NSString * const entryPoint = toNSStr(p_event->discovery_progress.psz_entry_point);
-            dispatch_async(dispatch_get_main_queue(), ^{
-                NSDictionary<NSString *, NSString *> * const info = entryPoint == nil
-                    ? nil
-                    : @{@"entryPoint": entryPoint};
-                NSNotificationCenter * const defaultCenter = NSNotificationCenter.defaultCenter;
-                [defaultCenter postNotificationName:VLCLibraryModelDiscoveryProgress
-                                             object:nil
-                                           userInfo:info];
-            });
-            break;
+            case VLC_ML_EVENT_DISCOVERY_STARTED:
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    NSNotificationCenter * const defaultCenter = NSNotificationCenter.defaultCenter;
+                    [defaultCenter postNotificationName:VLCLibraryModelDiscoveryStarted object:nil];
+                });
+                break;
+            case VLC_ML_EVENT_DISCOVERY_PROGRESS:
+            {
+                NSString * const entryPoint = toNSStr(p_event->discovery_progress.psz_entry_point);
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    NSDictionary<NSString *, NSString *> * const info = entryPoint == nil
+                        ? nil
+                        : @{@"entryPoint": entryPoint};
+                    NSNotificationCenter * const defaultCenter = NSNotificationCenter.defaultCenter;
+                    [defaultCenter postNotificationName:VLCLibraryModelDiscoveryProgress
+                                                 object:nil
+                                               userInfo:info];
+                });
+                break;
+            }
+            case VLC_ML_EVENT_DISCOVERY_COMPLETED:
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    NSNotificationCenter * const defaultCenter = NSNotificationCenter.defaultCenter;
+                    [defaultCenter postNotificationName:VLCLibraryModelDiscoveryCompleted object:nil];
+                });
+                break;
+            case VLC_ML_EVENT_DISCOVERY_FAILED:
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    NSNotificationCenter * const defaultCenter = NSNotificationCenter.defaultCenter;
+                    [defaultCenter postNotificationName:VLCLibraryModelDiscoveryFailed object:nil];
+                });
+                break;
+            default:
+                break;
         }
-        case VLC_ML_EVENT_DISCOVERY_COMPLETED:
-            dispatch_async(dispatch_get_main_queue(), ^{
-                NSNotificationCenter * const defaultCenter = NSNotificationCenter.defaultCenter;
-                [defaultCenter postNotificationName:VLCLibraryModelDiscoveryCompleted object:nil];
-            });
-            break;
-        case VLC_ML_EVENT_DISCOVERY_FAILED:
-            dispatch_async(dispatch_get_main_queue(), ^{
-                NSNotificationCenter * const defaultCenter = NSNotificationCenter.defaultCenter;
-                [defaultCenter postNotificationName:VLCLibraryModelDiscoveryFailed object:nil];
-            });
-            break;
-        default:
-            break;
     }
 }
 


=====================================
modules/gui/macosx/library/media-source/VLCMediaSource.m
=====================================
@@ -86,19 +86,21 @@ static void cb_preparse_ended(vlc_media_tree_t * __unused p_tree,
                               int status,
                               void *p_data)
 {
-    VLCMediaSource * const mediaSource = (__bridge VLCMediaSource *)p_data;
-    VLCInputItem * const inputItem = p_node->p_item == NULL
-        ? nil
-        : [[VLCInputItem alloc] initWithInputItem:p_node->p_item];
-    NSDictionary * const userInfo = inputItem == nil
-        ? @{ VLCMediaSourcePreparseStatusKey: @(status) }
-        : @{ VLCMediaSourcePreparseInputItemKey: inputItem,
-             VLCMediaSourcePreparseStatusKey: @(status) };
-    dispatch_async(dispatch_get_main_queue(), ^{
-        [NSNotificationCenter.defaultCenter postNotificationName:VLCMediaSourcePreparsingEnded
-                                                          object:mediaSource
-                                                        userInfo:userInfo];
-    });
+    @autoreleasepool {
+        VLCMediaSource * const mediaSource = (__bridge VLCMediaSource *)p_data;
+        VLCInputItem * const inputItem = p_node->p_item == NULL
+            ? nil
+            : [[VLCInputItem alloc] initWithInputItem:p_node->p_item];
+        NSDictionary * const userInfo = inputItem == nil
+            ? @{ VLCMediaSourcePreparseStatusKey: @(status) }
+            : @{ VLCMediaSourcePreparseInputItemKey: inputItem,
+                 VLCMediaSourcePreparseStatusKey: @(status) };
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [NSNotificationCenter.defaultCenter postNotificationName:VLCMediaSourcePreparsingEnded
+                                                              object:mediaSource
+                                                            userInfo:userInfo];
+        });
+    }
 }
 
 static const struct vlc_media_tree_callbacks treeCallbacks = {


=====================================
modules/gui/macosx/playqueue/VLCPlayQueueController.m
=====================================
@@ -79,15 +79,17 @@ cb_playlist_items_reset(vlc_playlist_t *const __unused playlist,
                         size_t numberOfItems,
                         void *p_data)
 {
-    NSMutableArray * const array = [NSMutableArray arrayWithCapacity:numberOfItems];
-    for (size_t i = 0; i < numberOfItems; i++) {
-        VLCPlayQueueItem * const item = [[VLCPlayQueueItem alloc] initWithPlaylistItem:items[i]];
-        [array addObject:item];
+    @autoreleasepool {
+        NSMutableArray * const array = [NSMutableArray arrayWithCapacity:numberOfItems];
+        for (size_t i = 0; i < numberOfItems; i++) {
+            VLCPlayQueueItem * const item = [[VLCPlayQueueItem alloc] initWithPlaylistItem:items[i]];
+            [array addObject:item];
+        }
+        dispatch_async(dispatch_get_main_queue(), ^{
+            VLCPlayQueueController * const playQueueController = (__bridge VLCPlayQueueController *)p_data;
+            [playQueueController playQueueResetWithItems:array];
+        });
     }
-    dispatch_async(dispatch_get_main_queue(), ^{
-        VLCPlayQueueController * const playQueueController = (__bridge VLCPlayQueueController *)p_data;
-        [playQueueController playQueueResetWithItems:array];
-    });
 }
 
 static void
@@ -97,15 +99,17 @@ cb_playlist_items_added(vlc_playlist_t *const __unused playlist,
                         size_t numberOfAddedItems,
                         void *p_data)
 {
-    NSMutableArray * const array = [NSMutableArray arrayWithCapacity:numberOfAddedItems];
-    for (size_t i = 0; i < numberOfAddedItems; i++) {
-        VLCPlayQueueItem * const item = [[VLCPlayQueueItem alloc] initWithPlaylistItem:items[i]];
-        [array addObject:item];
+    @autoreleasepool {
+        NSMutableArray * const array = [NSMutableArray arrayWithCapacity:numberOfAddedItems];
+        for (size_t i = 0; i < numberOfAddedItems; i++) {
+            VLCPlayQueueItem * const item = [[VLCPlayQueueItem alloc] initWithPlaylistItem:items[i]];
+            [array addObject:item];
+        }
+        dispatch_async(dispatch_get_main_queue(), ^{
+            VLCPlayQueueController * const playQueueController = (__bridge VLCPlayQueueController *)p_data;
+            [playQueueController playQueueAdded:array atIndex:insertionIndex count:numberOfAddedItems];
+        });
     }
-    dispatch_async(dispatch_get_main_queue(), ^{
-        VLCPlayQueueController * const playQueueController = (__bridge VLCPlayQueueController *)p_data;
-        [playQueueController playQueueAdded:array atIndex:insertionIndex count:numberOfAddedItems];
-    });
 }
 
 static void



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/c785d5da010b2607aaf409f378373fb826c22cd5...f175076990e14a714a359a1333921cb21d80fb6e

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/c785d5da010b2607aaf409f378373fb826c22cd5...f175076990e14a714a359a1333921cb21d80fb6e
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