I wanna preface this by saying that I could be way off course here, and it might be the case that what I'm trying to accomplish here is in fact a terrible idea β please let me know if this is the case.
I'm trying to implement a ThreadSafe property wrapper that prevents data races when a value is accessed from multiple threads simultaneously.
As is already known, it is not possible to implement this using the get /set accessors, since a mutating access to a value would get compiled into a two separate get
and set
accesses.
My understanding is that the _modify
accessor would enable the correct behaviour here: by yielding a reference to the underlying wrapped value, we can turn a mutating access into a single _modify
, rather than a pair of a get
and a set
.
This is the code I have so far, and it seems to be working, but I'm not sure if this is because it's actually how this should be implemented, or whether it's merely working as i'd expect by coincidence:
@propertyWrapper
final class ThreadSafe<Value> {
private let lock = OSAllocatedUnfairLock()
private var value: Value
init(_ value: Value) {
self.value = value
}
convenience init(wrappedValue value: Value) {
self.init(value)
}
var wrappedValue: Value {
_read {
lock.lock()
defer { lock.unlock() }
yield value
}
_modify {
lock.lock()
defer { lock.unlock() }
yield &value
}
}
}
(The alternative of course would be to use eg a DispatchQueue
to synchronise accesses to a protected value, but that wouldn't work with the semantics of a property wrapper...)
(Also: I'm aware that modify accessors are currently still officially unsupported; i'm to an extent just curious whether this would be the overall correct approach, or whether this is the completely wrong way for trying to achieve this.)