Section 1/133 menit

1. Pengantar: Mengapa Isolation?

1. Pengantar: Mengapa Isolation?

1.1 Masalah Fundamental: Memori Bersama

Komputer modern menjalankan banyak hal bersamaan. CPU punya banyak core, dan setiap core bisa mengeksekusi instruksi secara paralel. Ini bagus untuk performa — tapi menimbulkan masalah serius ketika dua core mengakses lokasi memori yang sama secara bersamaan.

Bayangkan dua orang mencoba menulis di papan tulis yang sama pada saat yang sama. Hasilnya bukan gabungan tulisan keduanya — hasilnya adalah coretan yang tidak terbaca.

Di level hardware, ini terjadi karena instruksi seperti value += 1 sebenarnya bukan satu instruksi atomik — ia adalah tiga langkah:

swift
1. LOAD  value dari memori → register CPU
2. ADD   register + 1
3. STORE hasil kembali ke memori

Jika dua thread melakukan ini bersamaan:

swift
Thread A: LOAD value (dapat: 5)
Thread B: LOAD value (dapat: 5)   ← keduanya baca nilai lama!
Thread A: ADD  5 + 1 = 6
Thread B: ADD  5 + 1 = 6
Thread A: STORE 6
Thread B: STORE 6increment B menimpa increment A!

Hasil akhir: value = 6, padahal seharusnya 7. Ini adalah data race — dan ini adalah undefined behavior di Swift (dan C/C++/Rust). Artinya compiler bebas menghasilkan output apa saja, termasuk crash, nilai salah, atau kerusakan memori yang baru muncul jauh kemudian.

1.2 Solusi Tradisional dan Masalahnya

Sebelum Swift 6, developer mengandalkan beberapa pendekatan:

DispatchQueue (Serial Queue)

swift
// "Semua akses ke data ini harus lewat queue ini"
let queue = DispatchQueue(label: "com.app.counter")
var value = 0

queue.async { value += 1 }  // thread-safe
queue.async { value += 1 }  // thread-safe

Masalah: Tidak ada yang mencegah developer secara tidak sengaja mengakses value langsung tanpa queue. Compiler tidak tahu tentang aturan ini — itu ada di kepala developer saja.

NSLock / os_unfair_lock

swift
let lock = NSLock()
var value = 0

lock.lock()
value += 1
lock.unlock()

Masalah: Mudah lupa unlock (→ deadlock), tidak bisa dikomposisi (lock di dalam lock → deadlock), dan tidak ada pemeriksaan compiler.

Keduanya adalah konvensi, bukan kontrak. Compiler tidak bisa memverifikasi bahwa kamu mengikuti aturan. Satu kesalahan programmer cukup untuk merusak segalanya.

1.3 Filosofi Swift 6: "If It Compiles, It's Safe"

Swift 6 mengambil pendekatan berbeda: encode aturan keamanan ke dalam type system. Jika kodenya compile, maka secara definisi tidak ada data race — karena compiler yang memverifikasinya, bukan manusia.

swift
// Swift 5 — tidak ada compile-time check, ini bisa crash di runtime
class Counter {
    var value = 0
    
    func increment() {
        value += 1  // DANGER: tidak thread-safe
    }
}

let counter = Counter()
Task { counter.increment() }
Task { counter.increment() }  // data race! — hanya ketahuan saat runtime crash
swift
// Swift 6 — ini adalah COMPILE ERROR, bukan runtime crash
// error: sending 'counter' risks causing data races

Pergeseran ini fundamental: dari "runtime safety" ke "compile-time safety".

1.4 Isolation = Kepemilikan Data yang Jelas

Isolation adalah kontrak: "data ini hanya boleh diakses dari satu domain pada satu waktu."

Analoginya: bayangkan sebuah kantor dengan ruangan-ruangan terpisah. Dokumen yang ada di ruangan A hanya bisa diakses oleh orang yang berada di ruangan A. Untuk mengakses dokumen dari ruangan B, kamu harus mengirim pesan ke ruangan A dan menunggu balasan — kamu tidak bisa langsung masuk.

Domain isolasi di Swift:

  • actor instance — setiap actor adalah "ruangan" sendiri
  • @MainActor — ruangan khusus: main thread
  • Custom global actor — ruangan bersama yang diberi nama
  • Nonisolated — di luar ruangan mana pun, hanya bisa akses data yang aman dibagi
Swift 5 Swift 6
Data race check Runtime (crash tidak terduga) Compile-time (error jelas)
Concurrency warning Opt-in Error by default
Isolation enforcement Manual (konvensi) Type system (compiler)
Keterbacaan aturan Di komentar / dokumen Di kode itu sendiri

1.5 Kapan Isolation Diperlukan dan Kapan Tidak

Isolation diperlukan ketika:

  • Kamu punya mutable state yang bisa diakses dari beberapa concurrent contexts
  • Kamu menggunakan async/await dan melewatkan data antar tasks
  • Kamu menggunakan background processing + UI update

Isolation tidak diperlukan ketika:

  • Semua state adalah immutable (let) — tidak bisa di-mutate, tidak ada race
  • Kamu menggunakan value types yang di-copy saat diteruskan — setiap Task punya salinannya sendiri
  • Kamu sudah menggunakan mekanisme lain yang terbukti aman (misalnya atomic operations untuk tipe primitif sederhana)
swift
// TIDAK perlu isolation: value type + immutable
let config = AppConfig(timeout: 30, retries: 3)  // struct, let
Task { process(config) }  // Task punya salinannya sendiri — aman
Task { process(config) }  // aman juga

// PERLU isolation: reference type + mutable
class AppState {
    var currentUser: User?  // shared reference, bisa di-mutate
}