-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[stdlib] Implement Atomics module #30553
Conversation
@swift-ci test |
@swift-ci test |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@swift-ci test |
@swift-ci test |
@swift-ci test Windows platform |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…& storage mgmt - _PrimitiveAtomic is a new hidden protocol implementing the core atomic operations. - _PrimitiveAtomicOptional refines it adding nullability. - AtomicProtocol now consists of a _PrimitiveAtomic-constrained _AtomicStorage, plus conversion methods — all underscored. - Pointer types have opaque atomic storage. - User-visible atomic storage type is UnsafeAtomic.Storage — an opaque value type with a convenience initializer and a dispose() method.
This looks great, thanks for working on it! Looking forward to seeing this added, and help avoid many hacks non-official implementations. I do wonder though if there are any plans for Atomicity in a type-safe level, as opposed to this UnsafeAtomic implementation. Not necessarily in this implementation, but generally speaking ? Thanks ! |
Yes -- the current implementation was specifically designed to allow easy transition to the eventual memory-safe constructs later. The preliminary proposal text has a section explaining what's missing. |
@swift-ci test |
Build failed |
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) | ||
public protocol _PrimitiveAtomicOptional: _PrimitiveAtomic { | ||
var _isNil: Bool { get } | ||
static var _nilValue: Self { get } | ||
} | ||
|
||
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) | ||
extension Optional: AtomicProtocol | ||
where Wrapped: AtomicProtocol, Wrapped._AtomicStorage: _PrimitiveAtomicOptional | ||
{ | ||
public typealias _AtomicStorage = Wrapped._AtomicStorage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you use Equatable & ExpressibleByNilLiteral
storage to eliminate the _PrimitiveAtomicOptional
protocol?
return _AtomicStorage._nilValue
➡️return nil
!storage._isNil
➡️storage != nil
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) | |
public protocol _PrimitiveAtomicOptional: _PrimitiveAtomic { | |
var _isNil: Bool { get } | |
static var _nilValue: Self { get } | |
} | |
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) | |
extension Optional: AtomicProtocol | |
where Wrapped: AtomicProtocol, Wrapped._AtomicStorage: _PrimitiveAtomicOptional | |
{ | |
public typealias _AtomicStorage = Wrapped._AtomicStorage | |
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) | |
extension Optional: AtomicProtocol where | |
Wrapped: AtomicProtocol, | |
Wrapped._AtomicStorage: Equatable & ExpressibleByNilLiteral | |
{ | |
public typealias _AtomicStorage = Wrapped._AtomicStorage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but that doesn't look like a particularly great idea to me. I prefer the explicit un-cleverness of a dedicated protocol here.
There is also this:
nil
has a specific meaning in Swift—the absence of a value. Only theOptional
type conforms toExpressibleByNilLiteral
.ExpressibleByNilLiteral
conformance for types that use nil for other purposes is discouraged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is also this:
nil
has a specific meaning in Swift—the absence of a value. Only theOptional
type conforms toExpressibleByNilLiteral
.
And the _OptionalNilComparisonType
!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do think it'd be fair to say _PrimitiveAtomicOptional
inherits from ExpressibleByNilLiteral
though. That would still simplify the implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The storage types aren't currently ExpressibleByNilLiteral
, though, and I think I'd like to keep them that way.
I am ambivalent towards adding Equatable
conformance to atomic storage. (For the same reason I keep fiddling with the FixedWidthInteger
conformance on _PrimitiveAtomicInteger
. I dislike adding too much structure to the storage types -- these should be treated as opaque boxes.)
@frozen | ||
public struct Storage { | ||
@usableFromInline | ||
internal var _value: Value._AtomicStorage | ||
|
||
@inlinable @inline(__always) | ||
internal init(_value: __owned Value._AtomicStorage) { | ||
self._value = _value | ||
} | ||
|
||
@inlinable @inline(__always) | ||
public init(_ value: __owned Value) { | ||
self._value = Value._prepareAtomicStorage(for: value) | ||
} | ||
|
||
@inlinable @inline(__always) | ||
@discardableResult | ||
public mutating func dispose() -> Value { | ||
return Value._disposeAtomicStorage(&_value) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you use a typealias to eliminate the UnsafeAtomic.Storage
structure?
- the computed
var _ptr
and itsassumingMemoryBound(to:)
wouldn't be needed. - possibly rename
_AtomicStorage
➡️AtomicStorage
(since integers already have nestedSIMDStorage
types).
@frozen | |
public struct Storage { | |
@usableFromInline | |
internal var _value: Value._AtomicStorage | |
@inlinable @inline(__always) | |
internal init(_value: __owned Value._AtomicStorage) { | |
self._value = _value | |
} | |
@inlinable @inline(__always) | |
public init(_ value: __owned Value) { | |
self._value = Value._prepareAtomicStorage(for: value) | |
} | |
@inlinable @inline(__always) | |
@discardableResult | |
public mutating func dispose() -> Value { | |
return Value._disposeAtomicStorage(&_value) | |
} | |
} | |
public typealias Storage = Value._AtomicStorage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, this is technically possible, but I prefer to have a dedicated, public wrapper so that particular choices for _AtomicStorage
won't get hardwired into (ostensibly) portable codebases.
UnsafeAtomic.Storage
has the additional benefit that we can attach API to it (init(_:)
, dispose()
) that would be inappropriate on _AtomicStorage
, esp. in the case where atomic types use Self
as their atomic storage.
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) | ||
@frozen | ||
public struct UnsafeAtomicLazyReference<Instance: AnyObject> { | ||
public typealias Value = Instance? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be clearer to remove this typealias, and use Instance?
directly.
(Instance
and Instance?
are already used in the store/load methods.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Value
is part of the new unsafe API pattern for high-performance synchronization primitives. This is pure 100% ceremony, but it helps establishing / highlighting patterns for such types.
@swift-ci test |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@swift-ci test |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Closing -- Atomics are published as a standalone package for now: https://github.com/apple/swift-atomics |
This is the implementation that goes with the Low-Level Atomic Operations pitch. For details, see there!
In this pitch, we are gathering feedback about the idea of adding a limited set of low-level atomic operations to the Standard Library, including native spellings for C++-style acquire-release memory orderings. Our goal is to enable intrepid library authors to start building synchronization constructs directly in Swift.
As a quick taste, we want to make atomics look like this:
(This is a followup to the atomic experiments in #27229.)
Fixes #51640
rdar://17171396