Skip to content

Commit 0d21163

Browse files
committed
Tightened up the language and aimed it at a more advanced audience.
Also replaced the code snippets with a better example.
1 parent 6bb2bd2 commit 0d21163

File tree

1 file changed

+30
-69
lines changed

1 file changed

+30
-69
lines changed

TSPL.docc/ReferenceManual/Attributes.md

Lines changed: 30 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -216,92 +216,53 @@ including important milestones.
216216

217217
Because Swift concurrency can resume on a different thread
218218
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.
223221

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:
257223

258224
```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+
}
263236
}
264237
```
265238

266239
This attribute tells the compiler to raise a build error
267240
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
269242
about the symbol.
270243

271244
```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)
276248
}
277249
```
278250

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.
287254

288255
```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()
297260

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
305266
}
306267
```
307268

0 commit comments

Comments
 (0)