Skip to content

Commit

Permalink
Make HBFluent Sendable
Browse files Browse the repository at this point in the history
Wrap migrations in a MainActorBox
Wrap Databases in a UnsafeTransfer
  • Loading branch information
adam-fowler committed Dec 26, 2023
1 parent 30ef912 commit 1489205
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 10 deletions.
20 changes: 10 additions & 10 deletions Sources/HummingbirdFluent/Fluent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,28 @@ import ServiceLifecycle

@MainActor
public struct MainActorBox<Value>: Sendable {
let value: Value
public let value: Value
}

extension Databases: @unchecked Sendable {}
extension DatabaseID: @unchecked Sendable {}

/// Manage fluent databases and migrations
///
/// You can either create this separate from `HBApplication` or add it to your application
/// using `HBApplication.addFluent`.
public struct HBFluent: Sendable, Service {
/// Databases attached
public let databases: Databases
/// List of migrations
let _migrations: MainActorBox<Migrations>
/// Event loop group
public let eventLoopGroup: EventLoopGroup
/// Logger
public let logger: Logger

/// List of migrations. Only accessible from the main actor
@MainActor
public var migrations: Migrations { self._migrations.value }
/// Databases attached
public var databases: Databases { self._databases.wrappedValue }

private let _databases: UnsafeTransfer<Databases>
private let _migrations: MainActorBox<Migrations>

/// Initialize HBFluent
/// - Parameters:
Expand All @@ -52,15 +52,15 @@ public struct HBFluent: Sendable, Service {
logger: Logger
) {
let eventLoopGroup = eventLoopGroupProvider.eventLoopGroup
self.databases = Databases(threadPool: threadPool, on: eventLoopGroup)
self._databases = .init(Databases(threadPool: threadPool, on: eventLoopGroup))
self._migrations = .init(value: .init())
self.eventLoopGroup = eventLoopGroup
self.logger = logger
}

public func run() async throws {
await GracefulShutdownWaiter().wait()
self.databases.shutdown()
self._databases.wrappedValue.shutdown()
}

/// fluent migrator
Expand Down Expand Up @@ -96,7 +96,7 @@ public struct HBFluent: Sendable, Service {
/// - pageSizeLimit: Set page size limit to avoid server overload
/// - Returns: Database connection
public func db(_ id: DatabaseID? = nil, history: QueryHistory? = nil, pageSizeLimit: Int? = nil) -> Database {
self.databases
self._databases.wrappedValue
.database(
id,
logger: self.logger,
Expand Down
70 changes: 70 additions & 0 deletions Sources/HummingbirdFluent/UnsafeTransfer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Hummingbird server framework project
//
// Copyright (c) 2021-2023 the Hummingbird authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2021-2022 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
//
//===----------------------------------------------------------------------===//

@available(*, deprecated, renamed: "Sendable")
public typealias NIOSendable = Swift.Sendable

@preconcurrency public protocol _NIOPreconcurrencySendable: Sendable {}

@available(*, deprecated, message: "use @preconcurrency and Sendable directly")
public typealias NIOPreconcurrencySendable = _NIOPreconcurrencySendable

/// ``UnsafeTransfer`` can be used to make non-`Sendable` values `Sendable`.
/// As the name implies, the usage of this is unsafe because it disables the sendable checking of the compiler.
/// It can be used similar to `@unsafe Sendable` but for values instead of types.
@usableFromInline
struct UnsafeTransfer<Wrapped> {
@usableFromInline
var wrappedValue: Wrapped

@inlinable
init(_ wrappedValue: Wrapped) {
self.wrappedValue = wrappedValue
}
}

extension UnsafeTransfer: @unchecked Sendable {}

extension UnsafeTransfer: Equatable where Wrapped: Equatable {}
extension UnsafeTransfer: Hashable where Wrapped: Hashable {}

/// ``UnsafeMutableTransferBox`` can be used to make non-`Sendable` values `Sendable` and mutable.
/// It can be used to capture local mutable values in a `@Sendable` closure and mutate them from within the closure.
/// As the name implies, the usage of this is unsafe because it disables the sendable checking of the compiler and does not add any synchronisation.
@usableFromInline
final class UnsafeMutableTransferBox<Wrapped> {
@usableFromInline
var wrappedValue: Wrapped

@inlinable
init(_ wrappedValue: Wrapped) {
self.wrappedValue = wrappedValue
}
}

extension UnsafeMutableTransferBox: @unchecked Sendable {}

0 comments on commit 1489205

Please sign in to comment.