@@ -13,7 +13,8 @@ class TestNSLock: XCTestCase {
13
13
14
14
( " test_lockWait " , test_lockWait) ,
15
15
( " test_threadsAndLocks " , test_threadsAndLocks) ,
16
-
16
+ ( " test_recursiveLock " , test_recursiveLock) ,
17
+
17
18
]
18
19
}
19
20
@@ -115,4 +116,75 @@ class TestNSLock: XCTestCase {
115
116
lock. unlock ( )
116
117
}
117
118
}
119
+
120
+ func test_recursiveLock( ) {
121
+ // We will start 10 threads, and their collective task will be to reduce
122
+ // the countdown value to zero in less than 30 seconds.
123
+
124
+ let threadCount = 10
125
+ let countdownPerThread = 1000
126
+ let endTime = Date ( timeIntervalSinceNow: 30 )
127
+
128
+ var completedThreadCount = 0
129
+ var countdownValue = threadCount * countdownPerThread
130
+
131
+ let threadCompletedCondition = NSCondition ( )
132
+ let countdownValueLock = NSRecursiveLock ( )
133
+
134
+ for _ in 0 ..< threadCount {
135
+ let thread = Thread {
136
+
137
+ // Some dummy work on countdown.
138
+ // Add/substract operations are balanced to decrease countdown
139
+ // exactly by countdownPerThread
140
+
141
+ for _ in 0 ..< countdownPerThread {
142
+ countdownValueLock. lock ( )
143
+ countdownValue -= 3
144
+ countdownValueLock. lock ( )
145
+ countdownValue += 4
146
+ countdownValueLock. unlock ( )
147
+ countdownValueLock. unlock ( )
148
+
149
+ countdownValueLock. lock ( )
150
+ countdownValue += 2
151
+ countdownValueLock. lock ( )
152
+ countdownValue -= 1
153
+ countdownValueLock. unlock ( )
154
+ countdownValue -= 3
155
+ countdownValueLock. unlock ( )
156
+
157
+ countdownValueLock. lock ( )
158
+ countdownValue += 1
159
+ countdownValueLock. unlock ( )
160
+
161
+ countdownValueLock. lock ( )
162
+ countdownValue -= 1
163
+ countdownValueLock. unlock ( )
164
+ }
165
+
166
+ threadCompletedCondition. lock ( )
167
+ completedThreadCount += 1
168
+ threadCompletedCondition. broadcast ( )
169
+ threadCompletedCondition. unlock ( )
170
+ }
171
+ thread. start ( )
172
+ }
173
+
174
+ threadCompletedCondition. lock ( )
175
+
176
+ var isTimedOut = false
177
+ while !isTimedOut && completedThreadCount < threadCount {
178
+ isTimedOut = !threadCompletedCondition. wait ( until: endTime)
179
+ }
180
+
181
+ countdownValueLock. lock ( )
182
+ let resultCountdownValue = countdownValue
183
+ countdownValueLock. unlock ( )
184
+
185
+ XCTAssertEqual ( completedThreadCount, threadCount, " Some threads are still not finished, could be hung " )
186
+ XCTAssertEqual ( resultCountdownValue, 0 , " Wrong coundtdown means unresolved race conditions, locks broken " )
187
+
188
+ threadCompletedCondition. unlock ( )
189
+ }
118
190
}
0 commit comments