gcd-operations
Skill13 starsChanged 7 months agoMITView on GitHub
Use for iOS/macOS concurrency problems involving dispatch queues, locks, or thread safety. Triggers on: deadlocks (sync on main, nested sync, ABBA lock ordering), thread explosion from too many DispatchQueue.global() calls, data races flagged by Thread Sanitizer (TSan), DispatchGroup enter/leave imbalance, DispatchSource timer leaks, lock selection (NSLock vs OSAllocatedUnfairLock vs os_unfair_lock), reader-writer barriers, AsyncOperation subclasses, dispatchPrecondition usage, and OperationQueue throttling. Also use when migrating GCD patterns to Swift Concurrency actors. Apply whenever someone asks about thread-safe properties, concurrent access to shared state, or sees TSan warnings in Apple platform code — even if they don't say 'GCD' or 'concurrency.'
In this repository11 other files it gives its agents.
- AGENTS.mdAGENTS.md
- ios-accessibilitySkill
- ios-localizationSkill
- ios-loggingSkill
- ios-securitySkill
- ios-testingSkill
- swift-concurrencySkill
- swiftui-mvvmSkill
- tca-swiftuiSkill
- uikit-mvvmSkill
- viper-uikitSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
24 rules. About 0 are common, 5 are distinctive, and 19 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
19
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 66
Check for thread explosion indicators: DispatchQueue.global() scattered throughout, many independent queues, no maxConcurrentOperationCount …
- Very specific: line 67
Verify lock correctness (references/thread-safety.md)
- Very specific: line 68
Check OperationQueue subclasses for missing KVO/state management (references/operation-queue.md)
- Very specific: line 69
Create refactoring/ directory with per-feature plans and severity-ranked findings (references/refactoring-workflow.md)
- Distinctive: line 76
Create a custom concurrent queue with descriptive label
- Very specific: line 79
Verify barrier is on custom queue, never global (references/thread-safety.md)
- Very specific: line 80
Add dispatchPrecondition assertions at boundaries
- Very specific: line 81
Test with DispatchQueue.concurrentPerform under Thread Sanitizer
- Very specific: line 88
Never call super.start() -- it marks the operation finished immediately
- Very specific: line 89
Check isCancelled at start, call finish() if cancelled
- Very specific: line 90
Call finish() from every completion path
- Very specific: line 91
Check dependency cancellation in main() -- cancelled deps satisfy dependencies, they don't block
- Distinctive: line 92
Test with Thread Sanitizer enabled
- Very specific: line 100
Keep as GCD: concurrentPerform, DispatchIO, DispatchSource, concurrent+barrier reader-writer
- Very specific: line 102
Never use DispatchSemaphore.wait() in any async context
- Distinctive: line 110
Use serial queues for state protection -- concurrent only when reads vastly outnumber writes
- Very specific: line 111
Use defer { group.leave() } immediately after every group.enter()
- Very specific: line 112
Use OSAllocatedUnfairLock (iOS 16+), NSLock (any iOS), or Mutex (Swift 6+) for short critical sections -- never os_unfair_lock as a direct …
- Very specific: line 113
Use dispatchPrecondition(condition:) before sync dispatch and UI updates
- Very specific: line 114
Use [weak self] in repeating timer handlers and stored closures
- Very specific: line 118
Never call DispatchQueue.main.sync from any code that might run on the main thread
- Very specific: line 119
Before generating concurrent code, output a brief <thought> analyzing potential deadlocks, races, and retention.
- Distinctive: line 125
When fixing concurrency bugs, you may encounter cascading issues.
- Distinctive: line 134
Before finalizing generated or refactored concurrent code, verify ALL:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
GCD & OperationQueue Concurrency
Enterprise-grade concurrency skill for Grand Central Dispatch and OperationQueue. Opinionated: prescribes serial queues by default, target queue hierarchies, labeled queues, NSLock/OSAllocatedUnfairLock for synchronization, barrier-based reader-writer on custom concurrent queues, and OperationQueue for dependency graphs. Apple has not deprecated any core GCD APIs — GCD remains appropriate for parallelism, system I/O, and performance-critical paths. This skill covers correct usage, deadly bug prevention, and coexistence with Swift Concurrency.
Concurrency Layers
Application Layer -> OperationQueue for dependency graphs, cancellation, throttling.
Dispatch Layer -> Serial queues for state protection, concurrent+barrier for R/W.
Synchronization -> NSLock / OSAllocatedUnfairLock for short critical sections.
System I/O -> DispatchSource (timers, file monitoring), DispatchIO (non-blocking file I/O).
Thread Pool -> GCD manages threads. App targets 3-4 well-defined queue subsystems.Quick Decision Trees
"Which queue type should I use?"
Do you need mutual exclusion (protecting shared state)?
+-- YES -> Serial queue (default). Simplest, safest.
+-- NO -> Is this independent parallel work (image processing, batch transforms)?
+-- YES -> Concurrent queue (custom) with barrier for any writes.
+-- NO -> Do you need dependency graphs, cancellation, or throttling?
+-- YES -> OperationQueue with maxConcurrentOperationCount.
+-- NO -> Serial queue. When in doubt, serial."Which lock should I use?"
Which lock?
+-- iOS 16+ -> OSAllocatedUnfairLock (Apple's safe wrapper)
+-- iOS 18+ / Swift 6 -> Mutex from Synchronization framework
+-- Any iOS -> NSLock (safe, simple, recommended by Apple DTS)
NEVER: os_unfair_lock as direct Swift stored property (memory corruption).
NEVER: DispatchSemaphore as a mutex (no priority donation)."Which QoS should I use?"
Is the user waiting and watching?
+-- Immediate response (animation, touch) -> .userInteractive
+-- User-triggered, blocking progress -> .userInitiated
+-- Long-running with progress bar -> .utility
+-- Invisible to user (prefetch, sync) -> .background
WARNING: .background QoS may be halted entirely in Low Power Mode.Workflows
Workflow: Review Existing Concurrency Code
When: First encounter with a codebase using GCD/OperationQueue.
- Scan for deadlock patterns using detection checklist (
references/deadlocks.md) - Scan for data race patterns (
references/data-races.md) - Check for thread explosion indicators:
DispatchQueue.global()scattered throughout, many independent queues, nomaxConcurrentOperationCount(references/thread-explosion.md) - Verify lock correctness (
references/thread-safety.md) - Check OperationQueue subclasses for missing KVO/state management (
references/operation-queue.md) - Create
refactoring/directory with per-feature plans and severity-ranked findings (references/refactoring-workflow.md) - Execute fixes in phase order: Critical → Thread Safety → Queue Architecture → OperationQueue → Monitoring
Workflow: Add Thread-Safe Collection
When: Need concurrent read/exclusive write access to shared state.
- Create a custom concurrent queue with descriptive label
- Implement reads via
queue.sync { return value } - Implement writes via
queue.async(flags: .barrier) { mutate } - Verify barrier is on custom queue, never global (
references/thread-safety.md) - Add
dispatchPreconditionassertions at boundaries - Test with
DispatchQueue.concurrentPerformunder Thread Sanitizer
Workflow: Create AsyncOperation Subclass
When: Need OperationQueue with async work (network, disk I/O).
- Override
isAsynchronous,isExecuting,isFinishedwith thread-safe KVO (references/operation-queue.md) - Never call
super.start()-- it marks the operation finished immediately - Check
isCancelledat start, callfinish()if cancelled - Call
finish()from every completion path - Check dependency cancellation in
main()-- cancelled deps satisfy dependencies, they don't block - Test with Thread Sanitizer enabled
Workflow: Migrate GCD Pattern to Swift Concurrency
When: Modernizing specific GCD patterns. Not all patterns should migrate.
- Identify the pattern category (
references/migration.md) - Easy:
DispatchQueue.main.async->@MainActor,DispatchGroup->TaskGroup, serial queue ->actor - Keep as GCD:
concurrentPerform,DispatchIO,DispatchSource, concurrent+barrier reader-writer - Bridge callbacks with
withCheckedThrowingContinuation-- resume exactly once on every path - Never use
DispatchSemaphore.wait()in any async context
Code Generation Rules
<critical_rules> Whether reviewing, generating, or refactoring concurrent code, every output must be thread-safe, deadlock-free, and production-ready. ALWAYS:
- Label every
DispatchQueuewith reverse DNS naming including subsystem - Use serial queues for state protection -- concurrent only when reads vastly outnumber writes
- Use
defer { group.leave() }immediately after everygroup.enter() - Use
OSAllocatedUnfairLock(iOS 16+),NSLock(any iOS), orMutex(Swift 6+) for short critical sections -- neveros_unfair_lockas a direct stored property - Use
dispatchPrecondition(condition:)before sync dispatch and UI updates - Use
[weak self]in repeating timer handlers and stored closures - Cancel
DispatchSourcetimers in deinit (resume first if suspended) - Override
isAsynchronous,isExecuting,isFinishedwith KVO in async Operations - Set
maxConcurrentOperationCounton OperationQueues -- never leave unlimited for blocking work - Never call
DispatchQueue.main.syncfrom any code that might run on the main thread - Before generating concurrent code, output a brief
<thought>analyzing potential deadlocks, races, and retention. </critical_rules>
Fallback Strategies & Loop Breakers
<fallback_strategies> When fixing concurrency bugs, you may encounter cascading issues. If you fail to fix the same issue twice, break the loop:
- Deadlock in sync dispatch: Replace
syncwithasyncand restructure the call site to use a completion handler or continuation. Why: eliminatingsynceliminates the entire deadlock category — async dispatch never blocks the caller. - Thread explosion from blocking work: Wrap blocking calls in Operations with
maxConcurrentOperationCount = 4instead of trying to make GCD limit threads. Why: GCD's thread pool grows unboundedly when work items block — OperationQueue is the only reliable throttle. - Data race under TSan: If concurrent queue + barrier is proving fragile, fall back to
NSLockprotecting a plain property. Why: barrier correctness requires remembering to use the barrier flag on every write and the exact same queue for every access — a lock has a simpler mental model with fewer ways to get wrong. </fallback_strategies>
Confidence Checks
Before finalizing generated or refactored concurrent code, verify ALL:
[] No deadlock risk -- no sync on main, no nested sync on same queue, no ABBA chains
[] No thread explosion -- maxConcurrentOperationCount set, no scattered global() calls
[] No data races -- shared mutable state protected by queue, lock, or actor
[] Queue labels -- every queue has reverse DNS label with subsystem name
[] Barrier correctness -- barriers on custom concurrent queues only, never global
[] Group balance -- every enter() has defer { group.leave() }
[] Timer safety -- [weak self], cancel in deinit, resume before dealloc if suspended
[] Lock safety -- no os_unfair_lock as stored property, no semaphore as mutex
[] Operation KVO -- async operations override isExecuting/isFinished with thread-safe KVO
[] Main thread -- UI updates verified with dispatchPrecondition(.onQueue(.main))
[] Swift Concurrency coexistence -- no semaphore.wait() in async contexts
[] Background tasks -- endBackgroundTask called on every pathCompanion Skills
Before refactoring GCD code to Swift Concurrency: load the swift-concurrency skill to understand actor isolation and Sendable constraints. Migration without that context leads to subtle bugs.
| Scenario | Companion skill | Apply when |
Migrating GCD patterns to async/await or actors | skills/swift-concurrency/SKILL.md | Converting completion handlers, replacing DispatchQueue with actors, adopting Swift 6 |
| GCD used in UIKit MVVM ViewModels | skills/uikit-mvvm/SKILL.md | Refactoring Massive ViewControllers, extracting ViewModels, setting up Combine bindings |
References
| Reference | When to Read |
references/rules.md | Do's and Don'ts quick reference: priority rules and critical anti-patterns |
references/queue-creation.md | Queue types, QoS selection, target queue hierarchies, queue labeling |
references/deadlocks.md | The 5 deadlock patterns, prevention with dispatchPrecondition, lock ordering |
references/thread-safety.md | Lock selection (NSLock, OSAllocatedUnfairLock, Mutex), reader-writer barrier, @Atomic trap, singletons |
references/thread-explosion.md | Thread explosion causes, priority inversion, thread starvation, throttling strategies |
references/dispatch-primitives.md | DispatchGroup, DispatchWorkItem, DispatchSemaphore, DispatchSource timers, DispatchIO |
references/operation-queue.md | AsyncOperation base class, KVO state management, cancellation, dependency graphs |
references/data-races.md | Value type COW races, memory management, main thread violations, real-world fixes |
references/debugging.md | Thread Sanitizer, dispatchPrecondition, Instruments, os_signpost, testing strategies |
references/migration.md | GCD to Swift Concurrency mapping, what to migrate vs keep, bridging with continuations |
references/refactoring-workflow.md | refactoring/ directory protocol, per-feature plans, PR sizing, verification checklist |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

