Skip to content

Commit

Permalink
Merge branch 'main' into v2
Browse files Browse the repository at this point in the history
  • Loading branch information
fabianfett committed Feb 6, 2025
2 parents 0d938bf + d6b6487 commit 7937b2f
Show file tree
Hide file tree
Showing 30 changed files with 431 additions and 1,545 deletions.
16 changes: 6 additions & 10 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ jobs:
fail-fast: false
matrix:
swift-image:
- swift:5.8-jammy
- swift:5.9-jammy
- swift:5.10-noble
- swiftlang/swift:nightly-6.0-jammy
- swift:6.0-noble
- swiftlang/swift:nightly-main-jammy
container: ${{ matrix.swift-image }}
runs-on: ubuntu-latest
Expand All @@ -48,13 +47,13 @@ jobs:
fail-fast: false
matrix:
postgres-image:
- postgres:16
- postgres:14
- postgres:17
- postgres:15
- postgres:12
include:
- postgres-image: postgres:16
- postgres-image: postgres:17
postgres-auth: scram-sha-256
- postgres-image: postgres:14
- postgres-image: postgres:15
postgres-auth: md5
- postgres-image: postgres:12
postgres-auth: trust
Expand Down Expand Up @@ -134,11 +133,8 @@ jobs:
# Only test one auth method on macOS, Linux tests will cover the others
- scram-sha-256
xcode-version:
- '~14.3'
- '~15'
include:
- xcode-version: '~14.3'
macos-version: 'macos-13'
- xcode-version: '~15'
macos-version: 'macos-14'
runs-on: ${{ matrix.macos-version }}
Expand Down Expand Up @@ -172,7 +168,7 @@ jobs:
uses: actions/checkout@v4
- name: Run all tests
run: swift test

api-breakage:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// swift-tools-version:5.8
// swift-tools-version:5.9
import PackageDescription

let swiftSettings: [SwiftSetting] = [
.enableUpcomingFeature("StrictConcurrency")
.enableUpcomingFeature("StrictConcurrency"),
]

let package = Package(
Expand Down
14 changes: 0 additions & 14 deletions Sources/ConnectionPoolModule/ConnectionPool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -571,20 +571,6 @@ extension PoolConfiguration {
}
}

#if swift(<5.9)
// This should be removed once we support Swift 5.9+ only
extension AsyncStream {
static func makeStream(
of elementType: Element.Type = Element.self,
bufferingPolicy limit: Continuation.BufferingPolicy = .unbounded
) -> (stream: AsyncStream<Element>, continuation: AsyncStream<Element>.Continuation) {
var continuation: AsyncStream<Element>.Continuation!
let stream = AsyncStream<Element>(bufferingPolicy: limit) { continuation = $0 }
return (stream: stream, continuation: continuation!)
}
}
#endif

@usableFromInline
protocol TaskGroupProtocol {
// We need to call this `addTask_` because some Swift versions define this
Expand Down
87 changes: 49 additions & 38 deletions Sources/ConnectionPoolModule/NIOLock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ import WinSDK
import Glibc
#elseif canImport(Musl)
import Musl
#elseif canImport(Bionic)
import Bionic
#elseif canImport(WASILibc)
import WASILibc
#if canImport(wasi_pthread)
import wasi_pthread
#endif
#else
#error("The concurrency NIOLock module was unable to identify your C library.")
#endif
Expand All @@ -37,61 +44,61 @@ typealias LockPrimitive = pthread_mutex_t
#endif

@usableFromInline
enum LockOperations { }
enum LockOperations {}

extension LockOperations {
@inlinable
static func create(_ mutex: UnsafeMutablePointer<LockPrimitive>) {
mutex.assertValidAlignment()

#if os(Windows)
#if os(Windows)
InitializeSRWLock(mutex)
#else
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
var attr = pthread_mutexattr_t()
pthread_mutexattr_init(&attr)
debugOnly {
pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK))
}

let err = pthread_mutex_init(mutex, &attr)
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
#endif
#endif
}

@inlinable
static func destroy(_ mutex: UnsafeMutablePointer<LockPrimitive>) {
mutex.assertValidAlignment()

#if os(Windows)
#if os(Windows)
// SRWLOCK does not need to be free'd
#else
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
let err = pthread_mutex_destroy(mutex)
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
#endif
#endif
}

@inlinable
static func lock(_ mutex: UnsafeMutablePointer<LockPrimitive>) {
mutex.assertValidAlignment()

#if os(Windows)
#if os(Windows)
AcquireSRWLockExclusive(mutex)
#else
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
let err = pthread_mutex_lock(mutex)
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
#endif
#endif
}

@inlinable
static func unlock(_ mutex: UnsafeMutablePointer<LockPrimitive>) {
mutex.assertValidAlignment()

#if os(Windows)
#if os(Windows)
ReleaseSRWLockExclusive(mutex)
#else
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
let err = pthread_mutex_unlock(mutex)
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
#endif
#endif
}
}

Expand Down Expand Up @@ -125,49 +132,52 @@ extension LockOperations {
// See also: https://github.com/apple/swift/pull/40000
@usableFromInline
final class LockStorage<Value>: ManagedBuffer<Value, LockPrimitive> {

@inlinable
static func create(value: Value) -> Self {
let buffer = Self.create(minimumCapacity: 1) { _ in
return value
value
}
let storage = unsafeDowncast(buffer, to: Self.self)

// Intentionally using a force cast here to avoid a miss compiliation in 5.10.
// This is as fast as an unsafeDownCast since ManagedBuffer is inlined and the optimizer
// can eliminate the upcast/downcast pair
let storage = buffer as! Self

storage.withUnsafeMutablePointers { _, lockPtr in
LockOperations.create(lockPtr)
}

return storage
}

@inlinable
func lock() {
self.withUnsafeMutablePointerToElements { lockPtr in
LockOperations.lock(lockPtr)
}
}

@inlinable
func unlock() {
self.withUnsafeMutablePointerToElements { lockPtr in
LockOperations.unlock(lockPtr)
}
}

@inlinable
deinit {
self.withUnsafeMutablePointerToElements { lockPtr in
LockOperations.destroy(lockPtr)
}
}

@inlinable
func withLockPrimitive<T>(_ body: (UnsafeMutablePointer<LockPrimitive>) throws -> T) rethrows -> T {
try self.withUnsafeMutablePointerToElements { lockPtr in
return try body(lockPtr)
try body(lockPtr)
}
}

@inlinable
func withLockedValue<T>(_ mutate: (inout Value) throws -> T) rethrows -> T {
try self.withUnsafeMutablePointers { valuePtr, lockPtr in
Expand All @@ -178,21 +188,18 @@ final class LockStorage<Value>: ManagedBuffer<Value, LockPrimitive> {
}
}

extension LockStorage: @unchecked Sendable { }

/// A threading lock based on `libpthread` instead of `libdispatch`.
///
/// - note: ``NIOLock`` has reference semantics.
/// - Note: ``NIOLock`` has reference semantics.
///
/// This object provides a lock on top of a single `pthread_mutex_t`. This kind
/// of lock is safe to use with `libpthread`-based threading models, such as the
/// one used by NIO. On Windows, the lock is based on the substantially similar
/// `SRWLOCK` type.
@usableFromInline
struct NIOLock {
@usableFromInline
internal let _storage: LockStorage<Void>

/// Create a new lock.
@inlinable
init() {
Expand All @@ -219,7 +226,7 @@ struct NIOLock {

@inlinable
internal func withLockPrimitive<T>(_ body: (UnsafeMutablePointer<LockPrimitive>) throws -> T) rethrows -> T {
return try self._storage.withLockPrimitive(body)
try self._storage.withLockPrimitive(body)
}
}

Expand All @@ -242,12 +249,12 @@ extension NIOLock {
}

@inlinable
func withLockVoid(_ body: () throws -> Void) rethrows -> Void {
func withLockVoid(_ body: () throws -> Void) rethrows {
try self.withLock(body)
}
}

extension NIOLock: Sendable {}
extension NIOLock: @unchecked Sendable {}

extension UnsafeMutablePointer {
@inlinable
Expand All @@ -263,6 +270,10 @@ extension UnsafeMutablePointer {
/// https://forums.swift.org/t/support-debug-only-code/11037 for a discussion.
@inlinable
internal func debugOnly(_ body: () -> Void) {
// FIXME: duplicated with NIO.
assert({ body(); return true }())
assert(
{
body()
return true
}()
)
}
46 changes: 43 additions & 3 deletions Sources/ConnectionPoolModule/NIOLockedValueBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

/// Provides locked access to `Value`.
///
/// - note: ``NIOLockedValueBox`` has reference semantics and holds the `Value`
/// - Note: ``NIOLockedValueBox`` has reference semantics and holds the `Value`
/// alongside a lock behind a reference.
///
/// This is no different than creating a ``Lock`` and protecting all
Expand All @@ -39,8 +39,48 @@ struct NIOLockedValueBox<Value> {
/// Access the `Value`, allowing mutation of it.
@inlinable
func withLockedValue<T>(_ mutate: (inout Value) throws -> T) rethrows -> T {
return try self._storage.withLockedValue(mutate)
try self._storage.withLockedValue(mutate)
}

/// Provides an unsafe view over the lock and its value.
///
/// This can be beneficial when you require fine grained control over the lock in some
/// situations but don't want lose the benefits of ``withLockedValue(_:)`` in others by
/// switching to ``NIOLock``.
var unsafe: Unsafe {
Unsafe(_storage: self._storage)
}

/// Provides an unsafe view over the lock and its value.
struct Unsafe {
@usableFromInline
let _storage: LockStorage<Value>

/// Manually acquire the lock.
@inlinable
func lock() {
self._storage.lock()
}

/// Manually release the lock.
@inlinable
func unlock() {
self._storage.unlock()
}

/// Mutate the value, assuming the lock has been acquired manually.
///
/// - Parameter mutate: A closure with scoped access to the value.
/// - Returns: The result of the `mutate` closure.
@inlinable
func withValueAssumingLockIsAcquired<Result>(
_ mutate: (_ value: inout Value) throws -> Result
) rethrows -> Result {
try self._storage.withUnsafeMutablePointerToHeader { value in
try mutate(&value.pointee)
}
}
}
}

extension NIOLockedValueBox: Sendable where Value: Sendable {}
extension NIOLockedValueBox: @unchecked Sendable where Value: Sendable {}
4 changes: 3 additions & 1 deletion Sources/ConnectionPoolModule/PoolStateMachine.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#if canImport(Darwin)
import Darwin
#else
#elseif canImport(Glibc)
import Glibc
#elseif canImport(Musl)
import Musl
#endif

@usableFromInline
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,22 @@ extension PostgresConnection {
/// - Parameters:
/// - channel: The `NIOCore/Channel` to use. The channel must already be active and connected to an
/// endpoint (i.e. `NIOCore/Channel/isActive` must be `true`).
/// - tls: The TLS mode to use. Defaults to ``TLS-swift.struct/disable``.
/// - tls: The TLS mode to use.
public init(establishedChannel channel: Channel, tls: PostgresConnection.Configuration.TLS, username: String, password: String?, database: String?) {
self.init(endpointInfo: .configureChannel(channel), tls: tls, username: username, password: password, database: database)
}

/// Create a configuration for establishing a connection to a Postgres server over a preestablished
/// `NIOCore/Channel`.
///
/// This is provided for calling code which wants to manage the underlying connection transport on its
/// own, such as when tunneling a connection through SSH.
///
/// - Parameters:
/// - channel: The `NIOCore/Channel` to use. The channel must already be active and connected to an
/// endpoint (i.e. `NIOCore/Channel/isActive` must be `true`).
public init(establishedChannel channel: Channel, username: String, password: String?, database: String?) {
self.init(endpointInfo: .configureChannel(channel), tls: .disable, username: username, password: password, database: database)
self.init(establishedChannel: channel, tls: .disable, username: username, password: password, database: database)
}

// MARK: - Implementation details
Expand Down
Loading

0 comments on commit 7937b2f

Please sign in to comment.