1+ //===----------------------------------------------------------------------===//
2+ //
3+ // This source file is part of the SwiftAWSLambdaRuntime open source project
4+ //
5+ // Copyright (c) 2025 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+ #if LocalServerSupport
16+ import DequeModule
17+ import Synchronization
18+
19+ @available ( LambdaSwift 2 . 0 , * )
20+ extension LambdaHTTPServer {
21+ /// A shared data structure to store the current invocation or response requests and the continuation objects.
22+ /// This data structure is shared between instances of the HTTPHandler
23+ /// (one instance to serve requests from the Lambda function and one instance to serve requests from the client invoking the lambda function).
24+ internal final class Pool < T> : AsyncSequence , AsyncIteratorProtocol , Sendable where T: Sendable {
25+ private let poolName : String
26+ internal init ( name: String = " Pool " ) { self . poolName = name }
27+
28+ typealias Element = T
29+
30+ enum State : ~ Copyable {
31+ case buffer( Deque < T > )
32+ case continuation( CheckedContinuation < T , any Error > ? )
33+ }
34+
35+ private let lock = Mutex < State > ( . buffer( [ ] ) )
36+
37+ /// enqueue an element, or give it back immediately to the iterator if it is waiting for an element
38+ public func push( _ invocation: T ) {
39+
40+ // if the iterator is waiting for an element on `next()``, give it to it
41+ // otherwise, enqueue the element
42+ let maybeContinuation = self . lock. withLock { state -> CheckedContinuation < T , any Error > ? in
43+ switch consume state {
44+ case . continuation( let continuation) :
45+ state = . buffer( [ ] )
46+ return continuation
47+
48+ case . buffer( var buffer) :
49+ buffer. append ( invocation)
50+ state = . buffer( buffer)
51+ return nil
52+ }
53+ }
54+
55+ maybeContinuation? . resume ( returning: invocation)
56+ }
57+
58+ func next( ) async throws -> T ? {
59+ // exit the async for loop if the task is cancelled
60+ guard !Task. isCancelled else {
61+ return nil
62+ }
63+
64+ return try await withTaskCancellationHandler {
65+ try await withCheckedThrowingContinuation { ( continuation: CheckedContinuation < T , any Error > ) in
66+ let ( nextAction, nextError) = self . lock. withLock { state -> ( T ? , PoolError ? ) in
67+ switch consume state {
68+ case . buffer( var buffer) :
69+ if let first = buffer. popFirst ( ) {
70+ state = . buffer( buffer)
71+ return ( first, nil )
72+ } else {
73+ state = . continuation( continuation)
74+ return ( nil , nil )
75+ }
76+
77+ case . continuation( let previousContinuation) :
78+ state = . buffer( [ ] )
79+ return ( nil , PoolError ( cause: . nextCalledTwice( [ previousContinuation, continuation] ) ) )
80+ }
81+ }
82+
83+ if let nextError,
84+ case let . nextCalledTwice( continuations) = nextError. cause
85+ {
86+ for continuation in continuations { continuation? . resume ( throwing: nextError) }
87+ } else if let nextAction {
88+ continuation. resume ( returning: nextAction)
89+ }
90+ }
91+ } onCancel: {
92+ self . lock. withLock { state in
93+ switch consume state {
94+ case . buffer( let buffer) :
95+ state = . buffer( buffer)
96+ case . continuation( let continuation) :
97+ state = . buffer( [ ] )
98+ continuation? . resume ( throwing: CancellationError ( ) )
99+ }
100+ }
101+ }
102+ }
103+
104+ func makeAsyncIterator( ) -> Pool {
105+ self
106+ }
107+
108+ struct PoolError : Error {
109+ let cause : Cause
110+ var message : String {
111+ switch self . cause {
112+ case . nextCalledTwice:
113+ return " Concurrent invocations to next(). This is not allowed. "
114+ }
115+ }
116+
117+ enum Cause {
118+ case nextCalledTwice( [ CheckedContinuation < T , any Error > ? ] )
119+ }
120+ }
121+ }
122+ }
123+ #endif
0 commit comments