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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#if os(Linux) || os(FreeBSD) || os(Android)
import CNIOLinux
#endif
enum LowLevelThreadOperations {
}
protocol ThreadOps {
associatedtype ThreadHandle
associatedtype ThreadSpecificKey
associatedtype ThreadSpecificKeyDestructor
static func threadName(_ thread: ThreadHandle) -> String?
static func run(handle: inout ThreadHandle?, args: Box<NIOThread.ThreadBoxValue>, detachThread: Bool)
static func isCurrentThread(_ thread: ThreadHandle) -> Bool
static func compareThreads(_ lhs: ThreadHandle, _ rhs: ThreadHandle) -> Bool
static var currentThread: ThreadHandle { get }
static func joinThread(_ thread: ThreadHandle)
static func allocateThreadSpecificValue(destructor: ThreadSpecificKeyDestructor) -> ThreadSpecificKey
static func deallocateThreadSpecificValue(_ key: ThreadSpecificKey)
static func getThreadSpecificValue(_ key: ThreadSpecificKey) -> UnsafeMutableRawPointer?
static func setThreadSpecificValue(key: ThreadSpecificKey, value: UnsafeMutableRawPointer?)
}
/// A Thread that executes some runnable block.
///
/// All methods exposed are thread-safe.
final class NIOThread {
internal typealias ThreadBoxValue = (body: (NIOThread) -> Void, name: String?)
internal typealias ThreadBox = Box<ThreadBoxValue>
private let desiredName: String?
/// The thread handle used by this instance.
private let handle: ThreadOpsSystem.ThreadHandle
/// Create a new instance
///
/// - arguments:
/// - handle: The `ThreadOpsSystem.ThreadHandle` that is wrapped and used by the `NIOThread`.
internal init(handle: ThreadOpsSystem.ThreadHandle, desiredName: String?) {
self.handle = handle
self.desiredName = desiredName
}
/// Execute the given body with the `pthread_t` that is used by this `NIOThread` as argument.
///
/// - warning: Do not escape `pthread_t` from the closure for later use.
///
/// - parameters:
/// - body: The closure that will accept the `pthread_t`.
/// - returns: The value returned by `body`.
internal func withUnsafeThreadHandle<T>(_ body: (ThreadOpsSystem.ThreadHandle) throws -> T) rethrows -> T {
return try body(self.handle)
}
/// Get current name of the `NIOThread` or `nil` if not set.
var currentName: String? {
return ThreadOpsSystem.threadName(self.handle)
}
func join() {
ThreadOpsSystem.joinThread(self.handle)
}
/// Spawns and runs some task in a `NIOThread`.
///
/// - arguments:
/// - name: The name of the `NIOThread` or `nil` if no specific name should be set.
/// - body: The function to execute within the spawned `NIOThread`.
/// - detach: Whether to detach the thread. If the thread is not detached it must be `join`ed.
static func spawnAndRun(name: String? = nil, detachThread: Bool = true,
body: @escaping (NIOThread) -> Void) {
var handle: ThreadOpsSystem.ThreadHandle? = nil
// Store everything we want to pass into the c function in a Box so we
// can hand-over the reference.
let tuple: ThreadBoxValue = (body: body, name: name)
let box = ThreadBox(tuple)
ThreadOpsSystem.run(handle: &handle, args: box, detachThread: detachThread)
}
/// Returns `true` if the calling thread is the same as this one.
var isCurrent: Bool {
return ThreadOpsSystem.isCurrentThread(self.handle)
}
/// Returns the current running `NIOThread`.
static var current: NIOThread {
let handle = ThreadOpsSystem.currentThread
return NIOThread(handle: handle, desiredName: nil)
}
}
extension NIOThread: CustomStringConvertible {
var description: String {
let desiredName = self.desiredName
let actualName = self.currentName
switch (desiredName, actualName) {
case (.some(let desiredName), .some(desiredName)):
// We know the current, actual name and the desired name and they match. This is hopefully the most common
// situation.
return "NIOThread(name = \(desiredName))"
case (.some(let desiredName), .some(let actualName)):
// We know both names but they're not equal. That's odd but not impossible, some misbehaved library might
// have changed the name.
return "NIOThread(desiredName = \(desiredName), actualName = \(actualName))"
case (.some(let desiredName), .none):
// We only know the desired name and can't get the actual thread name. The OS might not be able to provide
// the name to us.
return "NIOThread(desiredName = \(desiredName))"
case (.none, .some(let actualName)):
// We only know the actual name. This can happen when we don't have a reference to the actually spawned
// thread but rather ask for the current thread and then print it.
return "NIOThread(actualName = \(actualName))"
case (.none, .none):
// We know nothing, sorry.
return "NIOThread(n/a)"
}
}
}
/// A `ThreadSpecificVariable` is a variable that can be read and set like a normal variable except that it holds
/// different variables per thread.
///
/// `ThreadSpecificVariable` is thread-safe so it can be used with multiple threads at the same time but the value
/// returned by `currentValue` is defined per thread.
public final class ThreadSpecificVariable<Value: AnyObject> {
/* the actual type in there is `Box<(ThreadSpecificVariable<T>, T)>` but we can't use that as C functions can't capture (even types) */
private typealias BoxedType = Box<(AnyObject, AnyObject)>
internal class Key {
private var underlyingKey: ThreadOpsSystem.ThreadSpecificKey
internal init(destructor: @escaping ThreadOpsSystem.ThreadSpecificKeyDestructor) {
self.underlyingKey = ThreadOpsSystem.allocateThreadSpecificValue(destructor: destructor)
}
deinit {
ThreadOpsSystem.deallocateThreadSpecificValue(self.underlyingKey)
}
public func get() -> UnsafeMutableRawPointer? {
return ThreadOpsSystem.getThreadSpecificValue(self.underlyingKey)
}
public func set(value: UnsafeMutableRawPointer?) {
ThreadOpsSystem.setThreadSpecificValue(key: self.underlyingKey, value: value)
}
}
private let key: Key
/// Initialize a new `ThreadSpecificVariable` without a current value (`currentValue == nil`).
public init() {
self.key = Key(destructor: {
Unmanaged<BoxedType>.fromOpaque(($0 as UnsafeMutableRawPointer?)!).release()
})
}
/// Initialize a new `ThreadSpecificVariable` with `value` for the calling thread. After calling this, the calling
/// thread will see `currentValue == value` but on all other threads `currentValue` will be `nil` until changed.
///
/// - parameters:
/// - value: The value to set for the calling thread.
public convenience init(value: Value) {
self.init()
self.currentValue = value
}
/// The value for the current thread.
public var currentValue: Value? {
/// Get the current value for the calling thread.
get {
guard let raw = self.key.get() else { return nil }
// parenthesize the return value to silence the cast warning
return (Unmanaged<BoxedType>
.fromOpaque(raw)
.takeUnretainedValue()
.value.1 as! Value)
}
/// Set the current value for the calling threads. The `currentValue` for all other threads remains unchanged.
set {
if let raw = self.key.get() {
Unmanaged<BoxedType>.fromOpaque(raw).release()
}
self.key.set(value: newValue.map { Unmanaged.passRetained(Box((self, $0))).toOpaque() })
}
}
}
extension NIOThread: Equatable {
static func ==(lhs: NIOThread, rhs: NIOThread) -> Bool {
return lhs.withUnsafeThreadHandle { lhs in
rhs.withUnsafeThreadHandle { rhs in
ThreadOpsSystem.compareThreads(lhs, rhs)
}
}
}
}
|