@@ -229,4 +229,84 @@ struct SQLiteNIOExtrasTestSuite {
229229 #expect( result [ 0 ] . column ( " is_valid " ) ? . bool == true )
230230 }
231231 }
232+
233+ // MARK: - lock
234+
235+ private actor LockBarrier {
236+ private var ready = false
237+ private var waiters : [ CheckedContinuation < Void , Never > ] = [ ]
238+
239+ func waitUntilLocked( ) async {
240+ if ready { return }
241+ await withCheckedContinuation { continuation in
242+ waiters. append ( continuation)
243+ }
244+ }
245+
246+ func markLocked( ) {
247+ guard !ready else { return }
248+ ready = true
249+ let pending = waiters
250+ waiters. removeAll ( keepingCapacity: false )
251+ for continuation in pending {
252+ continuation. resume ( )
253+ }
254+ }
255+ }
256+
257+ @Test
258+ func warmupWaitsForTransientExclusiveLock( ) async throws {
259+ let dbPath =
260+ " /tmp/feather-lock- \( UInt64 . random ( in: 0 ... UInt64 . max) ) .sqlite "
261+
262+ var logger = Logger ( label: " test.sqlite.lock.warmup " )
263+ logger. logLevel = . info
264+
265+ let config = SQLiteClient . Configuration (
266+ storage: . file( path: dbPath) ,
267+ logger: logger,
268+ minimumConnections: 1 ,
269+ maximumConnections: 1 ,
270+ journalMode: . delete,
271+ busyTimeoutMilliseconds: 5_000
272+ )
273+
274+ let clientA = SQLiteClient ( configuration: config)
275+ let clientB = SQLiteClient ( configuration: config)
276+
277+ try await clientA. run ( )
278+ defer {
279+ Task {
280+ await clientB. shutdown ( )
281+ await clientA. shutdown ( )
282+ }
283+ }
284+
285+ let barrier = LockBarrier ( )
286+
287+ let holder = Task {
288+ try await clientA. withConnection { connection in
289+ _ = try await connection. query ( " BEGIN EXCLUSIVE; " )
290+ await barrier. markLocked ( )
291+ try await Task . sleep ( for: . milliseconds( 1200 ) )
292+ _ = try await connection. query ( " COMMIT; " )
293+ }
294+ }
295+
296+ await barrier. waitUntilLocked ( )
297+
298+ let clock = ContinuousClock ( )
299+ let start = clock. now
300+
301+ try await clientB. run ( )
302+ try await clientB. withConnection { connection in
303+ _ = try await connection. query ( " SELECT 1; " )
304+ }
305+
306+ let elapsed = start. duration ( to: clock. now)
307+ #expect( elapsed >= . milliseconds( 900 ) )
308+
309+ _ = try await holder. value
310+ }
311+
232312}
0 commit comments