-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
Copy pathUnfairLock.swift
122 lines (103 loc) · 3.2 KB
/
UnfairLock.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -enable-experimental-feature RawLayout -enable-experimental-feature BuiltinModule %s -o %t/a.out
// RUN: %target-codesign %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s
// REQUIRES: OS=macosx
// REQUIRES: executable_test
// REQUIRES: swift_feature_BuiltinModule
// REQUIRES: swift_feature_RawLayout
import Builtin
import Darwin
@_rawLayout(like: os_unfair_lock_s)
struct UnfairLock: ~Copyable, @unchecked Sendable {
// TODO: Clang importer can't handle the OS_UNFAIR_LOCK_INIT macro definition
private static let OS_UNFAIR_LOCK_INIT = os_unfair_lock_s()
// The lock is at a stable address as long as someone is borrowing it.
// If the address is gotten, it should only be used within the scope
// of a borrowing method.
@inline(__always)
private var _address: os_unfair_lock_t {
os_unfair_lock_t(Builtin.addressOfBorrow(self))
}
@inline(__always)
init() {
_address.initialize(to: UnfairLock.OS_UNFAIR_LOCK_INIT)
}
@inline(__always)
borrowing func withLock<R>(_ body: () throws -> R) rethrows -> R {
let address = _address
os_unfair_lock_lock(address)
defer { os_unfair_lock_unlock(address) }
return try body()
}
}
final class Locked<T>: @unchecked Sendable {
private let lock = UnfairLock()
// Don't need exclusivity checking since accesses always go through the
// lock
@exclusivity(unchecked)
private var value: T
init(initialValue: T) {
self.value = consume initialValue
}
func withLock<R>(_ body: (inout T) throws -> R) rethrows -> R {
return try lock.withLock { try body(&value) }
}
}
let myString = Locked(initialValue: "")
@Sendable
func thread1(_: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? {
usleep(1)
myString.withLock { $0 += "apple\n" }
usleep(1)
myString.withLock { $0 += "banana\n" }
usleep(1)
myString.withLock { $0 += "grapefruit\n" }
return nil
}
@Sendable
func thread2(_: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? {
usleep(1)
myString.withLock { $0 += "BRUSSELS SPROUTS\n" }
usleep(1)
myString.withLock { $0 += "CAULIFLOWER\n" }
usleep(1)
myString.withLock { $0 += "BROCCOLI\n" }
return nil
}
@Sendable
func thread3(_: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? {
usleep(1)
myString.withLock { $0 += "Croissant\n" }
usleep(1)
myString.withLock { $0 += "Boule\n" }
usleep(1)
myString.withLock { $0 += "Batard\n" }
return nil
}
func createPthread(
_ body: @Sendable @convention(c) (UnsafeMutableRawPointer) -> UnsafeMutableRawPointer?
) -> pthread_t? {
var thread: pthread_t? = nil
let r = pthread_create(&thread, nil, body, nil)
if r != 0 {
return nil
}
return thread
}
var t1 = createPthread(thread1)!
var t2 = createPthread(thread2)!
var t3 = createPthread(thread3)!
pthread_join(t1, nil)
pthread_join(t2, nil)
pthread_join(t3, nil)
// CHECK-DAG: apple
// CHECK-DAG: banana
// CHECK-DAG: grapefruit
// CHECK-DAG: BRUSSELS SPROUTS
// CHECK-DAG: CAULIFLOWER
// CHECK-DAG: BROCCOLI
// CHECK-DAG: Croissant
// CHECK-DAG: Boule
// CHECK-DAG: Batard
myString.withLock { print($0) }