-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Exception from SQLite provider when application is shutting down #27168
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
Some more investigation... I started working on commenting anything out that I thought might be an issue, working my way back up the main program loop...so to speak. Something strange:
After the "Application.Exit()" statement, the application continues on running the SplashScreen. I was under the impression that Application.Exit is supposed to close down all the forms and then exit the application from the "Main()" method. In other words, anything after "Application.Exit();" is just unreachable code. If I use:
...the program shuts down there, as expected. Looking at the documentation for Application.Exit() I see:
I'm no expert but am I to understand that "Application.Exit()" will kill off all message pumps, close all forms, but then the application continues to anything following it? -Joe |
Same thing happens if I use:
I know that the Application.Exit() closes the form that is currently running the code....but I would have expected the "this.Close()" would at least throw an exception. |
@dataweasel Application.Exit() is a Winforms API which terminates the Winforms application/resources (e.g. closing windows), but the .NET program itself continues running. Environment.Exit is the thing that actually terminates the .NET process, and also triggers the Sqlite connection pool clearing code which you see above. Microsoft.Data.Sqlite and Winforms are oblivious of each other. Regardless, the stack trace you posted above does seem to indicate a bug. A few questions:
From a cursory look at SqliteConnectionPool nothing obvious pops up. We do access _connections.Count without locking, but that shouldn't cause any exceptions (we may want to consider locking around it just in case though). /cc @bricelam |
@roji - I am using the latest Microsoft.Data.Sqlite from Nuget (so...6.0.1?). It does happen reliably every time...but I think I might have figured out my problem. I think I had a stray Sqlite connection still open when the program went to exit. I did a full code review and found a class where I had opened a Sqlite connection and did not have a close at the end of the method. When the close wasn't there I would get this exception 100% of the time. When I added the close, the problem went away. I then commented out the close and the exception returned. I will try to get a minimal code repro for you. I'm looking at the following:
I'll let you know if this repros. -Joe |
@dataweasel thanks for the additional. Even if you forgot the Close, I'm still very interested in looking into this, as it may indicate an internal bug which could have other effects. So a repro would be much appreciated. |
That's an interesting bug, Whenever the pool attempts to reclaim a connection, while enumerating the list, it might call Return on a leaked connection. While returning the connection to the pool, in some cases it might dispose them. And while disposing the connection, it's removed from the |
I quickly tried this with a Console Application and leaving the connection open didn't cause an issue. I'm not doing much work with the DB connections (just opening the connection to a file with no tables or data) so I'm not sure if that has anything to do with it. My application where this was failing was a Windows Form app (.Net 5). Still working on it. |
Managed to reproduce in a console app. using Microsoft.Data.Sqlite;
Test();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
static void Test()
{
var connection = new SqliteConnection("Data Source=hello.db");
connection.Open();
} |
This works:
This breaks:
Changing to var instead of a declaration is the difference? -Joe |
Kind of, in your case it's the static field - the point is for the GC to collect |
Okay...I am starting to see it now. It's now how it is being used or even that it is "open" when the program is ending. When GC attempts to dispose of it (from a pool or something) it's disposing of the connection object, which changes the collection of connections while it is being enumerated. Did I get that right? That's why the stack trace has the "ReclaimLeakedConnections()" and such. I'm still learning. Thanks for the insights. |
Not exactly. SQLite has a mechanism to detect leaked connections, which is mostly determined by |
Thanks for looking into this @vonzshik - yeah, ReclaimLeakedConnections iterates over _connections, but calls return inside which mutates them. So not a concurrency issue - just a plain old mutate-within-iteration bug. Should be fixable by copying _connections before iterating. |
I hope I'm posting this in the right location. I have been running this problem down for several days now and I'm not sure what is going on. I've checked all my various libraries and 3rd party packages to see if there might be an issue there. The problem is that the issue is happening when my code is no longer running.
In a nutshell, my issue is happening here:
It's at the point above that a "System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute." exception is thrown. No tasks show in the debugger. I'm not sure what collection it is talking about since no collections in my code are being iterated at this point.
So...I disable "Enable Just My Code" in the debugger options, then set a breakpoint for that curly bracket. I kick off a debugging session again, then close down the application. When I hit the breakpoint there are no "Tasks" and no "Locals" shown. With "Source Link" enabled I the debugger I get:
Yesterday I ran the application from OUTSIDE Visual Studio 2022 from the DEBUG directory. When I clicked the close button the application appeared to shut down without issue, but looking in the Windows Application Log I saw:
...then immediately after...
I checked that I closed and even DISPOSE() my Sqlite Database connections.
I'm totally at a loss if this is something with Visual Studio, .Net 5, or what. It doesn't appear to be code that I'm running, although I haven't ruled it out. It's just that the exception is being thrown after all my code stops running.
I'd appreciate any guidance on where to look next that anyone has.
-Joe
The text was updated successfully, but these errors were encountered: