Lock.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /*
  2. * Copyright 2023, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. //===----------------------------------------------------------------------===//
  17. //
  18. // This source file is part of the SwiftNIO open source project
  19. //
  20. // Copyright (c) 2017-2022 Apple Inc. and the SwiftNIO project authors
  21. // Licensed under Apache License v2.0
  22. //
  23. // See LICENSE.txt for license information
  24. // See CONTRIBUTORS.txt for the list of SwiftNIO project authors
  25. //
  26. // SPDX-License-Identifier: Apache-2.0
  27. //
  28. //===----------------------------------------------------------------------===//
  29. #if canImport(Darwin)
  30. import Darwin
  31. #elseif canImport(Glibc)
  32. import Glibc
  33. #endif
  34. @usableFromInline
  35. typealias LockPrimitive = pthread_mutex_t
  36. @usableFromInline
  37. enum LockOperations {}
  38. extension LockOperations {
  39. @inlinable
  40. static func create(_ mutex: UnsafeMutablePointer<LockPrimitive>) {
  41. mutex.assertValidAlignment()
  42. var attr = pthread_mutexattr_t()
  43. pthread_mutexattr_init(&attr)
  44. let err = pthread_mutex_init(mutex, &attr)
  45. precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
  46. }
  47. @inlinable
  48. static func destroy(_ mutex: UnsafeMutablePointer<LockPrimitive>) {
  49. mutex.assertValidAlignment()
  50. let err = pthread_mutex_destroy(mutex)
  51. precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
  52. }
  53. @inlinable
  54. static func lock(_ mutex: UnsafeMutablePointer<LockPrimitive>) {
  55. mutex.assertValidAlignment()
  56. let err = pthread_mutex_lock(mutex)
  57. precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
  58. }
  59. @inlinable
  60. static func unlock(_ mutex: UnsafeMutablePointer<LockPrimitive>) {
  61. mutex.assertValidAlignment()
  62. let err = pthread_mutex_unlock(mutex)
  63. precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
  64. }
  65. }
  66. // Tail allocate both the mutex and a generic value using ManagedBuffer.
  67. // Both the header pointer and the elements pointer are stable for
  68. // the class's entire lifetime.
  69. //
  70. // However, for safety reasons, we elect to place the lock in the "elements"
  71. // section of the buffer instead of the head. The reasoning here is subtle,
  72. // so buckle in.
  73. //
  74. // _As a practical matter_, the implementation of ManagedBuffer ensures that
  75. // the pointer to the header is stable across the lifetime of the class, and so
  76. // each time you call `withUnsafeMutablePointers` or `withUnsafeMutablePointerToHeader`
  77. // the value of the header pointer will be the same. This is because ManagedBuffer uses
  78. // `Builtin.addressOf` to load the value of the header, and that does ~magic~ to ensure
  79. // that it does not invoke any weird Swift accessors that might copy the value.
  80. //
  81. // _However_, the header is also available via the `.header` field on the ManagedBuffer.
  82. // This presents a problem! The reason there's an issue is that `Builtin.addressOf` and friends
  83. // do not interact with Swift's exclusivity model. That is, the various `with` functions do not
  84. // conceptually trigger a mutating access to `.header`. For elements this isn't a concern because
  85. // there's literally no other way to perform the access, but for `.header` it's entirely possible
  86. // to accidentally recursively read it.
  87. //
  88. // Our implementation is free from these issues, so we don't _really_ need to worry about it.
  89. // However, out of an abundance of caution, we store the Value in the header, and the LockPrimitive
  90. // in the trailing elements. We still don't use `.header`, but it's better to be safe than sorry,
  91. // and future maintainers will be happier that we were cautious.
  92. //
  93. // See also: https://github.com/apple/swift/pull/40000
  94. @usableFromInline
  95. final class LockStorage<Value>: ManagedBuffer<Value, LockPrimitive> {
  96. @inlinable
  97. static func create(value: Value) -> Self {
  98. let buffer = Self.create(minimumCapacity: 1) { _ in
  99. return value
  100. }
  101. let storage = unsafeDowncast(buffer, to: Self.self)
  102. storage.withUnsafeMutablePointers { _, lockPtr in
  103. LockOperations.create(lockPtr)
  104. }
  105. return storage
  106. }
  107. @inlinable
  108. func lock() {
  109. self.withUnsafeMutablePointerToElements { lockPtr in
  110. LockOperations.lock(lockPtr)
  111. }
  112. }
  113. @inlinable
  114. func unlock() {
  115. self.withUnsafeMutablePointerToElements { lockPtr in
  116. LockOperations.unlock(lockPtr)
  117. }
  118. }
  119. @inlinable
  120. deinit {
  121. self.withUnsafeMutablePointerToElements { lockPtr in
  122. LockOperations.destroy(lockPtr)
  123. }
  124. }
  125. @inlinable
  126. func withLockPrimitive<T>(
  127. _ body: (UnsafeMutablePointer<LockPrimitive>) throws -> T
  128. ) rethrows -> T {
  129. try self.withUnsafeMutablePointerToElements { lockPtr in
  130. return try body(lockPtr)
  131. }
  132. }
  133. @inlinable
  134. func withLockedValue<T>(_ mutate: (inout Value) throws -> T) rethrows -> T {
  135. try self.withUnsafeMutablePointers { valuePtr, lockPtr in
  136. LockOperations.lock(lockPtr)
  137. defer { LockOperations.unlock(lockPtr) }
  138. return try mutate(&valuePtr.pointee)
  139. }
  140. }
  141. }
  142. extension LockStorage: @unchecked Sendable {}
  143. /// A threading lock based on `libpthread` instead of `libdispatch`.
  144. ///
  145. /// - note: ``Lock`` has reference semantics.
  146. ///
  147. /// This object provides a lock on top of a single `pthread_mutex_t`. This kind
  148. /// of lock is safe to use with `libpthread`-based threading models, such as the
  149. /// one used by NIO. On Windows, the lock is based on the substantially similar
  150. /// `SRWLOCK` type.
  151. @usableFromInline
  152. struct Lock {
  153. @usableFromInline
  154. internal let _storage: LockStorage<Void>
  155. /// Create a new lock.
  156. @inlinable
  157. init() {
  158. self._storage = .create(value: ())
  159. }
  160. /// Acquire the lock.
  161. ///
  162. /// Whenever possible, consider using `withLock` instead of this method and
  163. /// `unlock`, to simplify lock handling.
  164. @inlinable
  165. func lock() {
  166. self._storage.lock()
  167. }
  168. /// Release the lock.
  169. ///
  170. /// Whenever possible, consider using `withLock` instead of this method and
  171. /// `lock`, to simplify lock handling.
  172. @inlinable
  173. func unlock() {
  174. self._storage.unlock()
  175. }
  176. @inlinable
  177. internal func withLockPrimitive<T>(
  178. _ body: (UnsafeMutablePointer<LockPrimitive>) throws -> T
  179. ) rethrows -> T {
  180. return try self._storage.withLockPrimitive(body)
  181. }
  182. }
  183. extension Lock {
  184. /// Acquire the lock for the duration of the given block.
  185. ///
  186. /// This convenience method should be preferred to `lock` and `unlock` in
  187. /// most situations, as it ensures that the lock will be released regardless
  188. /// of how `body` exits.
  189. ///
  190. /// - Parameter body: The block to execute while holding the lock.
  191. /// - Returns: The value returned by the block.
  192. @inlinable
  193. func withLock<T>(_ body: () throws -> T) rethrows -> T {
  194. self.lock()
  195. defer {
  196. self.unlock()
  197. }
  198. return try body()
  199. }
  200. }
  201. extension Lock: Sendable {}
  202. extension UnsafeMutablePointer {
  203. @inlinable
  204. func assertValidAlignment() {
  205. assert(UInt(bitPattern: self) % UInt(MemoryLayout<Pointee>.alignment) == 0)
  206. }
  207. }
  208. @usableFromInline
  209. struct LockedValueBox<Value> {
  210. @usableFromInline
  211. let storage: LockStorage<Value>
  212. @inlinable
  213. init(_ value: Value) {
  214. self.storage = .create(value: value)
  215. }
  216. @inlinable
  217. func withLockedValue<T>(_ mutate: (inout Value) throws -> T) rethrows -> T {
  218. return try self.storage.withLockedValue(mutate)
  219. }
  220. }
  221. extension LockedValueBox: Sendable where Value: Sendable {}