Atomics and Memory Fences in Swift


(Anders) #1

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.

P.S. Is any of these primitives available on Linux? It seems the glibc modulemap does not export an `stdatomic` submodule at all.

Best Regards,
Anders


(Andrew Trick) #2

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.

-Andy

···

On Nov 30, 2016, at 5:40 AM, Anders Ha via swift-users <swift-users@swift.org> wrote:

P.S. Is any of these primitives available on Linux? It seems the glibc modulemap does not export an `stdatomic` submodule at all.

Best Regards,
Anders

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Anders) #3

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.

-Andy

I wrote to ask because I have stumbled on the posts (which I linked) of a colleague of yours working on Core Audio, with the specific quote of "even using functions imported from C is not guaranteed to be atomic". To some extent I believe it is true, since the Swift compiler does not define a memory model to say it doesn't reorder stuff in this or that situation.

But it doesn't seem to be a problem in practice, as stuff apparently isn't being reordered around the function calls in generated code. So I guess the answer is "Swift does not have a memory model and you are relying on a unconstrained aspects of the compiler not to fail you". Hmm?

Regards,
Anders

···

On 5 Dec 2016, at 1:53 AM, Andrew Trick <atrick@apple.com> wrote:

On Nov 30, 2016, at 5:40 AM, Anders Ha via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

P.S. Is any of these primitives available on Linux? It seems the glibc modulemap does not export an `stdatomic` submodule at all.

Best Regards,
Anders

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(Joe Groff) #4

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

···

On Dec 4, 2016, at 4:53 PM, Andrew Trick via swift-users <swift-users@swift.org> wrote:

On Nov 30, 2016, at 5:40 AM, Anders Ha via swift-users <swift-users@swift.org <mailto:swift-users@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.


(Anders) #5

What about `withUnsafeMutablePointer` on a stored object property, marked with `final`? e.g. `withUnsafeMutablePointer(to: &object.lock, os_unfair_lock_lock)`.

The generated object code with `-Owmo` shows that it is optimised to an address calculation immediately followed by a call to `os_unfair_lock_lock`. But I am not quite sure about its behaviour with no optimisation flag.

Regards,
Anders

···

On 6 Dec 2016, at 1:27 AM, Joe Groff <jgroff@apple.com> wrote:

On Dec 4, 2016, at 4:53 PM, Andrew Trick via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

On Nov 30, 2016, at 5:40 AM, Anders Ha via swift-users <swift-users@swift.org <mailto:swift-users@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


(Shawn Erickson) #6

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
    }

}

···

On Mon, Dec 5, 2016 at 9:28 AM Joe Groff via swift-users < swift-users@swift.org> wrote:

On Dec 4, 2016, at 4:53 PM, Andrew Trick via swift-users < > swift-users@swift.org> wrote:

On Nov 30, 2016, at 5:40 AM, Anders Ha via swift-users < > swift-users@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


(Joe Groff) #7

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

···

On Apr 25, 2017, at 1:08 PM, Shawn Erickson <shawnce@gmail.com> wrote:

On Mon, Dec 5, 2016 at 9:28 AM Joe Groff via swift-users <swift-users@swift.org> wrote:

On Dec 4, 2016, at 4:53 PM, Andrew Trick via swift-users <swift-users@swift.org> wrote:

On Nov 30, 2016, at 5:40 AM, Anders Ha via swift-users <swift-users@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
    }


(Colin Barrett) #8

I haven't used this in production, but this repository looks pretty
promising. It's more or less just wrapping up the clang atomic intrinsics
into a Swift package.

https://github.com/glessard/swift-atomics

-Colin

···

On Mon, May 1, 2017 at 12:43 PM Joe Groff via swift-users < swift-users@swift.org> wrote:

> On Apr 25, 2017, at 1:08 PM, Shawn Erickson <shawnce@gmail.com> wrote:
>
>
> On Mon, Dec 5, 2016 at 9:28 AM Joe Groff via swift-users < > swift-users@swift.org> wrote:
>
>> On Dec 4, 2016, at 4:53 PM, Andrew Trick via swift-users < > swift-users@swift.org> wrote:
>>
>>
>>> On Nov 30, 2016, at 5:40 AM, Anders Ha via swift-users < > swift-users@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
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Guillaume Lessard) #9

It works now, but it's not correct. I wish there were a correct way available.

Guillaume Lessard

···

On May 3, 2017, at 21:30, Colin Barrett via swift-users <swift-users@swift.org> wrote:

I haven't used this in production, but this repository looks pretty promising. It's more or less just wrapping up the clang atomic intrinsics into a Swift package.

https://github.com/glessard/swift-atomics

-Colin

On Mon, May 1, 2017 at 12:43 PM Joe Groff via swift-users <swift-users@swift.org> wrote:

> On Apr 25, 2017, at 1:08 PM, Shawn Erickson <shawnce@gmail.com> wrote:
>
>
> On Mon, Dec 5, 2016 at 9:28 AM Joe Groff via swift-users <swift-users@swift.org> wrote:
>
>> On Dec 4, 2016, at 4:53 PM, Andrew Trick via swift-users <swift-users@swift.org> wrote:
>>
>>
>>> On Nov 30, 2016, at 5:40 AM, Anders Ha via swift-users <swift-users@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
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users