@@ -216,92 +216,53 @@ including important milestones.
216
216
217
217
Because Swift concurrency can resume on a different thread
218
218
after a potential suspension point,
219
- you can't use elements like thread-local storage, locks, mutexes, or semaphores
220
- across suspension points.
221
- For example, accessing thread-local storage
222
- on both sides of a suspension point could produce incorrect results.
219
+ using elements like thread-local storage, locks, mutexes, or semaphores
220
+ across suspension points can lead to incorrect results.
223
221
224
- ``` swift
225
- let customThreadLog = " CustomThreadLog"
226
-
227
- // Reads the log for the current thread.
228
- func readLog () -> String ? {
229
- return Thread.current .threadDictionary [customThreadLog] as? String
230
- }
231
-
232
- // Add text to the log for the current thread.
233
- func appendToLog (_ text : String ) {
234
- var log = readLog () ?? " "
235
- log.append (text)
236
- Thread.current .threadDictionary [customThreadLog] = log
237
- }
238
-
239
- // This function modifies thread-local storage across a
240
- // potential suspension point.
241
- func verify (name : String ) async {
242
- // This call appends the text to the current thread's log.
243
- appendToLog (" Verifying \( name ) .\n " )
244
-
245
- // This is a potential suspension point.
246
- let result = await coin.flip ()
247
-
248
- // This call might append the text to a different thread's log.
249
- appendToLog (" \( name ) \( result ? " is" : " isn't" ) verified!\n\n " )
250
- }
251
- ```
252
-
253
- In the previous code listing,
254
- the compiler doesn't have enough information to detect the problem.
255
- To prevent issues like this,
256
- add an ` @available(*, noasync) ` attribute to the symbol's declaration:
222
+ To avoid any errors, add an ` @available(*, noasync) ` attribute to the symbol's declaration:
257
223
258
224
``` swift
259
- // Reads the name for the current thread.
260
- @available (* , noasync )
261
- func readLog () -> String ? {
262
- return Thread.current .threadDictionary [customThreadLog] as? String
225
+ extension pthread_mutex_t {
226
+
227
+ @available (* , noasync )
228
+ mutating func lock () {
229
+ pthread_mutex_lock (& self )
230
+ }
231
+
232
+ @available (* , noasync )
233
+ mutating func unlock () {
234
+ pthread_mutex_unlock (& self )
235
+ }
263
236
}
264
237
```
265
238
266
239
This attribute tells the compiler to raise a build error
267
240
when the symbol is used in an asynchronous context.
268
- You can also use the ` message ` attribute to provide additional information
241
+ You can also use the ` message ` argument to provide additional information
269
242
about the symbol.
270
243
271
244
``` swift
272
- // Reads the name for the current thread.
273
- @available (* , noasync , message : " Use safeVerify(name:) instead." )
274
- func readLog () -> String ? {
275
- return Thread.current .threadDictionary [customThreadLog] as? String
245
+ @available (* , noasync , message : " Migrate locks to Swift concurrency." )
246
+ mutating func lock () {
247
+ pthread_mutex_lock (& self )
276
248
}
277
249
```
278
250
279
- While declaring noasync availability prevents accidental, unsafe use of a symbol,
280
- it can also prevent safe uses.
281
- In some cases, the symbols can be used safely in specific situations.
282
- If you can ensure that a particular use is safe in an asynchronous context,
283
- wrap the symbol in a safe, synchronous function.
284
- You can then call the synchronous wrapper from your asynchronous code.
285
- The compiler won't raise an error, because the ` noasync ` symbol
286
- isn't used directly in an asynchronous context.
251
+ If you can guarantee that your code uses a potentially unsafe symbol in a safe way,
252
+ You can wrap it in a function and call that function
253
+ from an asynchronous context.
287
254
288
255
``` swift
289
- // A synchronous wrapper function around the noasync functions
290
- // guarantees that there won't be a potential suspension point
291
- // between the appendToLog() calls.
292
- func safeVerify (name : String ) -> String {
293
- appendToLog (" Verifying \( name ) .\n " )
294
-
295
- // This is no longer a potential suspension point.
296
- let result = coin.synchronousFlip ()
256
+ // Using a wrapper ensures the mutex is locked
257
+ // and unlocked on the same thread.
258
+ func modifyProtectedData () -> Int {
259
+ var mutex = pthread_mutex_t ()
297
260
298
- appendToLog (" \( name ) \( result ? " is" : " isn't" ) verified!\n\n " )
299
-
300
- // If this is called from an asynchronous context,
301
- // multiple calls to safeVerify() don't necessarily occur on the same thread,
302
- // and each thread has its own log.
303
- // Return the log for the current thread.
304
- return readLog () ?? " No log found."
261
+ mutex.lock ()
262
+ count += 1
263
+ mutex.unlock ()
264
+
265
+ return count
305
266
}
306
267
```
307
268
0 commit comments