@@ -72,11 +72,11 @@ public actor ConnectionPool: Sendable {
7272 }
7373
7474 /// Gives the connection back to the pool.
75- private func reclaim( tx : borrowing Transaction ) async {
76- availableConnections. append ( tx . connection)
75+ private func reclaim( connection : SQLiteConnection , kind : Transaction . Kind ) async {
76+ availableConnections. append ( connection)
7777 alertAnyWaitersOfAvailableConnection ( )
7878
79- if tx . kind == . write {
79+ if kind == . write {
8080 await writeLock. unlock ( )
8181 }
8282 }
@@ -111,6 +111,7 @@ public actor ConnectionPool: Sendable {
111111 /// and we need to alert anybody waiting for one.
112112 private func alertAnyWaitersOfAvailableConnection( ) {
113113 guard !waitingForConnection. isEmpty, !availableConnections. isEmpty else { return }
114+ // I think its handing a connection off to a cancelled task?
114115 let waiter = waitingForConnection. removeFirst ( )
115116 let connection = availableConnections. removeFirst ( )
116117 waiter. resume ( with: . success( connection) )
@@ -127,32 +128,51 @@ extension ConnectionPool: Connection {
127128 }
128129
129130 /// Starts a transaction.
130- public func begin< Output> (
131+ public func begin< Output: Sendable > (
131132 _ kind: Transaction . Kind ,
132133 execute: ( borrowing Transaction ) throws -> Output
133134 ) async throws -> Output {
134- let tx = try await begin ( kind)
135-
136- // The `Result` wrapper seems weird, but allows us to keep
137- // tx functions consuming. Cause we cannot call `commit` in
138- // the `do` and on failure call `rollback` since it would
139- // have been consumed in the `commit`.
140- //
141- // Keeping them is consuming is nice since it stops callers
142- // from calling `commit` manually since its borrowed
143- let result = Result {
144- try execute ( tx)
135+ try await beginNoCommit ( kind) { tx in
136+ // The `Result` wrapper seems weird, but allows us to keep
137+ // tx functions consuming. Cause we cannot call `commit` in
138+ // the `do` and on failure call `rollback` since it would
139+ // have been consumed in the `commit`.
140+ //
141+ // Keeping them is consuming is nice since it stops callers
142+ // from calling `commit` manually since its borrowed
143+ let result = Result {
144+ try Task . checkCancellation ( )
145+ return try execute ( tx)
146+ }
147+
148+ switch result {
149+ case let . success( output) :
150+ try tx. commit ( )
151+ observer. didCommit ( )
152+ return output
153+ case let . failure( error) :
154+ try tx. commitOrRollback ( )
155+ throw error
156+ }
145157 }
158+ }
159+
160+ /// Starts a transaction for the lifetime of the closure
161+ /// and does not commit or rollback automatically. Just
162+ /// makes sure it reclaims the connection.
163+ public func beginNoCommit< Output: Sendable > (
164+ _ kind: Transaction . Kind ,
165+ execute: ( consuming Transaction ) async throws -> Output
166+ ) async throws -> Output {
167+ let tx = try await begin ( kind)
168+ let conn = tx. connection
146169
147- await reclaim ( tx: tx)
148-
149- switch result {
150- case let . success( output) :
151- try tx. commit ( )
152- observer. didCommit ( )
170+ do {
171+ let output = try await execute ( tx)
172+ await reclaim ( connection: conn, kind: kind)
153173 return output
154- case let . failure ( error ) :
155- try tx . commitOrRollback ( )
174+ } catch {
175+ await reclaim ( connection : conn , kind : kind )
156176 throw error
157177 }
158178 }
0 commit comments