Locks.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. //===----------------------------------------------------------------------===//
  2. //
  3. // This source file is part of the Swift Metrics API open source project
  4. //
  5. // Copyright (c) 2018-2026 Apple Inc. and the Swift Metrics API project authors
  6. // Licensed under Apache License v2.0
  7. //
  8. // See LICENSE.txt for license information
  9. // See CONTRIBUTORS.txt for the list of Swift Metrics API project authors
  10. //
  11. // SPDX-License-Identifier: Apache-2.0
  12. //
  13. //===----------------------------------------------------------------------===//
  14. //===----------------------------------------------------------------------===//
  15. //
  16. // This source file is part of the SwiftNIO open source project
  17. //
  18. // Copyright (c) 2017-2026 Apple Inc. and the SwiftNIO project authors
  19. // Licensed under Apache License v2.0
  20. //
  21. // See LICENSE.txt for license information
  22. // See CONTRIBUTORS.txt for the list of SwiftNIO project authors
  23. //
  24. // SPDX-License-Identifier: Apache-2.0
  25. //
  26. //===----------------------------------------------------------------------===//
  27. #if canImport(Darwin)
  28. import Darwin
  29. #elseif os(Windows)
  30. import ucrt
  31. import WinSDK
  32. #elseif canImport(Glibc)
  33. @preconcurrency import Glibc
  34. #elseif canImport(Android)
  35. @preconcurrency import Android
  36. #elseif canImport(Musl)
  37. @preconcurrency import Musl
  38. #elseif canImport(Bionic)
  39. @preconcurrency import Bionic
  40. #elseif canImport(WASILibc)
  41. @preconcurrency import WASILibc
  42. #if canImport(wasi_pthread)
  43. import wasi_pthread
  44. #endif
  45. #else
  46. #error("The concurrency lock module was unable to identify your C library.")
  47. #endif
  48. /// A threading lock based on `libpthread` instead of `libdispatch`.
  49. ///
  50. /// This object provides a lock on top of a single `pthread_mutex_t`. This kind
  51. /// of lock is safe to use with `libpthread`-based threading models, such as the
  52. /// one used by NIO. On Windows, the lock is based on the substantially similar
  53. /// `SRWLOCK` type.
  54. package final class Lock {
  55. #if os(Windows)
  56. fileprivate let mutex: UnsafeMutablePointer<SRWLOCK> =
  57. UnsafeMutablePointer.allocate(capacity: 1)
  58. #elseif os(FreeBSD) || os(OpenBSD)
  59. fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t?> =
  60. UnsafeMutablePointer.allocate(capacity: 1)
  61. #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
  62. fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t> =
  63. UnsafeMutablePointer.allocate(capacity: 1)
  64. #endif
  65. /// Create a new lock.
  66. package init() {
  67. #if os(Windows)
  68. InitializeSRWLock(self.mutex)
  69. #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
  70. #if os(FreeBSD) || os(OpenBSD)
  71. var attr = pthread_mutexattr_t(bitPattern: 0)
  72. #else
  73. var attr = pthread_mutexattr_t()
  74. #endif
  75. var err = pthread_mutexattr_init(&attr)
  76. precondition(err == 0, "\(#function) failed in pthread_mutexattr_init with error \(err)")
  77. debugOnly {
  78. #if os(FreeBSD) || os(OpenBSD)
  79. pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK.rawValue))
  80. #else
  81. pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK))
  82. #endif
  83. }
  84. err = pthread_mutex_init(self.mutex, &attr)
  85. precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
  86. // `pthread_mutexattr_t` only lives during init; destroy here instead of deinit.
  87. let attrDestroyErr = pthread_mutexattr_destroy(&attr)
  88. precondition(
  89. attrDestroyErr == 0,
  90. "\(#function) failed in pthread_mutexattr_destroy with error \(attrDestroyErr)"
  91. )
  92. #endif
  93. }
  94. deinit {
  95. #if os(Windows)
  96. mutex.deallocate()
  97. #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
  98. let err = pthread_mutex_destroy(self.mutex)
  99. precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
  100. mutex.deallocate()
  101. #endif
  102. }
  103. /// Acquire the lock.
  104. ///
  105. /// Whenever possible, consider using `withLock` instead of this method and
  106. /// `unlock`, to simplify lock handling.
  107. package func lock() {
  108. #if os(Windows)
  109. AcquireSRWLockExclusive(self.mutex)
  110. #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
  111. let err = pthread_mutex_lock(self.mutex)
  112. precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
  113. #endif
  114. }
  115. /// Release the lock.
  116. ///
  117. /// Whenever possible, consider using `withLock` instead of this method and
  118. /// `lock`, to simplify lock handling.
  119. package func unlock() {
  120. #if os(Windows)
  121. ReleaseSRWLockExclusive(self.mutex)
  122. #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
  123. let err = pthread_mutex_unlock(self.mutex)
  124. precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
  125. #endif
  126. }
  127. /// Acquire the lock for the duration of the given block.
  128. ///
  129. /// This convenience method should be preferred to `lock` and `unlock` in
  130. /// most situations, as it ensures that the lock will be released regardless
  131. /// of how `body` exits.
  132. ///
  133. /// - Parameter body: The block to execute while holding the lock.
  134. /// - Returns: The value returned by the block.
  135. @inlinable
  136. package func withLock<T>(_ body: () throws -> T) rethrows -> T {
  137. self.lock()
  138. defer {
  139. self.unlock()
  140. }
  141. return try body()
  142. }
  143. // specialise Void return (for performance)
  144. @inlinable
  145. package func withLockVoid(_ body: () throws -> Void) rethrows {
  146. try self.withLock(body)
  147. }
  148. }
  149. /// A utility function that runs the body code only in debug builds, without
  150. /// emitting compiler warnings.
  151. ///
  152. /// This is currently the only way to do this in Swift: see
  153. /// https://forums.swift.org/t/support-debug-only-code/11037 for a discussion.
  154. @inlinable
  155. internal func debugOnly(_ body: () -> Void) {
  156. assert(
  157. {
  158. body()
  159. return true
  160. }()
  161. )
  162. }
  163. extension Lock: @unchecked Sendable {}
  164. /// A reader/writer threading lock based on `libpthread` instead of `libdispatch`.
  165. ///
  166. /// This object provides a lock on top of a single `pthread_rwlock_t`. This kind
  167. /// of lock is safe to use with `libpthread`-based threading models, such as the
  168. /// one used by NIO. On Windows, the lock is based on the substantially similar
  169. /// `SRWLOCK` type.
  170. internal final class ReadWriteLock: @unchecked Sendable {
  171. #if canImport(WASILibc)
  172. // WASILibc is single threaded, provides no locks
  173. #elseif os(Windows)
  174. fileprivate let rwlock: UnsafeMutablePointer<SRWLOCK> =
  175. UnsafeMutablePointer.allocate(capacity: 1)
  176. fileprivate var shared: Bool = true
  177. #elseif os(FreeBSD) || os(OpenBSD)
  178. fileprivate let rwlock: UnsafeMutablePointer<pthread_rwlock_t?> =
  179. UnsafeMutablePointer.allocate(capacity: 1)
  180. #else
  181. fileprivate let rwlock: UnsafeMutablePointer<pthread_rwlock_t> =
  182. UnsafeMutablePointer.allocate(capacity: 1)
  183. #endif
  184. /// Create a new lock.
  185. public init() {
  186. #if os(Windows)
  187. InitializeSRWLock(self.rwlock)
  188. #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
  189. let err = pthread_rwlock_init(self.rwlock, nil)
  190. precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
  191. #endif
  192. }
  193. deinit {
  194. #if os(Windows)
  195. self.rwlock.deallocate()
  196. #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
  197. let err = pthread_rwlock_destroy(self.rwlock)
  198. precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
  199. self.rwlock.deallocate()
  200. #endif
  201. }
  202. /// Acquire a reader lock.
  203. ///
  204. /// Whenever possible, consider using `withReaderLock` instead of this
  205. /// method and `unlock`, to simplify lock handling.
  206. fileprivate func lockRead() {
  207. #if os(Windows)
  208. AcquireSRWLockShared(self.rwlock)
  209. self.shared = true
  210. #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
  211. let err = pthread_rwlock_rdlock(self.rwlock)
  212. precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
  213. #endif
  214. }
  215. /// Acquire a writer lock.
  216. ///
  217. /// Whenever possible, consider using `withWriterLock` instead of this
  218. /// method and `unlock`, to simplify lock handling.
  219. fileprivate func lockWrite() {
  220. #if os(Windows)
  221. AcquireSRWLockExclusive(self.rwlock)
  222. self.shared = false
  223. #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
  224. let err = pthread_rwlock_wrlock(self.rwlock)
  225. precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
  226. #endif
  227. }
  228. /// Release the lock.
  229. ///
  230. /// Whenever possible, consider using `withReaderLock` and `withWriterLock`
  231. /// instead of this method and `lockRead` and `lockWrite`, to simplify lock
  232. /// handling.
  233. fileprivate func unlock() {
  234. #if os(Windows)
  235. if self.shared {
  236. ReleaseSRWLockShared(self.rwlock)
  237. } else {
  238. ReleaseSRWLockExclusive(self.rwlock)
  239. }
  240. #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
  241. let err = pthread_rwlock_unlock(self.rwlock)
  242. precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
  243. #endif
  244. }
  245. }
  246. extension ReadWriteLock {
  247. /// Acquire the reader lock for the duration of the given block.
  248. ///
  249. /// This convenience method should be preferred to `lockRead` and `unlock`
  250. /// in most situations, as it ensures that the lock will be released
  251. /// regardless of how `body` exits.
  252. ///
  253. /// - Parameter body: The block to execute while holding the reader lock.
  254. /// - Returns: The value returned by the block.
  255. @inlinable
  256. internal func withReaderLock<T>(_ body: () throws -> T) rethrows -> T {
  257. self.lockRead()
  258. defer {
  259. self.unlock()
  260. }
  261. return try body()
  262. }
  263. /// Acquire the writer lock for the duration of the given block.
  264. ///
  265. /// This convenience method should be preferred to `lockWrite` and `unlock`
  266. /// in most situations, as it ensures that the lock will be released
  267. /// regardless of how `body` exits.
  268. ///
  269. /// - Parameter body: The block to execute while holding the writer lock.
  270. /// - Returns: The value returned by the block.
  271. @inlinable
  272. internal func withWriterLock<T>(_ body: () throws -> T) rethrows -> T {
  273. self.lockWrite()
  274. defer {
  275. self.unlock()
  276. }
  277. return try body()
  278. }
  279. // specialise Void return (for performance)
  280. @inlinable
  281. internal func withReaderLockVoid(_ body: () throws -> Void) rethrows {
  282. try self.withReaderLock(body)
  283. }
  284. // specialise Void return (for performance)
  285. @inlinable
  286. internal func withWriterLockVoid(_ body: () throws -> Void) rethrows {
  287. try self.withWriterLock(body)
  288. }
  289. }