44
55import Foundation
66
7+ /// Don't use this propertyWrapper for value types, esp. Collections (Array, Dictionary, Set)
8+ /// This can have unexpected results: see https://www.donnywals.com/why-your-atomic-property-wrapper-doesnt-work-for-collection-types/
79@propertyWrapper
810struct Atomic < Value> {
911 private var value : Value
@@ -14,19 +16,156 @@ struct Atomic<Value> {
1416 }
1517
1618 var wrappedValue : Value {
17- get { return load ( ) }
18- set { store ( newValue: newValue) }
19+ get { lock. atomic { value } }
20+ set { lock. atomic { value = newValue } }
21+ }
22+ }
23+
24+ extension NSLock {
25+ func atomic< T> ( _ closure: ( ) throws -> T ) rethrows -> T {
26+ lock ( )
27+ defer {
28+ unlock ( )
29+ }
30+ return try closure ( )
31+ }
32+ }
33+
34+ // Inspired by donnywals https://www.donnywals.com/why-your-atomic-property-wrapper-doesnt-work-for-collection-types/
35+ // We must make this a reference type to avoid getting a copy for each different thread (expected value type behavior)
36+ class AtomicDictionary < Key: Hashable , Value> : CustomDebugStringConvertible {
37+ private var dict = [ Key: Value] ( )
38+ private let lock = NSLock ( )
39+
40+ subscript( key: Key ) -> Value ? {
41+ get { lock. atomic { dict [ key] } }
42+ set { lock. atomic { dict [ key] = newValue } }
43+ }
44+
45+ var debugDescription : String {
46+ lock. atomic { dict. debugDescription }
47+ }
48+ }
49+
50+ extension AtomicDictionary where Value: Equatable {
51+ static func == < Key: Hashable , Value: Equatable > ( lhs: AtomicDictionary < Key , Value > , rhs: AtomicDictionary < Key , Value > ) -> Bool {
52+ lhs. dict == rhs. dict
53+ }
54+ }
55+
56+ class AtomicArray < T> : CustomDebugStringConvertible , Sequence , Collection {
57+ private var array : [ T ]
58+ private let lock = NSLock ( )
59+
60+ var startIndex : Int {
61+ lock. atomic { array. startIndex }
62+ }
63+
64+ var endIndex : Int {
65+ lock. atomic { array. endIndex }
66+ }
67+
68+ init ( _ array: [ T ] = [ ] ) {
69+ self . array = array
70+ }
71+
72+ subscript( index: Int ) -> T {
73+ get { lock. atomic { array [ index] } }
74+ set { lock. atomic { array [ index] = newValue } }
75+ }
76+
77+ func append( _ newElement: T ) {
78+ lock. atomic { array. append ( newElement) }
79+ }
80+
81+ var debugDescription : String {
82+ lock. atomic { array. debugDescription }
83+ }
84+
85+ func removeAll( ) {
86+ lock. atomic { array. removeAll ( ) }
87+ }
88+
89+ func removeAll( where shouldBeRemoved: ( Element ) throws -> Bool ) rethrows {
90+ try lock. atomic {
91+ try array. removeAll ( where: shouldBeRemoved)
92+ }
1993 }
2094
21- private func load( ) -> Value {
95+ func index( after index: Int ) -> Int {
96+ lock. atomic {
97+ array. index ( after: index)
98+ }
99+ }
100+
101+ func index( before index: Int ) -> Int {
102+ lock. atomic {
103+ array. index ( before: index)
104+ }
105+ }
106+
107+ func makeIterator( ) -> IndexingIterator < [ T ] > {
22108 lock. lock ( )
23109 defer { lock. unlock ( ) }
24- return value
110+ return array. makeIterator ( )
111+ }
112+ }
113+
114+ extension AtomicArray where T: Equatable {
115+ func contains( _ element: T ) -> Bool {
116+ lock. atomic { array. contains ( element) }
117+ }
118+
119+ static func == < T: Equatable > ( lhs: AtomicArray < T > , rhs: AtomicArray < T > ) -> Bool {
120+ lhs. array == rhs. array
121+ }
122+ }
123+
124+ class AtomicSet < T: Hashable > : CustomDebugStringConvertible , Sequence {
125+ private var set : Set < T >
126+ private let lock = NSLock ( )
127+
128+ init ( _ set: [ T ] = [ ] ) {
129+ self . set = Set ( set)
130+ }
131+
132+ func contains( _ member: T ) -> Bool {
133+ lock. atomic { set. contains ( member) }
25134 }
26135
27- private mutating func store( newValue: Value ) {
136+ @discardableResult func insert( _ newMember: T ) -> ( inserted: Bool , memberAfterInsert: T ) {
137+ lock. atomic { set. insert ( newMember) }
138+ }
139+
140+ var debugDescription : String {
141+ lock. atomic { set. debugDescription }
142+ }
143+
144+ func makeIterator( ) -> Set < T > . Iterator {
28145 lock. lock ( )
29146 defer { lock. unlock ( ) }
30- value = newValue
147+ return set. makeIterator ( )
148+ }
149+
150+ func formUnion< S> ( _ other: S ) where T == S . Element , S: Sequence {
151+ lock. atomic { set. formUnion ( other) }
152+ }
153+
154+ func removeAll( ) {
155+ lock. atomic { set. removeAll ( ) }
156+ }
157+
158+ var count : Int {
159+ lock. atomic { set. count }
160+ }
161+
162+ var first : T ? {
163+ lock. atomic { set. first }
164+ }
165+ }
166+
167+ extension AtomicSet : Equatable {
168+ static func == < T> ( lhs: AtomicSet < T > , rhs: AtomicSet < T > ) -> Bool {
169+ lhs. set == rhs. set
31170 }
32171}
0 commit comments