Last active
September 13, 2022 06:55
-
-
Save IuriiIaremenko/5e87b99d587796d9d825cd619efa093b to your computer and use it in GitHub Desktop.
ConcurrentSafe view Property Wrapper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ConcurrentSafe.swift | |
// | |
// Created by Iurii Iaremenko on 01.02.2021. | |
// | |
import Foundation | |
/// Property wrapper for safe access and modification of the value across multiple thread/queues. | |
@propertyWrapper | |
struct ConcurrentSafe<Value> { | |
private let lock: NSRecursiveLock | |
private var value: Value | |
init(wrappedValue: Value) { | |
lock = .init() | |
value = wrappedValue | |
lock.name = String(describing: value) | |
} | |
var wrappedValue: Value { | |
get { | |
assert(!(self is Invalid || value is Invalid), "Error please use read function instead!") | |
lock.lock() | |
defer { lock.unlock() } | |
return value | |
} | |
set { | |
/// Mutation of the array, dictionary, set, etc must be performed inside the mutate function otherwise data race will occur. | |
assert(!(self is Invalid || value is Invalid), "Error please use mutate function instead!") | |
lock.lock() | |
defer { lock.unlock() } | |
value = newValue | |
} | |
} | |
/// Safely read the Value. Useful for Arrays or Dictionaries, because direct access could cause data race due to COW. | |
/// | |
/// Example of usage: | |
/// | |
/// var value: Int? | |
/// _array.read { | |
/// value = $0.first | |
/// } | |
/// _dictionary.read { cachedDictionary in | |
/// value = cachedDictionary["key"] | |
/// } | |
public func read(block: (Value) -> Void) { | |
lock.lock() | |
defer { lock.unlock() } | |
block(value) | |
} | |
/// Mutate the Value | |
/// | |
/// Example of usage | |
/// | |
/// _array.mutate { | |
/// $0.append(index) | |
/// } | |
/// _dictionary.mutate { | |
/// $0[i] = "ConcurrentSafe" | |
/// } | |
mutating func mutate(_ mutation: (inout Value) -> Void) { | |
lock.lock() | |
defer { lock.unlock() } | |
mutation(&value) | |
} | |
/// Mutate the Value with throwing mutation | |
/// | |
/// Example of usage | |
/// | |
/// _array.mutate { | |
/// $0.append(index) | |
/// } | |
/// _dictionary.mutate { | |
/// $0[i] = "ConcurrentSafe" | |
/// } | |
mutating func mutate(_ mutation: (inout Value) throws -> Void) rethrows { | |
lock.lock() | |
defer { lock.unlock() } | |
try mutation(&value) | |
} | |
} | |
private protocol Invalid { } | |
// COW (copy on write) is not thread safe and cases data race on during direct read and write | |
extension ConcurrentSafe: Invalid where Value: Sequence { } | |
extension Array: Invalid { } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment