Skip to content

Commit eef16ad

Browse files
committed
Add failing test.
1 parent b665748 commit eef16ad

File tree

2 files changed

+46
-3
lines changed

2 files changed

+46
-3
lines changed

test/basic_test.dart

+24
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,30 @@ void main() {
368368
expect(await savedTx!.getAutoCommit(), equals(true));
369369
expect(savedTx!.closed, equals(true));
370370
});
371+
372+
test('closing', () async {
373+
// Test race condition in SqliteConnectionPool:
374+
// 1. Open two concurrent queries, which opens two connection.
375+
// 2. Second connection takes longer to open than first.
376+
// 3. Call db.close().
377+
// 4. Now second connection is ready. Second query has two connections to choose from.
378+
// 5. However, first connection is closed, so it's removed from the pool.
379+
// 6. Triggers `Concurrent modification during iteration: Instance(length:1) of '_GrowableList'`
380+
381+
final db =
382+
SqliteDatabase.withFactory(testFactory(path: path, initStatements: [
383+
// Second connection to sleep more than first connection
384+
'SELECT test_sleep(test_connection_number() * 10)'
385+
]));
386+
await createTables(db);
387+
388+
final future1 = db.get('SELECT test_sleep(10) as sleep');
389+
final future2 = db.get('SELECT test_sleep(10) as sleep');
390+
final closeFuture = db.close();
391+
await closeFuture;
392+
await future1;
393+
await future2;
394+
});
371395
});
372396
}
373397

test/util.dart

+22-3
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ const defaultSqlitePath = 'libsqlite3.so.0';
1414

1515
class TestSqliteOpenFactory extends DefaultSqliteOpenFactory {
1616
String sqlitePath;
17+
List<String> initStatements;
1718

1819
TestSqliteOpenFactory(
1920
{required super.path,
2021
super.sqliteOptions,
21-
this.sqlitePath = defaultSqlitePath});
22+
this.sqlitePath = defaultSqlitePath,
23+
this.initStatements = const []});
2224

2325
@override
2426
sqlite.Database open(SqliteOpenOptions options) {
@@ -45,12 +47,29 @@ class TestSqliteOpenFactory extends DefaultSqliteOpenFactory {
4547
},
4648
);
4749

50+
db.createFunction(
51+
functionName: 'test_connection_number',
52+
argumentCount: const sqlite.AllowedArgumentCount(0),
53+
function: (args) {
54+
// write: 0, read: 1 - 5
55+
final name = Isolate.current.debugName ?? '-0';
56+
var nr = name.split('-').last;
57+
return int.tryParse(nr) ?? 0;
58+
},
59+
);
60+
61+
for (var s in initStatements) {
62+
db.execute(s);
63+
}
64+
4865
return db;
4966
}
5067
}
5168

52-
SqliteOpenFactory testFactory({String? path}) {
53-
return TestSqliteOpenFactory(path: path ?? dbPath());
69+
SqliteOpenFactory testFactory(
70+
{String? path, List<String> initStatements = const []}) {
71+
return TestSqliteOpenFactory(
72+
path: path ?? dbPath(), initStatements: initStatements);
5473
}
5574

5675
Future<SqliteDatabase> setupDatabase({String? path}) async {

0 commit comments

Comments
 (0)