-
-
Notifications
You must be signed in to change notification settings - Fork 73
Expand file tree
/
Copy pathThreadLocal.swift
More file actions
131 lines (119 loc) · 3.92 KB
/
ThreadLocal.swift
File metadata and controls
131 lines (119 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#if arch(wasm32)
#if canImport(wasi_pthread)
import wasi_pthread
#endif
#elseif canImport(Darwin)
import Darwin
#elseif canImport(Android)
import Android
#elseif canImport(Glibc)
import Glibc
#else
#error("Unsupported platform")
#endif
/// A property wrapper that provides thread-local storage for a value.
///
/// The value is stored in a thread-local variable, which is a separate copy for each thread.
@propertyWrapper
final class ThreadLocal<Value>: Sendable {
#if compiler(>=6.1) && _runtime(_multithreaded)
/// The wrapped value stored in the thread-local storage.
/// The initial value is `nil` for each thread.
var wrappedValue: Value? {
get {
guard let pointer = pthread_getspecific(key) else {
return nil
}
return fromPointer(pointer)
}
set {
if let oldPointer = pthread_getspecific(key) {
release(oldPointer)
}
if let newValue = newValue {
let pointer = toPointer(newValue)
pthread_setspecific(key, pointer)
}
}
}
private let key: pthread_key_t
private let toPointer: @Sendable (Value) -> UnsafeMutableRawPointer
private let fromPointer: @Sendable (UnsafeMutableRawPointer) -> Value
private let release: @Sendable (UnsafeMutableRawPointer) -> Void
/// A constructor that requires `Value` to be `AnyObject` to be
/// able to store the value directly in the thread-local storage.
init() where Value: AnyObject {
var key = pthread_key_t()
pthread_key_create(&key, nil)
self.key = key
self.toPointer = { Unmanaged.passRetained($0).toOpaque() }
self.fromPointer = { Unmanaged<Value>.fromOpaque($0).takeUnretainedValue() }
self.release = { Unmanaged<Value>.fromOpaque($0).release() }
}
private class Box {
let value: Value
init(_ value: Value) {
self.value = value
}
}
/// A constructor that doesn't require `Value` to be `AnyObject` but
/// boxing the value in heap-allocated memory.
init(boxing _: Void) {
var key = pthread_key_t()
pthread_key_create(&key, nil)
self.key = key
self.toPointer = {
let box = Box($0)
let pointer = Unmanaged.passRetained(box).toOpaque()
return pointer
}
self.fromPointer = {
let box = Unmanaged<Box>.fromOpaque($0).takeUnretainedValue()
return box.value
}
self.release = { Unmanaged<Box>.fromOpaque($0).release() }
}
#else
// Fallback implementation for platforms that don't support pthread
private class SendableBox: @unchecked Sendable {
var value: Value? = nil
}
private let _storage = SendableBox()
var wrappedValue: Value? {
get { _storage.value }
set { _storage.value = newValue }
}
init() where Value: AnyObject {
wrappedValue = nil
}
init(boxing _: Void) {
wrappedValue = nil
}
#endif
deinit {
preconditionFailure("ThreadLocal can only be used as an immortal storage, cannot be deallocated")
}
}
/// A property wrapper that lazily initializes a thread-local value
/// for each thread that accesses the value.
@propertyWrapper
final class LazyThreadLocal<Value>: Sendable {
private let storage: ThreadLocal<Value>
var wrappedValue: Value {
if let value = storage.wrappedValue {
return value
}
let value = initialValue()
storage.wrappedValue = value
return value
}
private let initialValue: @Sendable () -> Value
init(initialize: @Sendable @escaping () -> Value) where Value: AnyObject {
self.storage = ThreadLocal()
self.initialValue = initialize
}
init(initialize: @Sendable @escaping () -> Value) {
self.storage = ThreadLocal(boxing: ())
self.initialValue = initialize
}
}