Skip to content

Commit a931e69

Browse files
committed
adoption of sendable
motivation: adopt to sendable requirments in swift 5.6 changes: * define sendable shims for protocols and structs that may be used in async context * adjust tests * add a test to make sure no warning are emittted
1 parent 4d0bba4 commit a931e69

File tree

8 files changed

+113
-18
lines changed

8 files changed

+113
-18
lines changed

Sources/AWSLambdaRuntimeCore/LambdaContext.swift

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,15 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
#if compiler(>=5.6)
16+
@preconcurrency import Dispatch
17+
@preconcurrency import Logging
18+
@preconcurrency import NIOCore
19+
#else
1520
import Dispatch
1621
import Logging
1722
import NIOCore
23+
#endif
1824

1925
// MARK: - InitializationContext
2026

@@ -23,7 +29,7 @@ extension Lambda {
2329
/// The Lambda runtime generates and passes the `InitializationContext` to the Handlers
2430
/// ``ByteBufferLambdaHandler/makeHandler(context:)`` or ``LambdaHandler/init(context:)``
2531
/// as an argument.
26-
public struct InitializationContext {
32+
public struct InitializationContext: _AWSLambdaSendable {
2733
/// `Logger` to log with
2834
///
2935
/// - note: The `LogLevel` can be configured using the `LOG_LEVEL` environment variable.
@@ -67,17 +73,17 @@ extension Lambda {
6773

6874
/// Lambda runtime context.
6975
/// The Lambda runtime generates and passes the `Context` to the Lambda handler as an argument.
70-
public struct LambdaContext: CustomDebugStringConvertible {
71-
final class _Storage {
72-
var requestID: String
73-
var traceID: String
74-
var invokedFunctionARN: String
75-
var deadline: DispatchWallTime
76-
var cognitoIdentity: String?
77-
var clientContext: String?
78-
var logger: Logger
79-
var eventLoop: EventLoop
80-
var allocator: ByteBufferAllocator
76+
public struct LambdaContext: CustomDebugStringConvertible, _AWSLambdaSendable {
77+
final class _Storage: _AWSLambdaSendable {
78+
let requestID: String
79+
let traceID: String
80+
let invokedFunctionARN: String
81+
let deadline: DispatchWallTime
82+
let cognitoIdentity: String?
83+
let clientContext: String?
84+
let logger: Logger
85+
let eventLoop: EventLoop
86+
let allocator: ByteBufferAllocator
8187

8288
init(
8389
requestID: String,

Sources/AWSLambdaRuntimeCore/LambdaHandler.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import NIOCore
2626
/// level protocols ``EventLoopLambdaHandler`` and
2727
/// ``ByteBufferLambdaHandler``.
2828
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
29-
public protocol LambdaHandler: EventLoopLambdaHandler {
29+
public protocol LambdaHandler: EventLoopLambdaHandler where Event: _AWSLambdaSendable {
3030
/// The Lambda initialization method
3131
/// Use this method to initialize resources that will be used in every request.
3232
///
@@ -157,7 +157,7 @@ extension EventLoopLambdaHandler where Output == Void {
157157
/// - note: This is a low level protocol designed to power the higher level ``EventLoopLambdaHandler`` and
158158
/// ``LambdaHandler`` based APIs.
159159
/// Most users are not expected to use this protocol.
160-
public protocol ByteBufferLambdaHandler {
160+
public protocol ByteBufferLambdaHandler: _ByteBufferLambdaHandlerSendable {
161161
/// Create your Lambda handler for the runtime.
162162
///
163163
/// Use this to initialize all your resources that you want to cache between invocations. This could be database

Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,8 @@ public final class LambdaRuntime<Handler: ByteBufferLambdaHandler> {
189189
}
190190
}
191191
}
192+
193+
// TODO: ideally this would not be @unchecked Sendable, but Sendable checks do not understand locks
194+
#if compiler(>=5.5) && canImport(_Concurrency)
195+
extension LambdaRuntime: @unchecked Sendable {}
196+
#endif

Sources/AWSLambdaRuntimeCore/LambdaRuntimeClient.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,13 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
import Logging
16+
#if compiler(>=5.6)
17+
@preconcurrency import NIOCore
18+
@preconcurrency import NIOHTTP1
19+
#else
1620
import NIOCore
1721
import NIOHTTP1
22+
#endif
1823

1924
/// An HTTP based client for AWS Runtime Engine. This encapsulates the RESTful methods exposed by the Runtime Engine:
2025
/// * /runtime/invocation/next
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2022 Apple Inc. and the SwiftAWSLambdaRuntime 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 SwiftAWSLambdaRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
// Sendable bridging types
16+
17+
#if compiler(>=5.6)
18+
@preconcurrency public protocol _ByteBufferLambdaHandlerSendable: Sendable {}
19+
#else
20+
public protocol _ByteBufferLambdaHandlerSendable {}
21+
#endif
22+
23+
#if compiler(>=5.6)
24+
public typealias _AWSLambdaSendable = Sendable
25+
#else
26+
public typealias _AWSLambdaSendable = Any
27+
#endif

Sources/AWSLambdaRuntimeCore/Terminator.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import NIOCore
1818
/// Lambda terminator.
1919
/// Utility to manage the lambda shutdown sequence.
2020
public final class LambdaTerminator {
21-
private typealias Handler = (EventLoop) -> EventLoopFuture<Void>
21+
fileprivate typealias Handler = (EventLoop) -> EventLoopFuture<Void>
2222

2323
private var storage: Storage
2424

@@ -99,7 +99,7 @@ extension LambdaTerminator {
9999
}
100100

101101
extension LambdaTerminator {
102-
private final class Storage {
102+
fileprivate final class Storage {
103103
private let lock: Lock
104104
private var index: [RegistrationKey]
105105
private var map: [RegistrationKey: (name: String, handler: Handler)]
@@ -137,3 +137,9 @@ extension LambdaTerminator {
137137
let underlying: [Error]
138138
}
139139
}
140+
141+
// TODO: ideally this would not be @unchecked Sendable, but Sendable checks do not understand locks
142+
#if compiler(>=5.5) && canImport(_Concurrency)
143+
extension LambdaTerminator: @unchecked Sendable {}
144+
extension LambdaTerminator.Storage: @unchecked Sendable {}
145+
#endif

Tests/AWSLambdaRuntimeCoreTests/LambdaTest.swift

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
@testable import AWSLambdaRuntimeCore
16+
#if compiler(>=5.6)
17+
@preconcurrency import Logging
18+
@preconcurrency import NIOPosix
19+
#else
1620
import Logging
17-
import NIOCore
1821
import NIOPosix
22+
#endif
23+
import NIOCore
1924
import XCTest
2025

2126
class LambdaTest: XCTestCase {
@@ -250,6 +255,47 @@ class LambdaTest: XCTestCase {
250255
XCTAssertLessThanOrEqual(context.getRemainingTime(), .seconds(1))
251256
XCTAssertGreaterThan(context.getRemainingTime(), .milliseconds(800))
252257
}
258+
259+
#if compiler(>=5.6)
260+
func testSendable() async throws {
261+
struct Handler: EventLoopLambdaHandler {
262+
typealias Event = String
263+
typealias Output = String
264+
265+
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Handler> {
266+
context.eventLoop.makeSucceededFuture(Handler())
267+
}
268+
269+
func handle(_ event: String, context: LambdaContext) -> EventLoopFuture<String> {
270+
context.eventLoop.makeSucceededFuture("hello")
271+
}
272+
}
273+
274+
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
275+
defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) }
276+
277+
let server = try MockLambdaServer(behavior: Behavior()).start().wait()
278+
defer { XCTAssertNoThrow(try server.stop().wait()) }
279+
280+
let logger = Logger(label: "TestLogger")
281+
let configuration = Lambda.Configuration(runtimeEngine: .init(requestTimeout: .milliseconds(100)))
282+
283+
let handler1 = Handler()
284+
let task = Task.detached {
285+
print(configuration.description)
286+
logger.info("hello")
287+
let runner = Lambda.Runner(eventLoop: eventLoopGroup.next(), configuration: configuration)
288+
289+
try runner.run(logger: logger, handler: handler1).wait()
290+
291+
try runner.initialize(logger: logger, terminator: LambdaTerminator(), handlerType: Handler.self).flatMap { handler2 in
292+
runner.run(logger: logger, handler: handler2)
293+
}.wait()
294+
}
295+
296+
try await task.value
297+
}
298+
#endif
253299
}
254300

255301
private struct Behavior: LambdaServerBehavior {

docker/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ RUN echo 'export PATH="$HOME/.tools:$PATH"' >> $HOME/.profile
1515

1616
# swiftformat (until part of the toolchain)
1717

18-
ARG swiftformat_version=0.47.3
18+
ARG swiftformat_version=0.49.6
1919
RUN git clone --branch $swiftformat_version --depth 1 https://github.com/nicklockwood/SwiftFormat $HOME/.tools/swift-format
2020
RUN cd $HOME/.tools/swift-format && swift build -c release
2121
RUN ln -s $HOME/.tools/swift-format/.build/release/swiftformat $HOME/.tools/swiftformat

0 commit comments

Comments
 (0)