-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathContents.swift
More file actions
100 lines (78 loc) · 2.48 KB
/
Contents.swift
File metadata and controls
100 lines (78 loc) · 2.48 KB
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
/*
This shows some basic usage of a Synchronized<T> resource.
*/
import Synchronized // <-- If you get an error here, build the `Synchronized` scheme
import Dispatch
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
// MARK: - Most basic usage, just with a local variable
let criticalString = Synchronized<String>("test")
let uppercasedString = criticalString.use { $0.uppercased() }
print("uppercased:", uppercasedString)
// MARK: - More realistic usage, in a class property
class C {
let criticalCount = Synchronized<Int>(0)
func incrementOnUnknownThread() {
criticalString.update { count in
count += 1
}
}
}
// MARK: - Concurrent mutations
// The following show an example of a race (exacerbased by
// adding a `sleep()` that Synchronized<T> is intended to
// help you avoid.
let numIterations = 10
// (1) Showing an example of the kind of problem that Synchronized<T> can solve.
if false {
var unsafeCriticalCount: Int = 0
let group1 = DispatchGroup()
for _ in 0..<numIterations {
group1.enter()
DispatchQueue.global().async {
// BAD: This is some racy code
let original = unsafeCriticalCount
usleep(10)
unsafeCriticalCount = original + 1
group1.leave()
}
}
group1.wait()
print("1) expected: \(numIterations), actual: \(criticalCount.unsafeGet())")
}
// (2) Showing concurrent mutations
if false {
let criticalCount2 = Synchronized(0)
let group2 = DispatchGroup()
for _ in 0..<numIterations {
group2.enter()
DispatchQueue.global().async {
criticalCount2.update { count in
let original = count
usleep(10)
count = original + 1
}
group2.leave()
}
}
group2.wait()
print("(2) expected: \(numIterations), actual: \(criticalCount2.unsafeGet())")
}
// (3) Use a different locking strategy
if false {
let criticalCount3 = Synchronized(0, lock: DispatchQueue(label: "lock"))
let group3 = DispatchGroup()
for _ in 0..<numIterations {
group3.enter()
DispatchQueue.global().async {
criticalCount3.update { count in
let original = count
usleep(10)
count = original + 1
}
group3.leave()
}
}
group3.wait()
print("(3) expected: \(numIterations), actual: \(criticalCount3.unsafeGet())")
}