Skip to content

Commit 26e346c

Browse files
authored
Merge pull request #12 from lorentey/restore-linux-support
Fix Linux compatibility
2 parents dc49369 + 7658766 commit 26e346c

15 files changed

+11540
-9982
lines changed

Diff for: Package.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ let package = Package(
2424
.target(name: "_AtomicsShims"),
2525
.target(
2626
name: "Atomics",
27-
dependencies: ["_AtomicsShims"],
28-
path: "Sources/Atomics"
27+
dependencies: ["_AtomicsShims"]
2928
),
3029
.testTarget(
3130
name: "AtomicsTests",

Diff for: README.md

+14
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,20 @@ Of particular note is full support for atomic strong references. This provides a
7979

8080
All atomic operations exposed by this package are guaranteed to have lock-free implementations. However, we do not guarantee wait-free operation -- depending on the capabilities of the target platform, some of the exposed operations may be implemented by compare-and-exchange loops. That said, all atomic operations map directly to dedicated CPU instructions where available -- to the extent supported by llvm & Clang.
8181

82+
## Portability Concerns
83+
84+
Lock-free double-wide atomics requires support for such things from the underlying target platform. Where such support isn't available, this package doesn't implement `DoubleWord` atomics or atomic strong references. While modern multiprocessing CPUs have been providing double-wide atomic instructions for a number of years now, some platforms still target older architectures by default; these require a special compiler option to enable double-wide atomic instructions. This currently includes Linux operating systems running on x86_64 processors, where the `cmpxchg16b` instruction isn't considered a baseline requirement.
85+
86+
To enable double-wide atomics on Linux/x86_64, you currently have to manually supply a couple of additional options on the SPM build invocation:
87+
88+
```
89+
$ swift build -Xcc -mcx16 -Xswiftc -DENABLE_DOUBLEWIDE_ATOMICS -c release
90+
```
91+
92+
(`-mcx16` turns on support for `cmpxchg16b` in Clang, and `-DENABLE_DOUBLEWIDE_ATOMICS` makes Swift aware that double-wide atomics are available. Note that the resulting binaries won't run on some older AMD64 CPUs.)
93+
94+
The package cannot currently configure this automatically.
95+
8296
## Memory Management
8397

8498
Atomic access is implemented in terms of dedicated atomic storage representations that are kept distinct from the corresponding regular (non-atomic) type. (E.g., the actual integer value underlying the counter above isn't directly accessible.) This has several advantages:

Diff for: Sources/Atomics/AtomicStrongReference.swift

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212

1313
import _AtomicsShims
1414

15+
// Double-wide atomic primitives on x86_64 CPUs aren't available by default
16+
// on Linux distributions, and we cannot currently enable them automatically.
17+
#if !(os(Linux) && arch(x86_64)) || ENABLE_DOUBLEWIDE_ATOMICS
18+
1519
/// A class type that supports atomic strong references.
1620
public protocol AtomicReference: AnyObject, AtomicOptionalWrappable
1721
where
@@ -569,3 +573,4 @@ extension AtomicOptionalReferenceStorage: AtomicStorage {
569573
}
570574
}
571575

576+
#endif // ENABLE_DOUBLEWIDE_ATOMICS

Diff for: Sources/Atomics/IntegerConformances.swift.gyb

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ ${autogenerated_warning()}
2424
import _AtomicsShims
2525

2626
% for swiftType in atomicTypes():
27+
% if swiftType == "DoubleWord":
28+
// Double-wide atomic primitives on x86_64 CPUs aren't available by default
29+
// on Linux distributions, and we cannot currently enable them automatically.
30+
#if !(os(Linux) && arch(x86_64)) || ENABLE_DOUBLEWIDE_ATOMICS
31+
% end
2732
extension ${swiftType}: AtomicValue {
2833
public struct AtomicRepresentation {
2934
public typealias Value = ${swiftType}
@@ -219,4 +224,7 @@ extension ${swiftType}.AtomicRepresentation: AtomicIntegerStorage {
219224
}
220225
% end
221226

227+
% if swiftType == "DoubleWord":
228+
#endif // ENABLE_DOUBLEWIDE_ATOMICS
229+
% end
222230
% end

Diff for: Sources/Atomics/autogenerated/IntegerConformances.swift

+4
Original file line numberDiff line numberDiff line change
@@ -4939,6 +4939,9 @@ extension UInt8.AtomicRepresentation: AtomicIntegerStorage {
49394939
}
49404940
}
49414941

4942+
// Double-wide atomic primitives on x86_64 CPUs aren't available by default
4943+
// on Linux distributions, and we cannot currently enable them automatically.
4944+
#if !(os(Linux) && arch(x86_64)) || ENABLE_DOUBLEWIDE_ATOMICS
49424945
extension DoubleWord: AtomicValue {
49434946
public struct AtomicRepresentation {
49444947
public typealias Value = DoubleWord
@@ -5262,3 +5265,4 @@ extension DoubleWord.AtomicRepresentation: AtomicStorage {
52625265
}
52635266

52645267

5268+
#endif // ENABLE_DOUBLEWIDE_ATOMICS

Diff for: Sources/_AtomicsShims/include/_AtomicsShims.h

+24
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@
5959
#include <stdatomic.h>
6060
#include <assert.h>
6161

62+
// For now, assume double-wide atomics are available everywhere,
63+
// except on Linux/x86_64, where they need to be manually enabled
64+
// by the `cx16` target attribute. (Unfortunately we cannot currently
65+
// turn that on in our package description.)
66+
#ifdef __APPLE__
67+
# define ENABLE_DOUBLEWIDE_ATOMICS 1
68+
#elif defined(_WIN32)
69+
# define ENABLE_DOUBLEWIDE_ATOMICS 1
70+
#elif defined(__linux__)
71+
# if !defined(__x86_64__) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)
72+
# define ENABLE_DOUBLEWIDE_ATOMICS 1
73+
# endif
74+
#endif
75+
6276
#define SWIFTATOMIC_INLINE static inline __attribute__((__always_inline__))
6377
#define SWIFTATOMIC_SWIFT_NAME(name) __attribute__((swift_name(#name)))
6478

@@ -320,9 +334,13 @@ _sa_dword _sa_decode_dword(__uint128_t value) {
320334
return result;
321335
}
322336

337+
#if ENABLE_DOUBLEWIDE_ATOMICS
323338
#define SWIFTATOMIC_ENCODE_DoubleWord(_value) (_value).value
324339
#define SWIFTATOMIC_DECODE_DoubleWord(value) _sa_decode_dword(value)
325340
SWIFTATOMIC_DEFINE_TYPE(COMPLEX, DoubleWord, _sa_dword, __uint128_t)
341+
#else
342+
SWIFTATOMIC_STORAGE_TYPE(DoubleWord, _sa_dword, __uint128_t)
343+
#endif
326344

327345
#elif __INTPTR_WIDTH__ == 32
328346

@@ -352,15 +370,21 @@ _sa_dword _sa_decode_dword(uint64_t value) {
352370
return result;
353371
}
354372

373+
#if ENABLE_DOUBLEWIDE_ATOMICS
355374
#define SWIFTATOMIC_ENCODE_DoubleWord(_value) (_value).value
356375
#define SWIFTATOMIC_DECODE_DoubleWord(value) _sa_decode_dword(value)
357376
SWIFTATOMIC_DEFINE_TYPE(COMPLEX, DoubleWord, _sa_dword, uint64_t)
377+
#else
378+
SWIFTATOMIC_STORAGE_TYPE(DoubleWord, _sa_dword, uint64_t)
379+
#endif // ENABLE_DOUBLEWIDE_ATOMICS
358380

359381
#else
360382
#error "Unsupported intptr_t bit width"
361383
#endif // __INTPTR_WIDTH
362384

385+
#if ENABLE_DOUBLEWIDE_ATOMICS
363386
extern void _sa_retain_n(void *object, uint32_t n);
364387
extern void _sa_release_n(void *object, uint32_t n);
388+
#endif
365389

366390
#endif //SWIFTATOMIC_HEADER_INCLUDED

Diff for: Sources/_AtomicsShims/src/_AtomicsShims.c

+11-2
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,23 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include <dlfcn.h>
1314
#include "_AtomicsShims.h"
1415

15-
void _sa_retain_n(void *object, uint32_t n) {
16+
#if ENABLE_DOUBLEWIDE_ATOMICS
17+
// FIXME: These should be static inline header-only shims, but Swift 5.3 doesn't
18+
// like calls to swift_retain_n/swift_release_n appearing in Swift code, not
19+
// even when imported through C. (See https://bugs.swift.org/browse/SR-13708)
20+
21+
void _sa_retain_n(void *object, uint32_t n)
22+
{
1623
extern void *swift_retain_n(void *object, uint32_t n);
1724
swift_retain_n(object, n);
1825
}
1926

20-
void _sa_release_n(void *object, uint32_t n) {
27+
void _sa_release_n(void *object, uint32_t n)
28+
{
2129
extern void swift_release_n(void *object, uint32_t n);
2230
swift_release_n(object, n);
2331
}
32+
#endif

Diff for: Tests/AtomicsTests/Basics.swift.gyb

+59-39
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,26 @@
2828
("UInt64", "UInt64", "12", "23"),
2929

3030
("Bool", "Bool", "true", "false"),
31-
("DoubleWord", "DoubleWord", "DoubleWord(high: 100, low: 64)", "DoubleWord(high: 50, low: 32)"),
3231

33-
("Pointer", "UnsafePointer<Foo>", "_foo1", "_foo2"),
34-
("OptionalPointer", "UnsafePointer<Foo>?", "nil", "_foo2"),
35-
("MutablePointer", "UnsafeMutablePointer<Foo>", "_mfoo1", "_mfoo2"),
36-
("OptionalMutablePointer", "UnsafeMutablePointer<Foo>?", "nil", "_mfoo2"),
37-
("RawPointer", "UnsafeMutableRawPointer", "_raw1", "_raw2"),
38-
("OptionalRawPointer", "UnsafeMutableRawPointer?", "nil", "_raw2"),
32+
("Pointer", "UnsafePointer<Foo>", "_foo1", "_foo2"),
33+
("OptionalPointer", "UnsafePointer<Foo>?", "nil", "_foo2"),
34+
("MutablePointer", "UnsafeMutablePointer<Foo>", "_mfoo1", "_mfoo2"),
35+
("OptionalMutablePointer", "UnsafeMutablePointer<Foo>?", "nil", "_mfoo2"),
36+
37+
("RawPointer", "UnsafeRawPointer", "_raw1", "_raw2"),
38+
("OptionalRawPointer", "UnsafeRawPointer?", "nil", "_raw2"),
39+
("MutableRawPointer", "UnsafeMutableRawPointer", "_mraw1", "_mraw2"),
40+
("OptionalMutableRawPointer", "UnsafeMutableRawPointer?", "nil", "_mraw2"),
3941

40-
("Unmanaged", "Unmanaged<Bar>", "_bar1", "_bar2"),
41-
("OptionalUnmanaged", "Unmanaged<Bar>?", "nil", "_bar2"),
42+
("Unmanaged", "Unmanaged<Bar>", "_bar1", "_bar2"),
43+
("OptionalUnmanaged", "Unmanaged<Bar>?", "nil", "_bar2"),
4244

43-
("Reference", "Baz", "_baz1", "_baz2"),
44-
("OptionalReference", "Baz?", "nil", "_baz2"),
45+
("RawRepresentable", "Fred", "Fred.one", "Fred.two"),
46+
47+
("DoubleWord", "DoubleWord", "DoubleWord(high: 100, low: 64)", "DoubleWord(high: 50, low: 32)"),
4548

46-
("RawRepresentable", "Fred", "Fred.one", "Fred.two"),
49+
("Reference", "Baz", "_baz1", "_baz2"),
50+
("OptionalReference", "Baz?", "nil", "_baz2"),
4751
]
4852
}%
4953
${autogenerated_warning()}
@@ -72,6 +76,7 @@ private class Bar: Equatable, CustomStringConvertible {
7276
}
7377
}
7478

79+
#if !(os(Linux) && arch(x86_64)) || ENABLE_DOUBLEWIDE_ATOMICS
7580
private class Baz: Equatable, CustomStringConvertible, AtomicReference {
7681
var value: Int
7782
init(_ value: Int) { self.value = value }
@@ -80,21 +85,28 @@ private class Baz: Equatable, CustomStringConvertible, AtomicReference {
8085
left === right
8186
}
8287
}
88+
#endif
8389

8490
private enum Fred: Int, AtomicValue {
8591
case one
8692
case two
8793
}
8894

95+
% for label, type, a, b in types:
96+
% if label == "DoubleWord" or label == "Reference" or label == "OptionalReference":
97+
#if !(os(Linux) && arch(x86_64)) || ENABLE_DOUBLEWIDE_ATOMICS
98+
% else:
99+
#if true
100+
% end
89101
/// Exercises all operations in a single-threaded context, verifying
90102
/// they provide the expected results.
91-
class BasicAtomicTests: XCTestCase {
103+
class BasicAtomic${label}Tests: XCTestCase {
104+
% if label == "Pointer" or label == "OptionalPointer" or label == "MutablePointer" or label == "OptionalMutablePointer":
92105
private let _mfoo1: UnsafeMutablePointer<Foo> = {
93106
let p = UnsafeMutablePointer<Foo>.allocate(capacity: 1)
94107
p.initialize(to: Foo(1))
95108
return p
96109
}()
97-
98110
private let _mfoo2: UnsafeMutablePointer<Foo> = {
99111
let p = UnsafeMutablePointer<Foo>.allocate(capacity: 1)
100112
p.initialize(to: Foo(2))
@@ -104,29 +116,38 @@ class BasicAtomicTests: XCTestCase {
104116
private var _foo1: UnsafePointer<Foo> { UnsafePointer(_mfoo1) }
105117
private var _foo2: UnsafePointer<Foo> { UnsafePointer(_mfoo2) }
106118

107-
private let _raw1 = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
108-
private let _raw2 = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
109-
private let _bar1 = Unmanaged<Bar>.passRetained(Bar(1))
110-
private let _bar2 = Unmanaged<Bar>.passRetained(Bar(2))
111-
private let _baz1 = Baz(1)
112-
private let _baz2 = Baz(2)
113-
114119
deinit {
115120
_mfoo1.deinitialize(count: 1)
116121
_mfoo1.deallocate()
117122

118123
_mfoo2.deinitialize(count: 1)
119124
_mfoo2.deallocate()
125+
}
126+
% elif label == "RawPointer" or label == "OptionalRawPointer" or label == "MutableRawPointer" or label == "OptionalMutableRawPointer":
127+
private let _mraw1 = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
128+
private let _mraw2 = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
120129

121-
_raw1.deallocate()
122-
_raw2.deallocate()
130+
private var _raw1: UnsafeRawPointer { UnsafeRawPointer(_mraw1) }
131+
private var _raw2: UnsafeRawPointer { UnsafeRawPointer(_mraw2) }
123132

133+
deinit {
134+
_mraw1.deallocate()
135+
_mraw2.deallocate()
136+
}
137+
% elif label == "Unmanaged" or label == "OptionalUnmanaged":
138+
private let _bar1 = Unmanaged<Bar>.passRetained(Bar(1))
139+
private let _bar2 = Unmanaged<Bar>.passRetained(Bar(2))
140+
141+
deinit {
124142
_bar1.release()
125143
_bar2.release()
126144
}
145+
% elif label == "Reference" or label == "OptionalReference":
146+
private let _baz1 = Baz(1)
147+
private let _baz2 = Baz(2)
148+
% end
127149

128-
% for label, type, a, b in types:
129-
func test_${label}_create_destroy() {
150+
func test_create_destroy() {
130151
let v = UnsafeAtomic<${type}>.create(${a})
131152
defer { v.destroy() }
132153
XCTAssertEqual(v.load(ordering: .relaxed), ${a})
@@ -137,7 +158,7 @@ class BasicAtomicTests: XCTestCase {
137158
}
138159

139160
% for (order, _) in loadOrderings:
140-
func test_${label}_load_${order}() {
161+
func test_load_${order}() {
141162
let v = UnsafeAtomic<${type}>.create(${a})
142163
defer { v.destroy() }
143164
XCTAssertEqual(v.load(ordering: .${order}), ${a})
@@ -149,7 +170,7 @@ class BasicAtomicTests: XCTestCase {
149170
% end
150171

151172
% for (order, _) in storeOrderings:
152-
func test_${label}_store_${order}() {
173+
func test_store_${order}() {
153174
let v = UnsafeAtomic<${type}>.create(${a})
154175
defer { v.destroy() }
155176
v.store(${b}, ordering: .${order})
@@ -163,7 +184,7 @@ class BasicAtomicTests: XCTestCase {
163184
% end
164185

165186
% for (order, _, _) in updateOrderings:
166-
func test_${label}_exchange_${order}() {
187+
func test_exchange_${order}() {
167188
let v = UnsafeAtomic<${type}>.create(${a})
168189
defer { v.destroy() }
169190

@@ -179,7 +200,7 @@ class BasicAtomicTests: XCTestCase {
179200
% end
180201

181202
% for (order, _, _) in updateOrderings:
182-
func test_${label}_compareExchange_${order}() {
203+
func test_compareExchange_${order}() {
183204
let v = UnsafeAtomic<${type}>.create(${a})
184205
defer { v.destroy() }
185206

@@ -220,7 +241,7 @@ class BasicAtomicTests: XCTestCase {
220241
% for operation in ["compareExchange", "weakCompareExchange"]:
221242
% for (successorder, _, _) in updateOrderings:
222243
% for (failorder, _) in loadOrderings:
223-
func test_${label}_${operation}_${successorder}_${failorder}() {
244+
func test_${operation}_${successorder}_${failorder}() {
224245
let v = UnsafeAtomic<${type}>.create(${a})
225246
defer { v.destroy() }
226247

@@ -265,12 +286,11 @@ class BasicAtomicTests: XCTestCase {
265286
% end
266287

267288

268-
// Bool operations
269-
270289
% if type == "Bool":
290+
// Bool operations
271291
% for (name, _, operator, arglabel, _) in boolOperations:
272292
% for (order, _, _) in updateOrderings:
273-
func test_${label}_loadThen${name}_${order}() {
293+
func test_loadThen${name}_${order}() {
274294
let v = UnsafeAtomic<Bool>.create(false)
275295
defer { v.destroy() }
276296

@@ -292,7 +312,7 @@ class BasicAtomicTests: XCTestCase {
292312

293313
% for (name, _, operator, arglabel, _) in boolOperations:
294314
% for (order, _, _) in updateOrderings:
295-
func test_${label}_${lowerFirst(name)}ThenLoad_${order}() {
315+
func test_${lowerFirst(name)}ThenLoad_${order}() {
296316
let v = UnsafeAtomic<Bool>.create(false)
297317
defer { v.destroy() }
298318

@@ -315,12 +335,11 @@ class BasicAtomicTests: XCTestCase {
315335
% end
316336
% end
317337

318-
// Integer operations
319-
320338
% if type.startswith("Int") or type.startswith("UInt"):
339+
// Integer operations
321340
% for (name, _, operator, arglabel, _) in integerOperations:
322341
% for (order, _, _) in updateOrderings:
323-
func test_${label}_loadThen${name}_${order}() {
342+
func test_loadThen${name}_${order}() {
324343
let a: ${type} = 3
325344
let b: ${type} = 8
326345
let c: ${type} = 12
@@ -343,7 +362,7 @@ class BasicAtomicTests: XCTestCase {
343362

344363
% for (name, _, operator, arglabel, _) in integerOperations:
345364
% for (order, _, _) in updateOrderings:
346-
func test_${label}_${lowerFirst(name)}ThenLoad_${order}() {
365+
func test_${lowerFirst(name)}ThenLoad_${order}() {
347366
let a: ${type} = 3
348367
let b: ${type} = 8
349368
let c: ${type} = 12
@@ -365,5 +384,6 @@ class BasicAtomicTests: XCTestCase {
365384
% end
366385
% end
367386

368-
% end
369387
}
388+
#endif
389+
% end

0 commit comments

Comments
 (0)