Skip to content

Improve Move to Trash performance#488

Open
richyk1 wants to merge 1 commit intolosnoco:mainfrom
richyk1:perf/async-trash-operation
Open

Improve Move to Trash performance#488
richyk1 wants to merge 1 commit intolosnoco:mainfrom
richyk1:perf/async-trash-operation

Conversation

@richyk1
Copy link
Copy Markdown

@richyk1 richyk1 commented Apr 9, 2026

Problem

When using Cog with a large playlist and trashing songs via "Move to Trash" the UI freezes noticeably, anywhere from a few hundred milliseconds to several seconds depending on playlist size and whether App Sandbox is enabled. This makes the workflow of listening through a playlist and trashing unwanted songs feel sluggish.

Root Cause

The trashObjectsAtArrangedObjectIndexes: method in PlaylistController.m was doing all of its work synchronously on the main thread:

  1. NSFileManager trashItemAtURL: — the actual file move to Trash, which is especially slow under App Sandbox due to security framework coordination
  2. commitPersistentStore — a synchronous CoreData save (performBlockAndWait:) after removing entries from the playlist
  3. Shuffle resetresetShuffleListaddShuffledListToFront, each containing their own synchronous commitPersistentStore calls

With shuffle enabled, a single trash operation could trigger 3 synchronous CoreData saves plus the file I/O, all blocking the UI.

Changes

  • Move file trash operations to a background queuetrashItemAtURL: calls are dispatched via dispatch_async. File URLs and CoreData objectIDs are captured on the main thread first (since PlaylistEntry is a managed object), then the file operations run in the background. After completion, trashUrl is updated on the CoreData context queue via performBlock:.
  • Add commitPersistentStoreAsync — a new method using performBlock: (non-blocking) instead of performBlockAndWait: (blocking).
  • Switch shuffle methods to async savesaddShuffledListToFront, addShuffledListToBack, and resetShuffleList now use commitPersistentStoreAsync. These methods only persist shuffle indexes, which are regenerated on app launch anyway, so there's no durability concern.
  • Remove redundant synchronous save from the trash path — the background block handles persistence after the file operations complete.

Benchmarks

Measured on a playlist with ~2000 entries, trashing 1 entry at a time (Debug build, shuffle enabled):

Before (all synchronous on main thread)

TOTAL: 0.270s | UI removal: 0.085s | CoreData save: 0.000s | Shuffle reset: 0.175s | File trash: 0.011s
TOTAL: 0.200s | UI removal: 0.108s | CoreData save: 0.000s | Shuffle reset: 0.089s | File trash: 0.002s
TOTAL: 0.161s | UI removal: 0.090s | CoreData save: 0.000s | Shuffle reset: 0.069s | File trash: 0.002s

After (file trash + CoreData saves async)

TOTAL: 0.117s | UI removal: 0.111s | Shuffle reset: 0.005s | File trash: async
TOTAL: 0.109s | UI removal: 0.104s | Shuffle reset: 0.005s | File trash: async
TOTAL: 0.117s | UI removal: 0.111s | Shuffle reset: 0.006s | File trash: async
TOTAL: 0.064s | UI removal: 0.059s | Shuffle reset: 0.005s | File trash: async

These improvements scale further with larger playlists and under App Sandbox, where the file trash and CoreData save costs are significantly higher.


I'm not sure what your policy is regarding clanker PRs but I used Claude to identify and fix the issue after experiencing it myself. Let me know if what needs to be changed and I will revise my PR. :)

Move NSFileManager trashItemAtURL: calls to a background queue and switch
shuffle-related CoreData saves from synchronous (performBlockAndWait:) to
asynchronous (performBlock:), eliminating multiple sources of main thread
blocking during the trash operation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@richyk1 richyk1 marked this pull request as ready for review April 9, 2026 18:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant