Skip to content

Commit

Permalink
adoption of sendable
Browse files Browse the repository at this point in the history
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
  • Loading branch information
tomerd committed Mar 18, 2022
1 parent 51c27f9 commit 2b236e4
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 20 deletions.
32 changes: 19 additions & 13 deletions Sources/AWSLambdaRuntimeCore/LambdaContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@
//
//===----------------------------------------------------------------------===//

#if compiler(>=5.6)
@preconcurrency import Dispatch
@preconcurrency import Logging
@preconcurrency import NIOCore
#else
import Dispatch
import Logging
import NIOCore
#endif

// MARK: - InitializationContext

Expand All @@ -23,7 +29,7 @@ extension Lambda {
/// The Lambda runtime generates and passes the `InitializationContext` to the Handlers
/// ``ByteBufferLambdaHandler/makeHandler(context:)`` or ``LambdaHandler/init(context:)``
/// as an argument.
public struct InitializationContext {
public struct InitializationContext: _AWSLambdaSendable {
/// `Logger` to log with
///
/// - note: The `LogLevel` can be configured using the `LOG_LEVEL` environment variable.
Expand Down Expand Up @@ -62,17 +68,17 @@ extension Lambda {

/// Lambda runtime context.
/// The Lambda runtime generates and passes the `Context` to the Lambda handler as an argument.
public struct LambdaContext: CustomDebugStringConvertible {
final class _Storage {
var requestID: String
var traceID: String
var invokedFunctionARN: String
var deadline: DispatchWallTime
var cognitoIdentity: String?
var clientContext: String?
var logger: Logger
var eventLoop: EventLoop
var allocator: ByteBufferAllocator
public struct LambdaContext: CustomDebugStringConvertible, _AWSLambdaSendable {
final class _Storage: _AWSLambdaSendable {
let requestID: String
let traceID: String
let invokedFunctionARN: String
let deadline: DispatchWallTime
let cognitoIdentity: String?
let clientContext: String?
let logger: Logger
let eventLoop: EventLoop
let allocator: ByteBufferAllocator

init(
requestID: String,
Expand Down Expand Up @@ -211,7 +217,7 @@ public struct LambdaContext: CustomDebugStringConvertible {
extension Lambda {
/// Lambda runtime shutdown context.
/// The Lambda runtime generates and passes the `ShutdownContext` to the Lambda handler as an argument.
public final class ShutdownContext {
public final class ShutdownContext: _AWSLambdaSendable {
/// `Logger` to log with
///
/// - note: The `LogLevel` can be configured using the `LOG_LEVEL` environment variable.
Expand Down
4 changes: 2 additions & 2 deletions Sources/AWSLambdaRuntimeCore/LambdaHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import NIOCore
/// level protocols ``EventLoopLambdaHandler`` and
/// ``ByteBufferLambdaHandler``.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public protocol LambdaHandler: EventLoopLambdaHandler {
public protocol LambdaHandler: EventLoopLambdaHandler where Event: _AWSLambdaSendable {
/// The Lambda initialization method
/// Use this method to initialize resources that will be used in every request.
///
Expand Down Expand Up @@ -157,7 +157,7 @@ extension EventLoopLambdaHandler where Output == Void {
/// - note: This is a low level protocol designed to power the higher level ``EventLoopLambdaHandler`` and
/// ``LambdaHandler`` based APIs.
/// Most users are not expected to use this protocol.
public protocol ByteBufferLambdaHandler {
public protocol ByteBufferLambdaHandler: _ByteBufferLambdaHandlerSendable {
/// Create your Lambda handler for the runtime.
///
/// Use this to initialize all your resources that you want to cache between invocations. This could be database
Expand Down
2 changes: 1 addition & 1 deletion Sources/AWSLambdaRuntimeCore/LambdaRunner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ extension Lambda {
func initialize<Handler: ByteBufferLambdaHandler>(logger: Logger, handlerType: Handler.Type) -> EventLoopFuture<Handler> {
logger.debug("initializing lambda")
// 1. create the handler from the factory
// 2. report initialization error if one occured
// 2. report initialization error if one occurred
let context = InitializationContext(logger: logger,
eventLoop: self.eventLoop,
allocator: self.allocator)
Expand Down
5 changes: 5 additions & 0 deletions Sources/AWSLambdaRuntimeCore/LambdaRuntimeClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@
//===----------------------------------------------------------------------===//

import Logging
#if compiler(>=5.6)
@preconcurrency import NIOCore
@preconcurrency import NIOHTTP1
#else
import NIOCore
import NIOHTTP1
#endif

/// An HTTP based client for AWS Runtime Engine. This encapsulates the RESTful methods exposed by the Runtime Engine:
/// * /runtime/invocation/next
Expand Down
27 changes: 27 additions & 0 deletions Sources/AWSLambdaRuntimeCore/Sendable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2022 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

// Sendable bridging types

#if compiler(>=5.6)
@preconcurrency public protocol _ByteBufferLambdaHandlerSendable: Sendable {}
#else
public protocol _ByteBufferLambdaHandlerSendable {}
#endif

#if compiler(>=5.6)
public typealias _AWSLambdaSendable = Sendable
#else
public typealias _AWSLambdaSendable = Any
#endif
3 changes: 1 addition & 2 deletions Sources/AWSLambdaTesting/Lambda+Testing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ extension Lambda {
public init(requestID: String = "\(DispatchTime.now().uptimeNanoseconds)",
traceID: String = "Root=\(DispatchTime.now().uptimeNanoseconds);Parent=\(DispatchTime.now().uptimeNanoseconds);Sampled=1",
invokedFunctionARN: String = "arn:aws:lambda:us-west-1:\(DispatchTime.now().uptimeNanoseconds):function:custom-runtime",
timeout: DispatchTimeInterval = .seconds(5))
{
timeout: DispatchTimeInterval = .seconds(5)) {
self.requestID = requestID
self.traceID = traceID
self.invokedFunctionARN = invokedFunctionARN
Expand Down
48 changes: 47 additions & 1 deletion Tests/AWSLambdaRuntimeCoreTests/LambdaTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@
//===----------------------------------------------------------------------===//

@testable import AWSLambdaRuntimeCore
#if compiler(>=5.6)
@preconcurrency import Logging
@preconcurrency import NIOPosix
#else
import Logging
import NIOCore
import NIOPosix
#endif
import NIOCore
import XCTest

class LambdaTest: XCTestCase {
Expand Down Expand Up @@ -250,6 +255,47 @@ class LambdaTest: XCTestCase {
XCTAssertLessThanOrEqual(context.getRemainingTime(), .seconds(1))
XCTAssertGreaterThan(context.getRemainingTime(), .milliseconds(800))
}

#if compiler(>=5.6)
func testSendable() async throws {
struct Handler: EventLoopLambdaHandler {
typealias Event = String
typealias Output = String

static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Handler> {
context.eventLoop.makeSucceededFuture(Handler())
}

func handle(_ event: String, context: LambdaContext) -> EventLoopFuture<String> {
context.eventLoop.makeSucceededFuture("hello")
}
}

let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) }

let server = try MockLambdaServer(behavior: Behavior()).start().wait()
defer { XCTAssertNoThrow(try server.stop().wait()) }

let logger = Logger(label: "TestLogger")
let configuration = Lambda.Configuration(runtimeEngine: .init(requestTimeout: .milliseconds(100)))

let handler1 = Handler()
let task = Task.detached {
print(configuration.description)
logger.info("hello")
let runner = Lambda.Runner(eventLoop: eventLoopGroup.next(), configuration: configuration)

try runner.run(logger: logger, handler: handler1).wait()

try runner.initialize(logger: logger, handlerType: Handler.self).flatMap { handler2 in
runner.run(logger: logger, handler: handler2)
}.wait()
}

try await task.value
}
#endif
}

private struct Behavior: LambdaServerBehavior {
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ RUN echo 'export PATH="$HOME/.tools:$PATH"' >> $HOME/.profile

# swiftformat (until part of the toolchain)

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

0 comments on commit 2b236e4

Please sign in to comment.