Skip to content

Commit 74819b7

Browse files
authored
Merge branch 'master' into feature/testing-harness-sync
2 parents f9d79b5 + 378adf8 commit 74819b7

File tree

2 files changed

+59
-32
lines changed

2 files changed

+59
-32
lines changed

Sources/AWSLambdaRuntime/Lambda.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,12 @@ public enum Lambda {
8383
let lifecycle = Lifecycle(eventLoop: eventLoopGroup.next(), logger: logger, configuration: configuration, factory: factory)
8484
let signalSource = trap(signal: configuration.lifecycle.stopSignal) { signal in
8585
logger.info("intercepted signal: \(signal)")
86-
lifecycle.stop()
87-
}
88-
return lifecycle.start().always { _ in
8986
lifecycle.shutdown()
90-
signalSource.cancel()
87+
}
88+
return lifecycle.start().flatMap {
89+
return lifecycle.shutdownFuture.always { _ in
90+
signalSource.cancel()
91+
}
9192
}
9293
}
9394
}

Sources/AWSLambdaRuntime/LambdaLifecycle.swift

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,30 @@ import NIO
1717
import NIOConcurrencyHelpers
1818

1919
extension Lambda {
20-
internal final class Lifecycle {
20+
/// `Lifecycle` manages the Lambda process lifecycle.
21+
public final class Lifecycle {
2122
private let eventLoop: EventLoop
23+
private let shutdownPromise: EventLoopPromise<Int>
2224
private let logger: Logger
2325
private let configuration: Configuration
2426
private let factory: LambdaHandlerFactory
2527

2628
private var _state = State.idle
2729
private let stateLock = Lock()
2830

31+
/// Create a new `Lifecycle`.
32+
///
33+
/// - parameters:
34+
/// - eventLoop: An `EventLoop` to run the Lambda on.
35+
/// - logger: A `Logger` to log the Lambda events.
36+
/// - factory: A `LambdaHandlerFactory` to create the concrete Lambda handler.
37+
public convenience init(eventLoop: EventLoop, logger: Logger, factory: @escaping LambdaHandlerFactory) {
38+
self.init(eventLoop: eventLoop, logger: logger, configuration: .init(), factory: factory)
39+
}
40+
2941
init(eventLoop: EventLoop, logger: Logger, configuration: Configuration, factory: @escaping LambdaHandlerFactory) {
3042
self.eventLoop = eventLoop
43+
self.shutdownPromise = eventLoop.makePromise(of: Int.self)
3144
self.logger = logger
3245
self.configuration = configuration
3346
self.factory = factory
@@ -39,46 +52,46 @@ extension Lambda {
3952
}
4053
}
4154

42-
private var state: State {
43-
get {
44-
self.stateLock.withLock {
45-
self._state
46-
}
47-
}
48-
set {
49-
self.stateLock.withLockVoid {
50-
precondition(newValue.order > _state.order, "invalid state \(newValue) after \(self._state)")
51-
self._state = newValue
52-
}
53-
}
55+
/// The `Lifecycle` shutdown future.
56+
///
57+
/// - Returns: An `EventLoopFuture` that is fulfilled after the Lambda lifecycle has fully shutdown.
58+
public var shutdownFuture: EventLoopFuture<Int> {
59+
self.shutdownPromise.futureResult
5460
}
5561

56-
func start() -> EventLoopFuture<Int> {
62+
/// Start the `Lifecycle`.
63+
///
64+
/// - Returns: An `EventLoopFuture` that is fulfilled after the Lambda hander has been created and initiliazed, and a first run has been schduled.
65+
public func start() -> EventLoopFuture<Void> {
5766
logger.info("lambda lifecycle starting with \(self.configuration)")
5867
self.state = .initializing
68+
// triggered when the Lambda has finished its last run
69+
let finishedPromise = self.eventLoop.makePromise(of: Int.self)
70+
finishedPromise.futureResult.always { _ in
71+
self.markShutdown()
72+
}.cascade(to: self.shutdownPromise)
5973
var logger = self.logger
6074
logger[metadataKey: "lifecycleId"] = .string(self.configuration.lifecycle.id)
6175
let runner = Runner(eventLoop: self.eventLoop, configuration: self.configuration)
62-
return runner.initialize(logger: logger, factory: self.factory).flatMap { handler in
76+
return runner.initialize(logger: logger, factory: self.factory).map { handler in
6377
self.state = .active(runner, handler)
64-
return self.run()
78+
self.run(promise: finishedPromise)
6579
}
6680
}
6781

68-
func stop() {
69-
self.logger.debug("lambda lifecycle stopping")
70-
self.state = .stopping
82+
// MARK: - Private
83+
84+
/// Begin the `Lifecycle` shutdown.
85+
public func shutdown() {
86+
self.state = .shuttingdown
7187
}
7288

73-
func shutdown() {
74-
self.logger.debug("lambda lifecycle shutdown")
89+
private func markShutdown() {
7590
self.state = .shutdown
7691
}
7792

7893
@inline(__always)
79-
private func run() -> EventLoopFuture<Int> {
80-
let promise = self.eventLoop.makePromise(of: Int.self)
81-
94+
private func run(promise: EventLoopPromise<Int>) {
8295
func _run(_ count: Int) {
8396
switch self.state {
8497
case .active(let runner, let handler):
@@ -96,23 +109,36 @@ extension Lambda {
96109
promise.fail(error)
97110
}
98111
}
99-
case .stopping, .shutdown:
112+
case .shuttingdown:
100113
promise.succeed(count)
101114
default:
102115
preconditionFailure("invalid run state: \(self.state)")
103116
}
104117
}
105118

106119
_run(0)
120+
}
107121

108-
return promise.futureResult
122+
private var state: State {
123+
get {
124+
self.stateLock.withLock {
125+
self._state
126+
}
127+
}
128+
set {
129+
self.stateLock.withLockVoid {
130+
precondition(newValue.order > self._state.order, "invalid state \(newValue) after \(self._state)")
131+
self._state = newValue
132+
}
133+
self.logger.debug("lambda lifecycle state: \(newValue)")
134+
}
109135
}
110136

111137
private enum State {
112138
case idle
113139
case initializing
114140
case active(Runner, ByteBufferLambdaHandler)
115-
case stopping
141+
case shuttingdown
116142
case shutdown
117143

118144
internal var order: Int {
@@ -123,7 +149,7 @@ extension Lambda {
123149
return 1
124150
case .active:
125151
return 2
126-
case .stopping:
152+
case .shuttingdown:
127153
return 3
128154
case .shutdown:
129155
return 4

0 commit comments

Comments
 (0)