Skip to content

19 — Cancellation in GCD

rebeloper edited this page Jul 12, 2026 · 1 revision

🍽️ Intuition

Yelling "cancel that order!" only works if the chef hasn't started cooking yet. Once it's on the grill, someone has to actively notice and stop — nobody stops it for them.

🧠 What It Means In Swift

GCD has no built-in structured cancellation. DispatchWorkItem.cancel() only prevents execution if the block hasn't started yet — it cannot interrupt work already running. To support mid-execution cancellation, the block itself must cooperatively check item.isCancelled at safe points.

📊 ASCII Diagram

Not started yet:  [ticket] ──cancel()──▶ 🗑️ torn up, never cooked
Already on grill: [ticket] ──cancel()──▶ 🔥 still cooking, unaffected

💻 Tiny Swift Example

var item: DispatchWorkItem!
item = DispatchWorkItem {
    for i in 0..<1_000_000 {
        if item.isCancelled { return } // must check cooperatively at safe points
        doChunkOfWork(i)
    }
}
queue.async(execute: item)
item.cancel() // only effective if `item` hasn't started running yet

👉 Once item has started executing, calling .cancel() sets a flag but does nothing to stop the loop already in progress — the block only bails out early because it checks item.isCancelled itself, between chunks.

🔄 vs. Swift Concurrency

Task cancellation is also cooperative, but it's first-class throughout the ecosystem — Task.isCancelled, Task.checkCancellation(), and cancellation propagating automatically to child tasks in a TaskGroup or async let.

🧸 Memory Sentence

Cancellation only works before the order hits the grill — after that, someone has to check.

⚠️ Analogy Limit

GCD gives you no structural help propagating a cancellation signal to related work the way Swift Concurrency's task trees do — you'd have to build that bookkeeping yourself.

✅ Check Your Understanding

Why doesn't calling .cancel() on a DispatchWorkItem that's already executing stop it?

Clone this wiki locally