The following simulates such a crash (but traps it):-
var lastIndex = 0
try
{
for (i in 0..1999) {
lastIndex = i
val csr: Cursor = db.openHelper.writableDatabase.query(SimpleSQLiteQuery("SELECT * FROM model"))
val count = csr.count.toLong()
}
}catch ( e:java.lang.Exception)
{
Log.d(
"DBEXCEPTION",
"Exception trapped at INDEX " + lastIndex + " Message was " + e.message
)
e.printStackTrace()
}
}
- where
db
is an instance of an @Database
annotated class (etc).
- 0..1999 as 2000 open Cursors should be enough to always fail
- note that empty Cursors do not appear to result in a failure and not actually traversing the Cursor in some way doesn't result in the a failure. hence the
csr.count.toLong
- I suspect that the file underlying the Cursor is created/opened and closed properly by SQLite and it is not until an attempt is made to access the Cursor that the file is actually opened and then left open until the file is closed.
The log will then contain something like:-
2022-09-20 09:48:01.929 E/CursorWindow: CursorWindow: mmap() failed: errno=12.
2022-09-20 09:48:01.930 D/DBEXCEPTION: Exception trapped at INDEX 1415 Message was Could not allocate CursorWindow '/data/user/0/a.a.so73757679kotlinroomuniqueconflict/databases/the_database.db' of size 2097152 due to error -12.
2022-09-20 09:48:01.930 W/System.err: android.database.CursorWindowAllocationException: Could not allocate CursorWindow '/data/user/0/a.a.so73757679kotlinroomuniqueconflict/databases/the_database.db' of size 2097152 due to error -12.
2022-09-20 09:48:01.930 W/System.err: at android.database.CursorWindow.nativeCreate(Native Method)
2022-09-20 09:48:01.930 W/System.err: at android.database.CursorWindow.<init>(CursorWindow.java:139)
2022-09-20 09:48:01.930 W/System.err: at android.database.CursorWindow.<init>(CursorWindow.java:120)
2022-09-20 09:48:01.930 W/System.err: at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:202)
2022-09-20 09:48:01.931 W/System.err: at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:147)
2022-09-20 09:48:01.931 W/System.err: at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:140)
2022-09-20 09:48:01.931 W/System.err: at android.app.Activity.performCreate(Activity.java:7994)
2022-09-20 09:48:01.931 W/System.err: at android.app.Activity.performCreate(Activity.java:7978)
2022-09-20 09:48:01.931 W/System.err: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
2022-09-20 09:48:01.931 W/System.err: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
2022-09-20 09:48:01.931 W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
2022-09-20 09:48:01.931 W/System.err: at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
2022-09-20 09:48:01.931 W/System.err: at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
2022-09-20 09:48:01.931 W/System.err: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
2022-09-20 09:48:01.931 W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
2022-09-20 09:48:01.932 W/System.err: at android.os.Handler.dispatchMessage(Handler.java:106)
2022-09-20 09:48:01.932 W/System.err: at android.os.Looper.loop(Looper.java:223)
2022-09-20 09:48:01.932 W/System.err: at android.app.ActivityThread.main(ActivityThread.java:7656)
2022-09-20 09:48:01.932 W/System.err: at java.lang.reflect.Method.invoke(Native Method)
2022-09-20 09:48:01.932 W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
2022-09-20 09:48:01.932 W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
2022-09-20 09:48:02.003 W/libc: malloc(4194304) failed: returning null pointer
2022-09-20 09:48:02.003 A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x80000 in tid 23176 (RenderThread), pid 23153 (muniqueconflict)
It should be noted that the issue at hand is not exactly the number of Cursors but that underlying a Cursor is a file and that (I believe) the issue is the number of unclosed file allocations of which the database itself will have 3 (for the database, the -wal file and the -shm file).
However, there may also be a memory related issue, as this test then results in the malloc error (failed to get 4Mb).