[TechDebt] Readers-writer lock

Muhammad Alfiansyah
2 min readJun 2, 2024

--

Thread-safe singletons — Bagaimana cara yang benar menggunakan Singleton

Ketika kita mendengar Singleton apa yang pertama kali terpikirkan ?

  • App Context kalau di Android
  • Shared constant property kalau di iOS
  • Single source of truth

Sebenarnya tidak ada yang salah dengan Singleton tetapi ketika Singleton bertemu dengan Concurrent dan Multithreading akan menimbulkan sebuah masalah, yaitu Race Condition.

Apa itu Concurrent ?

Concurrent adalah menjalankan beberapa Task secara bersamaan / paralel. Tidak ada yang salah dengan Task yang berjalan secara paralel tetapi akan jadi masalah ketika Task dilakukan secara paralel tetapi di Thread yang berbeda.

Oleh sebab itu ada Readers-writer lock untuk menyelesaikan masalah tersebut

Sederhananya Read dan Write akan dilock sementara jika proses Write sedang berjalan di Thread manapun (MainThread, BackgroundThread etc.)

Race Condition — terjadi ketika memory yang sama diakses oleh Multiple Thread tanpa melakukan Syncronization.

Nah untuk melakukan blocking ketika memory sedang diakses pada thread yang berbeda kita menggunakan barrier flag.

concurrentQueue.async(flags: .barrier) {
// Your Code
}

Dengan begitu Concurrent tetap masih bisa berjalan hanya saja akan menjadi Serialize ketika diakses pada thread yang berbeda.

Begitu juga ketika Read, Read akan dieksekusi ketika Write task selesai.

import Foundation

final class EventLogger {
// Singleton instance
static let shared = EventLogger()

// Private property to store events
private var eventFired: [String: String] = ["initialization": "Property initialized"]

// Concurrent queue for thread-safe read and write operations
private let concurrentQueue = DispatchQueue(label: "com.eventLogger.concurrentQueue", attributes: .concurrent)

// Private initializer to enforce singleton pattern
private init() {}

// Asynchronous method to read log safely
func readLog(for key: String, completion: @escaping (String?) -> Void) {
concurrentQueue.async {
let value = self.eventFired[key]
completion(value)
}
}

// Method to write log safely
func writeLog(key: String, content: String) {
concurrentQueue.async(flags: .barrier) { [weak self] in
self?.eventFired[key] = content
}
}
}

// Usage example
let logger = EventLogger.shared
logger.writeLog(key: "event1", content: "Event 1 occurred")

logger.readLog(for: "event1") { log in
if let log = log {
print(log) // Output: Event 1 occurred
} else {
print("Log not found")
}
}

Source

--

--

No responses yet