1- #if compiler(>=6.1) && _runtime(_multithreaded)
21#if canImport(wasi_pthread)
32import wasi_pthread
43#elseif canImport(Darwin)
@@ -9,8 +8,14 @@ import Glibc
98#error("Unsupported platform")
109#endif
1110
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.
1214@propertyWrapper
1315final 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.
1419 var wrappedValue : Value ? {
1520 get {
1621 guard let pointer = pthread_getspecific ( key) else {
@@ -34,6 +39,8 @@ final class ThreadLocal<Value>: Sendable {
3439 private let fromPointer : @Sendable ( UnsafeMutableRawPointer ) -> Value
3540 private let release : @Sendable ( UnsafeMutableRawPointer ) -> Void
3641
42+ /// A constructor that requires `Value` to be `AnyObject` to be
43+ /// able to store the value directly in the thread-local storage.
3744 init ( ) where Value: AnyObject {
3845 var key = pthread_key_t ( )
3946 pthread_key_create ( & key, nil )
@@ -43,13 +50,15 @@ final class ThreadLocal<Value>: Sendable {
4350 self . release = { Unmanaged < Value > . fromOpaque ( $0) . release ( ) }
4451 }
4552
46- class Box {
53+ private class Box {
4754 let value : Value
4855 init ( _ value: Value ) {
4956 self . value = value
5057 }
5158 }
5259
60+ /// A constructor that doesn't require `Value` to be `AnyObject` but
61+ /// boxing the value in heap-allocated memory.
5362 init ( boxing _: Void ) {
5463 var key = pthread_key_t ( )
5564 pthread_key_create ( & key, nil )
@@ -65,15 +74,26 @@ final class ThreadLocal<Value>: Sendable {
6574 }
6675 self . release = { Unmanaged < Box > . fromOpaque ( $0) . release ( ) }
6776 }
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
6889
6990 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 " )
7492 }
7593}
7694
95+ /// A property wrapper that lazily initializes a thread-local value
96+ /// for each thread that accesses the value.
7797@propertyWrapper
7898final class LazyThreadLocal < Value> : Sendable {
7999 private let storage : ThreadLocal < Value >
@@ -99,5 +119,3 @@ final class LazyThreadLocal<Value>: Sendable {
99119 self . initialValue = initialize
100120 }
101121}
102-
103- #endif
0 commit comments