[swift-users] Atomics and Memory Fences in Swift
Joe Groff
jgroff at apple.com
Mon May 1 11:43:10 CDT 2017
> On Apr 25, 2017, at 1:08 PM, Shawn Erickson <shawnce at gmail.com> wrote:
>
>
> On Mon, Dec 5, 2016 at 9:28 AM Joe Groff via swift-users <swift-users at swift.org> wrote:
>
>> On Dec 4, 2016, at 4:53 PM, Andrew Trick via swift-users <swift-users at swift.org> wrote:
>>
>>
>>> On Nov 30, 2016, at 5:40 AM, Anders Ha via swift-users <swift-users at swift.org> wrote:
>>>
>>> Hi guys
>>>
>>> I have recently started adopting lock-free atomics with memory fences, but it seems Swift at this moment does not have any native instruments.
>>>
>>> Then I read a thread in the Apple Developer Forum (https://forums.developer.apple.com/thread/49334), which an Apple staff claimed that all imported atomic operations are "not guaranteed to be atomic". But for my tests with all optimizations enabled (-Owholemodule and -O), the OSAtomic primitives and stdatomic fences do not seem going wild.
>>>
>>> Is these `atomic_*` and `OSAtomic*` primitives really unsafe in Swift as claimed? It doesn't seem like the Swift compiler would reorder memory accesses around a C function call that it wouldn't be able to see through.
>>
>> Did you get an answer to this? I’m not sure what led you to believe the primitives are unsafe in Swift. Importing them doesn’t change their semantics.
>
> If you apply them to memory you allocated manually with malloc/free on UnsafeMutablePointer's allocation methods, then yeah, they should work as they do in C. That's the safest way to use these functions today. Passing a Swift `var` inout to one of these functions does not guarantee that accesses to that var will maintain atomicity, since there may be bridging or reabstracting conversions happening under the hood.
>
> -Joe
>
> Is the following in the ball park of being correct (going back over some old code we have)...
>
> public struct AtomicBool {
>
> private static let bitLocation: UInt32 = 0
> private static let trueValue: UInt8 = 0x80
> private static let falseValue: UInt8 = 0x00
>
> private let value = UnsafeMutablePointer<UInt8>.allocate(capacity: 1) // TODO - leaking right? How to deal with that in a struct situation...?
> public var onSet: ((_ old: Bool, _ new: Bool) -> ())?
>
> public init(_ intialValue: Bool = false) {
> value.initialize(to: intialValue ? AtomicBool.trueValue : AtomicBool.falseValue)
> onSet = nil
> }
>
> public init(_ intialValue: Bool = false, onSet: ((_ old: Bool, _ new: Bool) -> ())?) {
> value.initialize(to: intialValue ? AtomicBool.trueValue : AtomicBool.falseValue)
> self.onSet = onSet
> }
>
> public mutating func set(_ newValue: Bool) {
> _ = getAndSet(newValue)
> }
>
> public mutating func getAndSet(_ newValue: Bool) -> Bool {
> let oldValue: Bool
> if newValue {
> oldValue = Darwin.OSAtomicTestAndSetBarrier(AtomicBool.bitLocation, value)
> }
> else {
> oldValue = Darwin.OSAtomicTestAndClearBarrier(AtomicBool.bitLocation, value)
> }
>
> onSet?(oldValue, newValue)
> return oldValue
> }
>
> public func get() -> Bool { // TODO - document the lazy "safety" aspect of get
> return value.pointee != AtomicBool.falseValue
> }
That looks OK. It might be better to provide an allocate/deallocate or with { ... } interface instead of burying the allocate call in the initializer since the user will need to handle the deallocation of the buffer at some point.
-Joe
More information about the swift-users
mailing list