Skip to content

Commit fafab8f

Browse files
authored
Overhaul service lifecycle for Structured Concurrency (#130)
* Overhaul service lifecycle for Structured Concurrency # Motivation Since the release of Swift Concurrency the server ecosystem has adopted `async`/`await` in a lot of APIs making the user experience amazing. Another thing that Swift Concurrency introduced is Structured Concurrency which allows to create a task tree that with parent to child relations. Structured Concurrency has many benefits such as automatic cancellation and task local propagation. The current state of this library predates the introduction of Swift Concurrency and is not inline anymore with how applications are structured that want to leverage Concurrency. # Modification This PR overhauls the implementation of service lifecycle completely. The reason for this is that the current implementation is very focused on how NIO based applications work before the introduction of Concurrency. Furthermore, Structured Concurrency is actually replacing part of the current functionality by providing new scoping mechanisms. The overhauled implementation provides two primitive types. Anew `Service` protocol and a `ServiceRunner`. The former is providing a clear API which services have to conform to so that they can be run by the `ServiceRunner`. An example usage of the types looks like this ```swift actor FooService: Service { func run() async throws {} func shutdownGracefully() async throws {} } actor BarService: Service { func run() async throws {} func shutdownGracefully() async throws {} } let fooService = FooService() let barService = BarService() let runner = ServiceRunner( services: [fooService, barService], configuration: .init(), logger: logger ) try await runner.run() ``` # Result We now have a service lifecycle library that integrates nicely with Structured Concurrency. Its value add is that it solves to complex setup of the task group with the signal handling. Furthermore, it provides a currency type `Service` that can be passed to inject services. * Remove `shutdownGracefully` method and switch to a handler based approach. * Code review from George * Add extensions to `TaskGroup` and `ThrowingTaskGroup` * Remove long running property * Documentation * Fix tests * Ensure ordering of shutdown * Test # Motivation # Modification # Result * Introduce ServiceLifecycleTestKit * Add AsyncCancelOngracefulShutdownSequence * Tests and docs for graceful shutdown # Motivation # Modification # Result * Remove unsafe flags * Add article for application authors * Add correct licence and notice information for the things we copied from swift-nio * Code review * Remove @_unsafeInheritExectuor * Code review * Change log levels, rename to withGracefulShutdownHandler and support nested service runner graceful shutdown * Add public `shutdownGracefully` method to the service runner * Code review and expose new cancelOnGracefulShutdown method * Extend graceful shutdown APIs and tolerate running outside of a `ServiceRunner` * Rename to `ServiceGroup` * Enable Swift 5.6 support * Update soundess dockerfile * Update Readme * Make 5.6 build work
1 parent 7b60c92 commit fafab8f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+3395
-4766
lines changed

.spi.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
version: 1
22
builder:
33
configs:
4-
- documentation_targets: [Lifecycle, LifecycleNIOCompat]
4+
- documentation_targets: [ServiceLifecycle, UnixSignals]

.swiftformat

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
# file options
22

3-
--swiftversion 5.0
3+
--swiftversion 5.7
44
--exclude .build
55

66
# format options
77

88
--self insert
99
--patternlet inline
10+
--ranges nospace
1011
--stripunusedargs unnamed-only
1112
--ifdef no-indent
13+
--extensionacl on-declarations
14+
--disable typeSugar # https://github.com/nicklockwood/SwiftFormat/issues/636
15+
--disable andOperator
16+
--disable wrapMultilineStatementBraces
17+
--disable enumNamespaces
18+
--disable redundantExtensionACL
19+
--disable redundantReturn
20+
--disable preferKeyPath
21+
--disable sortedSwitchCases
22+
--disable hoistTry
23+
--disable hoistAwait
24+
--disable redundantOptionalBinding
1225

1326
# rules

NOTICE.txt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
The ServiceLifecycle Project
3+
===========================
4+
5+
Please visit the ServiceLifecycle web site for more information:
6+
7+
* https://github.com/swift-server/swift-service-lifecycle
8+
9+
Copyright 2019-2023 The ServiceLifecycle Project
10+
11+
The ServiceLifecycle Project licenses this file to you under the Apache License,
12+
version 2.0 (the "License"); you may not use this file except in compliance
13+
with the License. You may obtain a copy of the License at:
14+
15+
https://www.apache.org/licenses/LICENSE-2.0
16+
17+
Unless required by applicable law or agreed to in writing, software
18+
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
19+
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
20+
License for the specific language governing permissions and limitations
21+
under the License.
22+
23+
Also, please refer to each LICENSE.txt file, which is located in
24+
the 'license' directory of the distribution file, for the license terms of the
25+
components that this product depends on.
26+
27+
---
28+
29+
This product contains derivations of the Lock and LockedValueBox implementations from SwiftNIO.
30+
31+
* LICENSE (Apache License 2.0):
32+
* https://www.apache.org/licenses/LICENSE-2.0
33+
* HOMEPAGE:
34+
* https://github.com/apple/swift-nio
35+
36+
---

Package.swift

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,83 @@ import PackageDescription
44

55
let package = Package(
66
name: "swift-service-lifecycle",
7+
platforms: [
8+
.macOS(.v10_15),
9+
.iOS(.v13),
10+
.watchOS(.v6),
11+
.tvOS(.v13),
12+
],
713
products: [
8-
.library(name: "Lifecycle", targets: ["Lifecycle"]),
9-
.library(name: "LifecycleNIOCompat", targets: ["LifecycleNIOCompat"]),
14+
.library(
15+
name: "ServiceLifecycle",
16+
targets: ["ServiceLifecycle"]
17+
),
18+
.library(
19+
name: "ServiceLifecycleTestKit",
20+
targets: ["ServiceLifecycleTestKit"]
21+
),
22+
.library(
23+
name: "UnixSignals",
24+
targets: ["UnixSignals"]
25+
),
1026
],
1127
dependencies: [
12-
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.0.0"),
13-
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
14-
.package(url: "https://github.com/apple/swift-metrics.git", "1.0.0" ..< "3.0.0"),
15-
.package(url: "https://github.com/swift-server/swift-backtrace.git", from: "1.1.1"),
16-
.package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0"), // used in tests
17-
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
28+
.package(
29+
url: "https://github.com/apple/swift-log.git",
30+
from: "1.5.2"
31+
),
32+
.package(
33+
url: "https://github.com/apple/swift-docc-plugin",
34+
from: "1.0.0"
35+
),
36+
.package(
37+
url: "https://github.com/apple/swift-async-algorithms",
38+
from: "0.1.0"
39+
),
1840
],
1941
targets: [
20-
.target(name: "Lifecycle", dependencies: [
21-
.product(name: "Atomics", package: "swift-atomics"),
22-
.product(name: "Logging", package: "swift-log"),
23-
.product(name: "Metrics", package: "swift-metrics"),
24-
.product(name: "Backtrace", package: "swift-backtrace"),
25-
]),
26-
27-
.target(name: "LifecycleNIOCompat", dependencies: [
28-
"Lifecycle",
29-
.product(name: "NIO", package: "swift-nio"),
30-
]),
31-
32-
.testTarget(name: "LifecycleTests", dependencies: ["Lifecycle", "LifecycleNIOCompat"]),
42+
.target(
43+
name: "ServiceLifecycle",
44+
dependencies: [
45+
.product(
46+
name: "Logging",
47+
package: "swift-log"
48+
),
49+
.product(
50+
name: "AsyncAlgorithms",
51+
package: "swift-async-algorithms"
52+
),
53+
.target(name: "UnixSignals"),
54+
.target(name: "ConcurrencyHelpers"),
55+
]
56+
),
57+
.target(
58+
name: "ServiceLifecycleTestKit",
59+
dependencies: [
60+
.target(name: "ServiceLifecycle"),
61+
]
62+
),
63+
.target(
64+
name: "UnixSignals",
65+
dependencies: [
66+
.target(name: "ConcurrencyHelpers"),
67+
]
68+
),
69+
.target(
70+
name: "ConcurrencyHelpers"
71+
),
72+
.testTarget(
73+
name: "ServiceLifecycleTests",
74+
dependencies: [
75+
.target(name: "ServiceLifecycle"),
76+
.target(name: "ServiceLifecycleTestKit"),
77+
]
78+
),
79+
.testTarget(
80+
name: "UnixSignalsTests",
81+
dependencies: [
82+
.target(name: "UnixSignals"),
83+
]
84+
),
3385
]
3486
)

[email protected]

Lines changed: 0 additions & 37 deletions
This file was deleted.

[email protected]

Lines changed: 0 additions & 37 deletions
This file was deleted.

[email protected]

Lines changed: 0 additions & 37 deletions
This file was deleted.

[email protected]

Lines changed: 0 additions & 37 deletions
This file was deleted.

[email protected]

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)