@@ -120,83 +120,129 @@ class DatabasePoolReleaseMemoryTests: GRDBTestCase {
120120
121121#endif
122122
123- // TODO: fix flaky test
124- // func testDatabasePoolReleaseMemoryClosesReaderConnections() throws {
125- // let countQueue = DispatchQueue(label: "GRDB")
126- // var openConnectionCount = 0
127- // var totalOpenConnectionCount = 0
128- //
129- // dbConfiguration.SQLiteConnectionDidOpen = {
130- // countQueue.sync {
131- // totalOpenConnectionCount += 1
132- // openConnectionCount += 1
133- // }
134- // }
135- //
136- // dbConfiguration.SQLiteConnectionDidClose = {
137- // countQueue.sync {
138- // openConnectionCount -= 1
139- // }
140- // }
141- //
142- // let dbPool = try makeDatabasePool()
143- // try dbPool.write { db in
144- // try db.execute(sql: "CREATE TABLE items (id INTEGER PRIMARY KEY)")
145- // for _ in 0..<2 {
146- // try db.execute(sql: "INSERT INTO items (id) VALUES (NULL)")
147- // }
148- // }
149- //
150- // // Block 1 Block 2 Block3
151- // // SELECT * FROM items
152- // // step
153- // // >
154- // let s1 = DispatchSemaphore(value: 0)
155- // // SELECT * FROM items
156- // // step
157- // // >
158- // let s2 = DispatchSemaphore(value: 0)
159- // // step step
160- // // >
161- // let s3 = DispatchSemaphore(value: 0)
162- // // end end releaseMemory
163- //
164- // let block1 = { () in
165- // try! dbPool.read { db in
166- // let cursor = try Row.fetchCursor(db, sql: "SELECT * FROM items")
167- // XCTAssertTrue(try cursor.next() != nil)
168- // s1.signal()
169- // _ = s2.wait(timeout: .distantFuture)
170- // XCTAssertTrue(try cursor.next() != nil)
171- // s3.signal()
172- // XCTAssertTrue(try cursor.next() == nil)
173- // }
174- // }
175- // let block2 = { () in
176- // _ = s1.wait(timeout: .distantFuture)
177- // try! dbPool.read { db in
178- // let cursor = try Row.fetchCursor(db, sql: "SELECT * FROM items")
179- // XCTAssertTrue(try cursor.next() != nil)
180- // s2.signal()
181- // XCTAssertTrue(try cursor.next() != nil)
182- // XCTAssertTrue(try cursor.next() == nil)
183- // }
184- // }
185- // let block3 = { () in
186- // _ = s3.wait(timeout: .distantFuture)
187- // dbPool.releaseMemory()
188- // }
189- // let blocks = [block1, block2, block3]
190- // DispatchQueue.concurrentPerform(iterations: blocks.count) { index in // FIXME: this crashes sometimes
191- // blocks[index]()
192- // }
193- //
194- // // Two readers, one writer
195- // XCTAssertEqual(totalOpenConnectionCount, 3)
196- //
197- // // Writer is still open
198- // XCTAssertEqual(openConnectionCount, 1)
199- // }
123+ func test_DatabasePool_releaseMemory_closes_reader_connections( ) throws {
124+ // A complicated test setup that opens multiple reader connections.
125+ let countQueue = DispatchQueue ( label: " GRDB " )
126+ var openConnectionCount = 0
127+ var totalOpenConnectionCount = 0
128+
129+ dbConfiguration. SQLiteConnectionDidOpen = {
130+ countQueue. sync {
131+ totalOpenConnectionCount += 1
132+ openConnectionCount += 1
133+ }
134+ }
135+
136+ dbConfiguration. SQLiteConnectionDidClose = {
137+ countQueue. sync {
138+ openConnectionCount -= 1
139+ }
140+ }
141+
142+ let dbPool = try makeDatabasePool ( )
143+ try dbPool. write { db in
144+ try db. execute ( sql: " CREATE TABLE items (id INTEGER PRIMARY KEY) " )
145+ for _ in 0 ..< 2 {
146+ try db. execute ( sql: " INSERT INTO items (id) VALUES (NULL) " )
147+ }
148+ }
149+
150+ // Block 1 Block 2 Block3
151+ // SELECT * FROM items
152+ // step
153+ // >
154+ let s1 = DispatchSemaphore ( value: 0 )
155+ // SELECT * FROM items
156+ // step
157+ // >
158+ let s2 = DispatchSemaphore ( value: 0 )
159+ // step step
160+ // >
161+ let s3 = DispatchSemaphore ( value: 0 )
162+ // end end releaseMemory
163+
164+ let block1 = { ( ) in
165+ try ! dbPool. read { db in
166+ let cursor = try Row . fetchCursor ( db, sql: " SELECT * FROM items " )
167+ XCTAssertTrue ( try cursor. next ( ) != nil )
168+ s1. signal ( )
169+ _ = s2. wait ( timeout: . distantFuture)
170+ XCTAssertTrue ( try cursor. next ( ) != nil )
171+ s3. signal ( )
172+ XCTAssertTrue ( try cursor. next ( ) == nil )
173+ }
174+ }
175+ let block2 = { ( ) in
176+ _ = s1. wait ( timeout: . distantFuture)
177+ try ! dbPool. read { db in
178+ let cursor = try Row . fetchCursor ( db, sql: " SELECT * FROM items " )
179+ XCTAssertTrue ( try cursor. next ( ) != nil )
180+ s2. signal ( )
181+ XCTAssertTrue ( try cursor. next ( ) != nil )
182+ XCTAssertTrue ( try cursor. next ( ) == nil )
183+ }
184+ }
185+ let block3 = { ( ) in
186+ _ = s3. wait ( timeout: . distantFuture)
187+ dbPool. releaseMemory ( )
188+ }
189+ let blocks = [ block1, block2, block3]
190+ DispatchQueue . concurrentPerform ( iterations: blocks. count) { index in // FIXME: this crashes sometimes
191+ blocks [ index] ( )
192+ }
193+
194+ // Two readers, one writer
195+ XCTAssertEqual ( totalOpenConnectionCount, 3 )
196+
197+ // Writer is still open
198+ XCTAssertEqual ( openConnectionCount, 1 )
199+ }
200+
201+ func test_DatabasePool_releaseMemory_closes_reader_connections_when_persistentReaderConnections_is_false( ) throws {
202+ var persistentConnectionCount = 0
203+
204+ dbConfiguration. SQLiteConnectionDidOpen = {
205+ persistentConnectionCount += 1
206+ }
207+
208+ dbConfiguration. SQLiteConnectionDidClose = {
209+ persistentConnectionCount -= 1
210+ }
211+
212+ dbConfiguration. persistentReaderConnections = false
213+
214+ let dbPool = try makeDatabasePool ( )
215+ XCTAssertEqual ( persistentConnectionCount, 1 ) // writer
216+
217+ try dbPool. read { _ in }
218+ XCTAssertEqual ( persistentConnectionCount, 2 ) // writer + reader
219+
220+ dbPool. releaseMemory ( )
221+ XCTAssertEqual ( persistentConnectionCount, 1 ) // writer
222+ }
223+
224+ func test_DatabasePool_releaseMemory_does_not_close_reader_connections_when_persistentReaderConnections_is_true( ) throws {
225+ var persistentConnectionCount = 0
226+
227+ dbConfiguration. SQLiteConnectionDidOpen = {
228+ persistentConnectionCount += 1
229+ }
230+
231+ dbConfiguration. SQLiteConnectionDidClose = {
232+ persistentConnectionCount -= 1
233+ }
234+
235+ dbConfiguration. persistentReaderConnections = true
236+
237+ let dbPool = try makeDatabasePool ( )
238+ XCTAssertEqual ( persistentConnectionCount, 1 ) // writer
239+
240+ try dbPool. read { _ in }
241+ XCTAssertEqual ( persistentConnectionCount, 2 ) // writer + reader
242+
243+ dbPool. releaseMemory ( )
244+ XCTAssertEqual ( persistentConnectionCount, 2 ) // writer + reader
245+ }
200246
201247 func testBlocksRetainConnection( ) throws {
202248 let countQueue = DispatchQueue ( label: " GRDB " )
0 commit comments