Skip to content

Commit

Permalink
Add async alternative for Application.shutdown (#3189)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xTim committed May 10, 2024
1 parent d411635 commit 840d87c
Show file tree
Hide file tree
Showing 9 changed files with 55 additions and 27 deletions.
14 changes: 9 additions & 5 deletions Sources/Development/entrypoint.swift
Expand Up @@ -7,11 +7,15 @@ struct Entrypoint {
var env = try Environment.detect()
try LoggingSystem.bootstrap(from: &env)

let app = await Application(env)
defer { app.shutdown() }

try configure(app)
try await app.execute()
let app = try await Application.make(env)
do {
try configure(app)
try await app.execute()
try await app.asyncShutdown()
} catch {
try? await app.asyncShutdown()
throw error
}
}
}

42 changes: 33 additions & 9 deletions Sources/Vapor/Application.swift
Expand Up @@ -242,25 +242,36 @@ public final class Application: Sendable {
}
}

@available(*, noasync, message: "This can block the thread and should not be called in an async context", renamed: "asyncShutdown()")
public func shutdown() {
assert(!self.didShutdown, "Application has already shut down")
self.logger.debug("Application shutting down")
triggerShutdown()

self.logger.trace("Shutting down providers")
self.lifecycle.handlers.reversed().forEach { $0.shutdown(self) }
self.lifecycle.handlers = []
switch self.eventLoopGroupProvider {
case .shared:
self.logger.trace("Running on shared EventLoopGroup. Not shutting down EventLoopGroup.")
case .createNew:
self.logger.trace("Shutting down EventLoopGroup")
do {
try self.eventLoopGroup.syncShutdownGracefully()
} catch {
self.logger.warning("Shutting down EventLoopGroup failed: \(error)")
}
}

self.logger.trace("Clearing Application storage")
self.storage.shutdown()
self.storage.clear()
self._didShutdown.withLockedValue { $0 = true }
self.logger.trace("Application shutdown complete")
}

public func asyncShutdown() async throws {
triggerShutdown()

switch self.eventLoopGroupProvider {
case .shared:
self.logger.trace("Running on shared EventLoopGroup. Not shutting down EventLoopGroup.")
case .createNew:
self.logger.trace("Shutting down EventLoopGroup")
do {
try self.eventLoopGroup.syncShutdownGracefully()
try await self.eventLoopGroup.shutdownGracefully()
} catch {
self.logger.warning("Shutting down EventLoopGroup failed: \(error)")
}
Expand All @@ -269,6 +280,19 @@ public final class Application: Sendable {
self._didShutdown.withLockedValue { $0 = true }
self.logger.trace("Application shutdown complete")
}

private func triggerShutdown() {
assert(!self.didShutdown, "Application has already shut down")
self.logger.debug("Application shutting down")

self.logger.trace("Shutting down providers")
self.lifecycle.handlers.reversed().forEach { $0.shutdown(self) }
self.lifecycle.handlers = []

self.logger.trace("Clearing Application storage")
self.storage.shutdown()
self.storage.clear()
}

deinit {
self.logger.trace("Application deinitialized, goodbye!")
Expand Down
4 changes: 2 additions & 2 deletions Tests/VaporTests/AsyncClientTests.swift
Expand Up @@ -55,7 +55,7 @@ final class AsyncClientTests: XCTestCase {
}

override func tearDown() async throws {
remoteApp.shutdown()
try await remoteApp.asyncShutdown()
}

func testClientConfigurationChange() async throws {
Expand Down Expand Up @@ -119,7 +119,7 @@ final class AsyncClientTests: XCTestCase {
}

func testClientBeforeSend() async throws {
let app = await Application()
let app = try await Application.make()
defer { app.shutdown() }
try app.boot()

Expand Down
12 changes: 6 additions & 6 deletions Tests/VaporTests/AsyncPasswordTests.swift
Expand Up @@ -5,39 +5,39 @@ import Vapor
final class AsyncPasswordTests: XCTestCase {
func testAsyncBCryptRequestPassword() async throws {
let test = Environment(name: "testing", arguments: ["vapor"])
let app = await Application(test)
let app = try await Application.make(test)
defer { app.shutdown() }

try await assertAsyncRequestPasswordVerifies(.bcrypt, on: app)
}

func testAsyncPlaintextRequestPassword() async throws {
let test = Environment(name: "testing", arguments: ["vapor"])
let app = await Application(test)
let app = try await Application.make(test)
defer { app.shutdown() }

try await assertAsyncRequestPasswordVerifies(.plaintext, on: app)
}

func testAsyncBCryptApplicationPassword() async throws {
let test = Environment(name: "testing", arguments: ["vapor"])
let app = await Application(test)
let app = try await Application.make(test)
defer { app.shutdown() }

try await assertAsyncApplicationPasswordVerifies(.bcrypt, on: app)
}

func testAsyncPlaintextApplicationPassword() async throws {
let test = Environment(name: "testing", arguments: ["vapor"])
let app = await Application(test)
let app = try await Application.make(test)
defer { app.shutdown() }

try await assertAsyncApplicationPasswordVerifies(.plaintext, on: app)
}

func testAsyncUsesProvider() async throws {
let test = Environment(name: "testing", arguments: ["vapor"])
let app = await Application(test)
let app = try await Application.make(test)
defer { app.shutdown() }
app.passwords.use(.plaintext)
let hash = try await app.password.async(
Expand All @@ -49,7 +49,7 @@ final class AsyncPasswordTests: XCTestCase {

func testAsyncApplicationDefault() async throws {
let test = Environment(name: "testing", arguments: ["vapor"])
let app = await Application(test)
let app = try await Application.make(test)
defer { app.shutdown() }
app.passwords.use(.plaintext)
let hash = try await app.password.async.hash("vapor")
Expand Down
2 changes: 1 addition & 1 deletion Tests/VaporTests/AsyncRequestTests.swift
Expand Up @@ -25,7 +25,7 @@ final class AsyncRequestTests: XCTestCase {
}

override func tearDown() async throws {
app.shutdown()
try await app.asyncShutdown()
}

func testStreamingRequest() async throws {
Expand Down
2 changes: 1 addition & 1 deletion Tests/VaporTests/AsyncSessionTests.swift
Expand Up @@ -40,7 +40,7 @@ final class AsyncSessionTests: XCTestCase {

var cookie: HTTPCookies.Value?

let app = await Application()
let app = try await Application.make()
defer { app.shutdown() }

let cache = MockKeyedCache()
Expand Down
2 changes: 1 addition & 1 deletion Tests/VaporTests/ClientTests.swift
Expand Up @@ -62,7 +62,7 @@ final class ClientTests: XCTestCase {
}

override func tearDown() async throws {
remoteApp.shutdown()
try await remoteApp.asyncShutdown()
}

func testClientConfigurationChange() throws {
Expand Down
2 changes: 1 addition & 1 deletion Tests/VaporTests/PipelineTests.swift
Expand Up @@ -14,7 +14,7 @@ final class PipelineTests: XCTestCase {
}

override func tearDown() async throws {
app.shutdown()
try await app.asyncShutdown()
}


Expand Down
2 changes: 1 addition & 1 deletion Tests/VaporTests/SessionTests.swift
Expand Up @@ -41,7 +41,7 @@ final class SessionTests: XCTestCase {

var cookie: HTTPCookies.Value?

let app = await Application()
let app = try await Application.make()
defer { app.shutdown() }

let cache = MockKeyedCache()
Expand Down

0 comments on commit 840d87c

Please sign in to comment.