Consider exception paths as well as standard code flow. Is it possible that you are locking within a Try block and unloading at the end of a method, or something more complicated along those lines?
The reason it doesn’t show in debug is that preemptive threads are only active in built applications.
I’m not sure when or where I read or heard this. I seem to remember it being in a video (on YouTube? maybe this one?) . A presentation where @Geoff_Perlman was speaking and @Paul_Lefebvre assured him that preemptive threads are preemptive even during debugging.
I suspect you’re thinking of William. I’ve not been on any webinars regarding preemptive threads. But yes, preemptive threads are preemptive when debugging, although they are slower than they are in built apps.
First, I should have specified in my original post that I am using coop threads, not preemptive, but the discussion has been interesting.
Second, related to Ian’s observation about exceptions, I wonder whether my approach to combining Try/Catch with critical sections is correct. The approach I use is as follows:
Try
CriticalSection.Enter
DB.BeginTransaction
[Do sqlite things that do not directly or indirectly involve entering or leaving critical section]
DB.CommitTransaction
CriticalSection.Leave
Catch Bad Things
DB.RollbackTransaction
CriticalSection.Leave
End Try
Is this the proper structure or is there a better approach?
A couple of things. When posting code highlight it and press the </> button (or place ``` before and after it) It makes it readable and prevents " being turned into smart quotes.
Secondly, this is the format that others are suggesting. The Finally section of Try / Catch block gets executed no matter what happens with the exception. It happens after the Try, and after the Catch if it occurs. Perfect for this sort of thing. It becomes even more useful if you have more than one Catch block, which is allowed.
Try
CriticalSection.Enter
DB.BeginTransaction
[Do sqlite things that do not directly or indirectly involve entering or leaving critical section]
DB.CommitTransaction
Catch Bad Things
DB.RollbackTransaction
Finally
CriticalSection.Leave
End Try
Finally would not execute in that case. If the exception was not part of the try...catch it would bubble to App.UnhandledException and the rest of the code would not execute.
Finally is “the try part completed or the catch part completed”, which really is synonymous with “anything after end try”. It’s a stylistic choice really, but I’d have written Ian’s sample with the try...catch being exclusively database code, and the CriticalSection being handled outside the statement.
Also of concern is something that would re-raise the exception from within a catch block. Which could cause the Finally to fail to execute. That said putting it outside of the Try Catch block would fail too, if the exception is allowed to propagate back out of the method.
Perhaps the best format is as follows:
Try
CriticalSection.Enter
DB.BeginTransaction
[Do sqlite things that do not directly or indirectly involve entering or leaving critical section]
DB.CommitTransaction
Catch oError as DatabaseExcpetion
DB.RollbackTransaction
Catch oError as RuntimeException
// We're about to crash out of this method, tidy up
CriticalSection.Leave
RaiseException oError // This prevents Finally and any code after this
Finally
CriticalSection.Leave
End Try
One should NEVERcatch ex as RuntimeException. If the code isn’t designed to handle a specific exception, the exception needs to bubble to UnhandledException and the app needs to terminate.
I’ve been trying to avoid plain old writing the code, to allow the reader to exercise their creativity, but this try...catch is specifically for Database code and DatabaseExceptions. In my opinion, that’s a cleaner, more straightforward design.
CriticalSection.Enter
try
DB.BeginTransaction
// Do database things
DB.CommitTransaction
catch ex as DatabaseException
DB.RollbackTransaction
end try
CriticalSection.Leave
This is functionally the same as a finally statement. The key difference is that this doesn’t “catch all” exceptions, because again, one should never do that.
That never calls leave if there is an unhandled exception. I deliberately dealt with the re-raising of the two exceptions that needs to bubble for app / thread termination. It bubbles all unhandled exceptions but tidies the Critical Exception too.
If there are no other exception handler blocks that would happen, just as it does now. Leaving the Critical section locked is likely going to hang your program.