EDIT: (Resolved by number 6)

What causes a System.AccessViolationException or System.ExecutionEngineException crash in SQLitePCLRaw.provider.e_sqlite3.dll when multiple threads access my FooDbContext simultaneously?

I have a Xamarin Forms app (3.5.0.169047) supporting UWP, Android, and iOS, using NETstandard 2.0.3, Microsoft.EntityFrameworkCore 2.2.4, Microsoft.EntityFrameworkCore.Sqlite 2.2.4, and I have a reproducible crash situation (it happens for sure on UWP) that I'm have trouble resolving that arises during simultaneous access to a sqlite database on device from two different threads at once. I have a sync process (pushes local data to an API and pulls online data from an API) that can take up to a minute or so that I need to execute in a separate thread to keep the UI responsive during its operation. I also need to allow querying of local data during sync to allow navigation during sync or other read-only data operations within the app during sync. The long-running sync works fine if I don't do any data access operation during the sync, but crashes right after the completion of any interrupting shorter data access operation.

The two crash exceptions that I've seen occur (possibly timing related for which causes the crash on identical reproductions) are the following, as seen from the Debug output from Visual Studio 2017 (v15.9.5):

  1. An unhandled exception of type 'System.AccessViolationException' occurred in SQLitePCLRaw.provider.e_sqlite3.dll Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

  2. An unhandled exception of type 'System.ExecutionEngineException' occurred in SQLitePCLRaw.provider.e_sqlite3.dll

While debugging, there is no additional detail about the exception; it happens during different long-running lines of the sync code, depending on the timing of doing the interrupting action; and the long-running code where the debugger shows the exception occurring is all contained within a Try { ... } Catch(Exception) { ... } try block in my code.

What might be causing this and how can I resolve it?

I've gone through all of the following:

  1. Is the SQLite usage compatible with multiple threads?

    • Yes; it is in "Serialized" mode by default according to https://www.sqlite.org/threadsafe.html and that supports multi-thread usage without restriction. The underlying version of SQLite being used is 3.26.0, which I determined while investigating my Microsoft.Data.Sqlite.SQLiteConnection information while debugging.
  2. Is it because I can't have more than one connection open with write capabilities simultaneously?

    • No; I even modified my connection strings using Microsoft.Data.Sqlite.SqliteConnectionStringBuilder to have the appropriate Mode (SqliteOpenMode.ReadOnly or SqliteOpenMode.ReadWriteCreate) depending on the needs of each data access.
  3. Is it because the interrupting thread's FooDbContext's Dispose() gets rid of resources that the long-running thread's FooDbContext relied upon?

    • No; I investigated this a lot since that was the last breakpoint I could hit prior to the crash. Even when I overrode the Dispose method in FooDbContext to do nothing, not even call the base class's Dispose (not recommended, but I tried it temporarily), the crash still occurred.
  4. Is there a setting I can set using Microsoft.Data.Sqlite.SqliteConnectionStringBuilder or Microsoft.Data.Sqlite.SQLiteConnection or Microsoft.EntityFrameworkCore.DbContextOptionsBuilder's UseSqlite function to ensure that Serialized mode is used (I wasn't 100% sure it was at this point)?

    • No; I looked extensively, and that option must be hidden away in the internals of the SQLite library chosen.
  5. I did a fair amount of reading and research, which still gave me little else I could try.

EDIT: Answer that solved this problem for me:

  1. I noticed that the dll the exception came from was SQLitePCLRaw.provider.e_sqlite3.dll. That led me to looking at what actually sets up the low-level SQLite library, and it is ultimately the call to SQLitePCL.Batteries_V2.Init(); that selects the platform-specific low-level SQLite provider to be used. Could that be wrong?
    • Yes; it turns out that after reading through the wiki info at https://github.com/ericsink/SQLitePCL.raw/wiki/SQLitePCL.Batteries.Init#what-does-batteries_v2init-do that the call to SQLitePCL.Batteries_V2.Init was intended to be done only once per platform (either in platform specific code, or in the shared code as long as Microsoft.EntityFrameworkCore.Sqlite is installed in the shared project as well as each platform specific project). My SQLitePCL.Batteries.Init usage was incorrectly inside OnConfiguring in FooDbContext, which made it called once per configuring of a FooDbContext instead of only being done once per app startup. Moving the SQLitePCL.Batteries_V2.Init(); line out of OnConfiguring, and into the App.xaml.cs constructor in my shared project fixed it! The crashes no longer occurred after the interrupting thread's data access. I really hope this saves someone the huge hassle it saved me trying to get to the bottom of this.

1 Answers

1
Tommy Elliott On Best Solutions

Answer that solved this problem for me:

  1. I noticed that the dll the exception came from was SQLitePCLRaw.provider.e_sqlite3.dll. That led me to looking at what actually sets up the low-level SQLite library, and it is ultimately the call to SQLitePCL.Batteries_V2.Init(); that selects the platform-specific low-level SQLite provider to be used. Could that be wrong?
    • Yes; it turns out that after reading through the wiki info at https://github.com/ericsink/SQLitePCL.raw/wiki/SQLitePCL.Batteries.Init#what-does-batteries_v2init-do that the call to SQLitePCL.Batteries_V2.Init was intended to be done only once per platform (either in platform specific code, or in the shared code as long as Microsoft.EntityFrameworkCore.Sqlite is installed in the shared project as well as each platform specific project). My SQLitePCL.Batteries.Init usage was incorrectly inside OnConfiguring in FooDbContext, which made it called once per configuring of a FooDbContext instead of only being done once per app startup. Moving the SQLitePCL.Batteries_V2.Init(); line out of OnConfiguring, and into the App.xaml.cs constructor in my shared project fixed it! The crashes no longer occurred after the interrupting thread's data access. I really hope this saves someone the huge hassle it saved me trying to get to the bottom of this.