1
- #if compiler(>=6.1) && _runtime(_multithreaded)
2
1
#if canImport(wasi_pthread)
3
2
import wasi_pthread
4
3
#elseif canImport(Darwin)
@@ -9,8 +8,14 @@ import Glibc
9
8
#error("Unsupported platform")
10
9
#endif
11
10
11
+ /// A property wrapper that provides thread-local storage for a value.
12
+ ///
13
+ /// The value is stored in a thread-local variable, which is a separate copy for each thread.
12
14
@propertyWrapper
13
15
final class ThreadLocal < Value> : Sendable {
16
+ #if compiler(>=6.1) && _runtime(_multithreaded)
17
+ /// The wrapped value stored in the thread-local storage.
18
+ /// The initial value is `nil` for each thread.
14
19
var wrappedValue : Value ? {
15
20
get {
16
21
guard let pointer = pthread_getspecific ( key) else {
@@ -34,6 +39,8 @@ final class ThreadLocal<Value>: Sendable {
34
39
private let fromPointer : @Sendable ( UnsafeMutableRawPointer ) -> Value
35
40
private let release : @Sendable ( UnsafeMutableRawPointer ) -> Void
36
41
42
+ /// A constructor that requires `Value` to be `AnyObject` to be
43
+ /// able to store the value directly in the thread-local storage.
37
44
init ( ) where Value: AnyObject {
38
45
var key = pthread_key_t ( )
39
46
pthread_key_create ( & key, nil )
@@ -43,13 +50,15 @@ final class ThreadLocal<Value>: Sendable {
43
50
self . release = { Unmanaged < Value > . fromOpaque ( $0) . release ( ) }
44
51
}
45
52
46
- class Box {
53
+ private class Box {
47
54
let value : Value
48
55
init ( _ value: Value ) {
49
56
self . value = value
50
57
}
51
58
}
52
59
60
+ /// A constructor that doesn't require `Value` to be `AnyObject` but
61
+ /// boxing the value in heap-allocated memory.
53
62
init ( boxing _: Void ) {
54
63
var key = pthread_key_t ( )
55
64
pthread_key_create ( & key, nil )
@@ -65,15 +74,26 @@ final class ThreadLocal<Value>: Sendable {
65
74
}
66
75
self . release = { Unmanaged < Box > . fromOpaque ( $0) . release ( ) }
67
76
}
77
+ #else
78
+ // Fallback implementation for platforms that don't support pthread
79
+
80
+ var wrappedValue : Value ?
81
+
82
+ init ( ) where Value: AnyObject {
83
+ wrappedValue = nil
84
+ }
85
+ init ( boxing _: Void ) {
86
+ wrappedValue = nil
87
+ }
88
+ #endif
68
89
69
90
deinit {
70
- if let oldPointer = pthread_getspecific ( key) {
71
- release ( oldPointer)
72
- }
73
- pthread_key_delete ( key)
91
+ preconditionFailure ( " ThreadLocal can only be used as an immortal storage, cannot be deallocated " )
74
92
}
75
93
}
76
94
95
+ /// A property wrapper that lazily initializes a thread-local value
96
+ /// for each thread that accesses the value.
77
97
@propertyWrapper
78
98
final class LazyThreadLocal < Value> : Sendable {
79
99
private let storage : ThreadLocal < Value >
@@ -99,5 +119,3 @@ final class LazyThreadLocal<Value>: Sendable {
99
119
self . initialValue = initialize
100
120
}
101
121
}
102
-
103
- #endif
0 commit comments