Skip to content

Commit fb16228

Browse files
committed
remove @unchecked sendable on LambdaRuntime
1 parent de38324 commit fb16228

File tree

5 files changed

+62
-69
lines changed

5 files changed

+62
-69
lines changed

Examples/APIGateway+LambdaAuthorizer/Sources/AuthorizerLambda/main.swift

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -22,58 +22,57 @@ import AWSLambdaRuntime
2222
// This code is shown for the example only and is not used in this demo.
2323
// This code doesn't perform any type of token validation. It should be used as a reference only.
2424
let policyAuthorizerHandler:
25-
(APIGatewayLambdaAuthorizerRequest, LambdaContext) async throws -> APIGatewayLambdaAuthorizerPolicyResponse = {
26-
(request: APIGatewayLambdaAuthorizerRequest, context: LambdaContext) in
25+
@Sendable (APIGatewayLambdaAuthorizerRequest, LambdaContext) async throws ->
26+
APIGatewayLambdaAuthorizerPolicyResponse = {
27+
(request: APIGatewayLambdaAuthorizerRequest, context: LambdaContext) in
2728

28-
context.logger.debug("+++ Policy Authorizer called +++")
29+
context.logger.debug("+++ Policy Authorizer called +++")
2930

30-
// typically, this function will check the validity of the incoming token received in the request
31+
// typically, this function will check the validity of the incoming token received in the request
3132

32-
// then it creates and returns a response
33-
return APIGatewayLambdaAuthorizerPolicyResponse(
34-
principalId: "John Appleseed",
33+
// then it creates and returns a response
34+
return APIGatewayLambdaAuthorizerPolicyResponse(
35+
principalId: "John Appleseed",
3536

36-
// this policy allows the caller to invoke any API Gateway endpoint
37-
policyDocument: .init(statement: [
38-
.init(
39-
action: "execute-api:Invoke",
40-
effect: .allow,
41-
resource: "*"
42-
)
37+
// this policy allows the caller to invoke any API Gateway endpoint
38+
policyDocument: .init(statement: [
39+
.init(
40+
action: "execute-api:Invoke",
41+
effect: .allow,
42+
resource: "*"
43+
)
4344

44-
]),
45+
]),
4546

46-
// this is additional context we want to return to the caller
47-
context: [
48-
"abc1": "xyz1",
49-
"abc2": "xyz2",
50-
]
51-
)
52-
}
47+
// this is additional context we want to return to the caller
48+
context: [
49+
"abc1": "xyz1",
50+
"abc2": "xyz2",
51+
]
52+
)
53+
}
54+
// let runtime = LambdaRuntime(body: policyAuthorizerHandler)
5355

5456
//
5557
// This is an example of a simple authorizer that always authorizes the request.
5658
// A simple authorizer returns a yes/no decision and optional context key-value pairs
5759
//
5860
// This code doesn't perform any type of token validation. It should be used as a reference only.
59-
let simpleAuthorizerHandler:
60-
(APIGatewayLambdaAuthorizerRequest, LambdaContext) async throws -> APIGatewayLambdaAuthorizerSimpleResponse = {
61-
(_: APIGatewayLambdaAuthorizerRequest, context: LambdaContext) in
61+
let runtime = LambdaRuntime {
62+
(_: APIGatewayLambdaAuthorizerRequest, context: LambdaContext) -> APIGatewayLambdaAuthorizerSimpleResponse in
6263

63-
context.logger.debug("+++ Simple Authorizer called +++")
64+
context.logger.debug("+++ Simple Authorizer called +++")
6465

65-
// typically, this function will check the validity of the incoming token received in the request
66+
// typically, this function will check the validity of the incoming token received in the request
6667

67-
return APIGatewayLambdaAuthorizerSimpleResponse(
68-
// this is the authorization decision: yes or no
69-
isAuthorized: true,
68+
return APIGatewayLambdaAuthorizerSimpleResponse(
69+
// this is the authorization decision: yes or no
70+
isAuthorized: true,
7071

71-
// this is additional context we want to return to the caller
72-
context: ["abc1": "xyz1"]
73-
)
74-
}
72+
// this is additional context we want to return to the caller
73+
context: ["abc1": "xyz1"]
74+
)
75+
}
7576

76-
// create the runtime and start polling for new events.
77-
// in this demo we use the simple authorizer handler
78-
let runtime = LambdaRuntime(body: simpleAuthorizerHandler)
77+
// start polling for new events.
7978
try await runtime.run()

Sources/AWSLambdaRuntime/FoundationSupport/Lambda+JSON.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ extension LambdaRuntime {
9595
decoder: JSONDecoder = JSONDecoder(),
9696
encoder: JSONEncoder = JSONEncoder(),
9797
logger: Logger = Logger(label: "LambdaRuntime"),
98-
body: sending @escaping (Event, LambdaContext) async throws -> Output
98+
body: @Sendable @escaping (Event, LambdaContext) async throws -> Output
9999
)
100100
where
101101
Handler == LambdaCodableAdapter<
@@ -122,7 +122,7 @@ extension LambdaRuntime {
122122
public convenience init<Event: Decodable>(
123123
decoder: JSONDecoder = JSONDecoder(),
124124
logger: Logger = Logger(label: "LambdaRuntime"),
125-
body: sending @escaping (Event, LambdaContext) async throws -> Void
125+
body: @Sendable @escaping (Event, LambdaContext) async throws -> Void
126126
)
127127
where
128128
Handler == LambdaCodableAdapter<

Sources/AWSLambdaRuntime/Lambda+Codable.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import NIOCore
1616

1717
/// The protocol a decoder must conform to so that it can be used with ``LambdaCodableAdapter`` to decode incoming
1818
/// `ByteBuffer` events.
19-
public protocol LambdaEventDecoder {
19+
public protocol LambdaEventDecoder: Sendable {
2020
/// Decode the `ByteBuffer` representing the received event into the generic `Event` type
2121
/// the handler will receive.
2222
/// - Parameters:
@@ -28,7 +28,7 @@ public protocol LambdaEventDecoder {
2828

2929
/// The protocol an encoder must conform to so that it can be used with ``LambdaCodableAdapter`` to encode the generic
3030
/// ``LambdaOutputEncoder/Output`` object into a `ByteBuffer`.
31-
public protocol LambdaOutputEncoder {
31+
public protocol LambdaOutputEncoder: Sendable {
3232
associatedtype Output
3333

3434
/// Encode the generic type `Output` the handler has returned into a `ByteBuffer`.
@@ -52,7 +52,7 @@ public struct LambdaHandlerAdapter<
5252
Event: Decodable,
5353
Output,
5454
Handler: LambdaHandler
55-
>: LambdaWithBackgroundProcessingHandler where Handler.Event == Event, Handler.Output == Output {
55+
>: LambdaWithBackgroundProcessingHandler where Handler.Event == Event, Handler.Output == Output, Handler: Sendable {
5656
@usableFromInline let handler: Handler
5757

5858
/// Initializes an instance given a concrete handler.
@@ -86,7 +86,15 @@ public struct LambdaCodableAdapter<
8686
Output,
8787
Decoder: LambdaEventDecoder,
8888
Encoder: LambdaOutputEncoder
89-
>: StreamingLambdaHandler where Handler.Event == Event, Handler.Output == Output, Encoder.Output == Output {
89+
>: StreamingLambdaHandler
90+
where
91+
Handler.Event == Event,
92+
Handler.Output == Output,
93+
Encoder.Output == Output,
94+
Handler: Sendable,
95+
Decoder: Sendable,
96+
Encoder: Sendable
97+
{
9098
@usableFromInline let handler: Handler
9199
@usableFromInline let encoder: Encoder
92100
@usableFromInline let decoder: Decoder

Sources/AWSLambdaRuntime/LambdaHandlers.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import NIOCore
2121
/// Background work can also be executed after returning the response. After closing the response stream by calling
2222
/// ``LambdaResponseStreamWriter/finish()`` or ``LambdaResponseStreamWriter/writeAndFinish(_:)``,
2323
/// the ``handle(_:responseWriter:context:)`` function is free to execute any background work.
24-
public protocol StreamingLambdaHandler: _Lambda_SendableMetatype {
24+
public protocol StreamingLambdaHandler: Sendable, _Lambda_SendableMetatype {
2525
/// The handler function -- implement the business logic of the Lambda function here.
2626
/// - Parameters:
2727
/// - event: The invocation's input data.
@@ -65,7 +65,7 @@ public protocol LambdaResponseStreamWriter {
6565
///
6666
/// - note: This handler protocol does not support response streaming because the output has to be encoded prior to it being sent, e.g. it is not possible to encode a partial/incomplete JSON string.
6767
/// This protocol also does not support the execution of background work after the response has been returned -- the ``LambdaWithBackgroundProcessingHandler`` protocol caters for such use-cases.
68-
public protocol LambdaHandler {
68+
public protocol LambdaHandler: Sendable {
6969
/// Generic input type.
7070
/// The body of the request sent to Lambda will be decoded into this type for the handler to consume.
7171
associatedtype Event
@@ -87,7 +87,7 @@ public protocol LambdaHandler {
8787
/// ``LambdaResponseWriter``that is passed in as an argument, meaning that the
8888
/// ``LambdaWithBackgroundProcessingHandler/handle(_:outputWriter:context:)`` function is then
8989
/// free to implement any background work after the result has been sent to the AWS Lambda control plane.
90-
public protocol LambdaWithBackgroundProcessingHandler {
90+
public protocol LambdaWithBackgroundProcessingHandler: Sendable {
9191
/// Generic input type.
9292
/// The body of the request sent to Lambda will be decoded into this type for the handler to consume.
9393
associatedtype Event
@@ -151,17 +151,17 @@ public struct StreamingClosureHandler: StreamingLambdaHandler {
151151
/// A ``LambdaHandler`` conforming handler object that can be constructed with a closure.
152152
/// Allows for a handler to be defined in a clean manner, leveraging Swift's trailing closure syntax.
153153
public struct ClosureHandler<Event: Decodable, Output>: LambdaHandler {
154-
let body: (Event, LambdaContext) async throws -> Output
154+
let body: @Sendable (Event, LambdaContext) async throws -> Output
155155

156156
/// Initialize with a closure handler over generic `Input` and `Output` types.
157157
/// - Parameter body: The handler function written as a closure.
158-
public init(body: sending @escaping (Event, LambdaContext) async throws -> Output) where Output: Encodable {
158+
public init(body: @Sendable @escaping (Event, LambdaContext) async throws -> Output) where Output: Encodable {
159159
self.body = body
160160
}
161161

162162
/// Initialize with a closure handler over a generic `Input` type, and a `Void` `Output`.
163163
/// - Parameter body: The handler function written as a closure.
164-
public init(body: @escaping (Event, LambdaContext) async throws -> Void) where Output == Void {
164+
public init(body: @Sendable @escaping (Event, LambdaContext) async throws -> Void) where Output == Void {
165165
self.body = body
166166
}
167167

@@ -202,7 +202,7 @@ extension LambdaRuntime {
202202
encoder: sending Encoder,
203203
decoder: sending Decoder,
204204
logger: Logger = Logger(label: "LambdaRuntime"),
205-
body: sending @escaping (Event, LambdaContext) async throws -> Output
205+
body: @Sendable @escaping (Event, LambdaContext) async throws -> Output
206206
)
207207
where
208208
Handler == LambdaCodableAdapter<
@@ -232,7 +232,7 @@ extension LambdaRuntime {
232232
public convenience init<Event: Decodable, Decoder: LambdaEventDecoder>(
233233
decoder: sending Decoder,
234234
logger: Logger = Logger(label: "LambdaRuntime"),
235-
body: sending @escaping (Event, LambdaContext) async throws -> Void
235+
body: @Sendable @escaping (Event, LambdaContext) async throws -> Void
236236
)
237237
where
238238
Handler == LambdaCodableAdapter<

Sources/AWSLambdaRuntime/LambdaRuntime.swift

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,9 @@ import FoundationEssentials
2222
import Foundation
2323
#endif
2424

25-
// We need `@unchecked` Sendable here, as `NIOLockedValueBox` does not understand `sending` today.
26-
// We don't want to use `NIOLockedValueBox` here anyway. We would love to use Mutex here, but this
27-
// sadly crashes the compiler today.
28-
public final class LambdaRuntime<Handler>: @unchecked Sendable where Handler: StreamingLambdaHandler {
29-
// TODO: We want to change this to Mutex as soon as this doesn't crash the Swift compiler on Linux anymore
25+
public final class LambdaRuntime<Handler>: Sendable where Handler: StreamingLambdaHandler {
3026
@usableFromInline
31-
let handlerMutex: NIOLockedValueBox<Handler?>
27+
let handler: Handler
3228
@usableFromInline
3329
let logger: Logger
3430
@usableFromInline
@@ -39,7 +35,7 @@ public final class LambdaRuntime<Handler>: @unchecked Sendable where Handler: St
3935
eventLoop: EventLoop = Lambda.defaultEventLoop,
4036
logger: Logger = Logger(label: "LambdaRuntime")
4137
) {
42-
self.handlerMutex = NIOLockedValueBox(handler)
38+
self.handler = handler
4339
self.eventLoop = eventLoop
4440

4541
// by setting the log level here, we understand it can not be changed dynamically at runtime
@@ -64,16 +60,6 @@ public final class LambdaRuntime<Handler>: @unchecked Sendable where Handler: St
6460

6561
@inlinable
6662
internal func _run() async throws {
67-
let handler = self.handlerMutex.withLockedValue { handler in
68-
let result = handler
69-
handler = nil
70-
return result
71-
}
72-
73-
guard let handler else {
74-
throw LambdaRuntimeError(code: .runtimeCanOnlyBeStartedOnce)
75-
}
76-
7763
// are we running inside an AWS Lambda runtime environment ?
7864
// AWS_LAMBDA_RUNTIME_API is set when running on Lambda
7965
// https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html
@@ -110,7 +96,7 @@ public final class LambdaRuntime<Handler>: @unchecked Sendable where Handler: St
11096
) { runtimeClient in
11197
try await Lambda.runLoop(
11298
runtimeClient: runtimeClient,
113-
handler: handler,
99+
handler: self.handler,
114100
logger: self.logger
115101
)
116102
}

0 commit comments

Comments
 (0)