Hey i have a UserControl that kept crashing my Visual Studio.
So i ran another instance of VS and debugged the other VS and this is what i figured:
Collection was modified after the enumerator was instantiated.
Here is my array:
private static Color[] colors =
{
Color.FromArgb(155, 188, 255), // 40000
Color.FromArgb(156, 189, 255), // 39500
Color.FromArgb(157, 188, 255), // 39000
Color.FromArgb(156, 189, 254), // 38500
};
And here is my loop that crashes the bussines
public Heater()
{
InitializeComponent();
this.tarTemp = this.curTemp;
new Thread(() => UpdateTemp(true)).Start();
}
private delegate void UpdateTempDelegate(bool loop);
private void UpdateTemp(bool loop)
{
if (lblTemp.InvokeRequired)
{
UpdateTempDelegate del = new UpdateTempDelegate(UpdateTemp);
lblTemp.Invoke(del, loop);
}
else
{
do
{
lblTemp.Text = curTemp + C;
if (curTemp >= 0)
{
int i = curTemp - 10;
if (i < 0)
i = 0;
if (i > colors.Length - 1)
i = colors.Length - 1;
this.BackColor = colors[i]; // I'M CRASHING !!!
}
} while (loop && !this.Disposing);
}
}
The line that crashes the Visual Studio Designer is this.BackColor = colors[i];
Here is the image of the running Threads:
All threads stopped on the same line... this.BackColor = colors[i];
Here is the EventViewer crash log:
Application: devenv.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.InvalidOperationException
Stack:
at System.ThrowHelper.ThrowInvalidOperationException(System.ExceptionResource)
at System.Collections.Generic.SortedList`2+SortedListValueEnumerator[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].MoveNext()
at Microsoft.VisualStudio.Shell.ServiceProviderHierarchy.GetService(System.Type)
at System.ComponentModel.Design.ServiceContainer.GetService(System.Type)
at System.ComponentModel.Design.DesignerHost.GetService(System.Type)
at System.ComponentModel.Design.DesignerHost+Site.System.IServiceProvider.GetService(System.Type)
at System.Windows.Forms.Control.get_AmbientPropertiesService()
at System.Windows.Forms.Control.get_BackColor()
at System.Windows.Forms.Control.set_BackColor(System.Drawing.Color)
at Multiplier.Heater.UpdateTemp(Boolean)
at Multiplier.Heater.<.ctor>b__0()
at System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
at System.Threading.ThreadHelper.ThreadStart()
This is the weirdest thing i encountered so far.
Help whould be appriciated.
As you found out, your code is crashing the designer, taking VS down with it. The problem is that you start a thread in design mode, triggered by the designer running some of your code at design time. It for example will run the constructor, the Load event, OnHandleCreated, etcetera. That makes for a very nice design-time experience, your control will look just like it does at runtime.
But that can also cause plenty of problems. You have to avoid running code that may cause an exception when it runs in a different execution context. Classic examples are trying to open a file without specifying the full path, opening a dbase connection with the dbase server offline or unreachable. And definitely starting a thread, InvokeRequired is not going to reliably work as the designer constructs and destroys the native window handle. The fix is simple:
public Heater()
{
InitializeComponent();
this.tarTemp = this.curTemp;
if (!this.DesignMode) {
new Thread(() => UpdateTemp(true)).Start();
}
}
You'll need to do more work, this code won't work well at runtime either. The threaded code will bomb when the form on which the user control is placed is closed. Once you fix that, odds are good that it now works correctly at design-time as well. But don't.
Are you modifying the collection using any other code? Usually this happens when you are enumerating a collection in a loop and you try to modify the collection.
Related
I'm experiencing a rather strange hang with an indefinitely climbing memory leak when calling OpenFileDialog.ShowDialog (.NET Framework 4.8). As shown in the animated gif/video (apologies for the lack of editing), there seems to be no noteworthy change in the snapshots regardless of how long I wait - even when it reaches multiple allocated gigabytes. Am I missing something regarding how to use the Memory Usage snapshot diagnostic tool?
The initial folder it tries to access doesn't have very many files and opening that same folder using FileDialog boxes in other applications (such as Visual Studio 2019) doesn't result in any problems.
There is no funky timer code anywhere in the application, it does run tasks (as shown below) to load the data in the background for a more responsive UI, however the dialog is intentionally loaded in a section of code where the logic path is guaranteed to be running on the UI thread to simplify matters. The same code worked fine for years, before, the only code I've added since this odd behaviour began is to add report printing functionality in an unrelated section of code.
Any ideas? Thanks.
Progress update:
I have only one Shell extension (WinMerge), it's been working fine for years.
I've tested the program in a VM and it works exactly as intended.
I've downloaded the debug PDB symbols using the Microsoft and NuGet.org symbol servers, and the leak is definitely happening in unmanaged Windows code - the memory usage tool is referring to the leaked objects as an endlessly growing list of opaque (void*) memory addresses and values within an "Object Type" named "unresolved allocations", unfortunately. A sample callstack is as follows:
ntdll.dll!NtTraceEvent()
ntdll.dll!RtlpLogHeapFreeEvent()
ntdll.dll!RtlpFreeHeap()
ntdll.dll!RtlpFreeHeapInternal()
ntdll.dll!RtlFreeHeap()
ntdll.dll!RtlDebugFreeHeap()
ntdll.dll!RtlpFreeHeap()
ntdll.dll!RtlpFreeHeapInternal()
ntdll.dll!RtlFreeHeap()
KernelBase.dll!LocalFree()
ExplorerFrame.dll!CTContainer_PolicyLocalMem::DestroyMem(void *)
ExplorerFrame.dll!TabletModeHelpers::GetMonitorConfig(int *, bool *)
ExplorerFrame.dll!SHIsFileExplorerInTabletMode(void)
ExplorerFrame.dll!UICheckbox::OnHosted()
dui70.dll!DirectUI::Element::OnHosted()
ExplorerFrame.dll!UIItem::OnHosted(class DirectUI::Element *)
dui70.dll!DirectUI::Element::OnPropertyChanged()
ExplorerFrame.dll!UIItem::OnPropertyChanged()
dui70.dll!DirectUI::Element::_PostSourceChange()
dui70.dll!DirectUI::Element::Insert()
ExplorerFrame.dll!UICollection::_CreateAndInsertItems()
ExplorerFrame.dll!UICollection::_RealizeItems()
ExplorerFrame.dll!UICollection::RealizeSection(class SectionInfo *)
ExplorerFrame.dll!LineScroller::_RealizeDirectionWorker()
ExplorerFrame.dll!LineScroller::_RealizeDirectionWorker()
ExplorerFrame.dll!LineScroller::_RealizeDirectionWorker()
ExplorerFrame.dll!LineScroller::_RealizeAroundAnchor()
ExplorerFrame.dll!LineScroller::_RealizeContent()
ExplorerFrame.dll!LineScroller::_LayoutContent()
ExplorerFrame.dll!LineScroller::_Viewer_SelfLayoutDoLayout(int,int)
dui70.dll!DirectUI::Element::_FlushLayout()
dui70.dll!DirectUI::Element::_FlushLayout()
dui70.dll!DirectUI::Element::_FlushLayout()
dui70.dll!DirectUI::Element::_FlushLayout()
dui70.dll!DirectUI::Element::_FlushLayout()
dui70.dll!DirectUI::DeferCycle::_EndDefer()
dui70.dll!DirectUI::Element::EndDefer()
dui70.dll!DirectUI::Element::_PostSourceChange()
dui70.dll!DirectUI::Element::_SetValue()
dui70.dll!DirectUI::Element::SetValue(struct DirectUI::PropertyInfo const * (*)(void),int,class DirectUI::Value *)
ExplorerFrame.dll!LineViewer::SetLayoutState(int)
ExplorerFrame.dll!LineScroller::_EnsureRealizePass(void)
ExplorerFrame.dll!LineScroller::_OnLayoutChangeEvent(struct LayoutChangeEvent *)
ExplorerFrame.dll!LineScroller::OnEvent()
dui70.dll!DirectUI::Element::s_HandleDUIEventMessage(class DirectUI::Element *,struct EventMsg *)
dui70.dll!DirectUI::Element::_DisplayNodeCallback()
duser.dll!GPCB::xwInvokeDirect()
duser.dll!SafeMsgQ::xwProcessNL()
duser.dll!SafeMsgQ::xwProcessNL(void)
duser.dll!CoreSC::xwProcessMsgQNL(void)
duser.dll!CoreSC::xwProcessNL()
duser.dll!MphProcessMessage()
user32.dll!__ClientGetMessageMPH()
ntdll.dll!KiUserCallbackDispatcherContinue()
win32u.dll!NtUserPeekMessage()
user32.dll!_PeekMessage()
user32.dll!DialogBox2()
user32.dll!InternalDialogBox()
user32.dll!DialogBoxIndirectParamAorW()
user32.dll!DialogBoxIndirectParamW()
comdlg32.dll!<lambda>(void)()
comdlg32.dll!CFileOpenSave::Show(struct HWND__*)
[Managed to Native Transition]
System.Windows.Forms.dll!System.Windows.Forms.FileDialog.RunDialogVista(System.IntPtr hWndOwner)
System.Windows.Forms.dll!System.Windows.Forms.CommonDialog.ShowDialog(System.Windows.Forms.IWin32Window owner)
Bingo.exe!OPEQ.frmBingo.tsmiFileOpen_Click(object sender, System.EventArgs e) Line 1098
private void tsmiFileOpen_Click(object sender, EventArgs e)
{
Logging.Method();
//Gets the filter description retrieval task for the dialog
//box which was begun at program launch (if this fails or has
//yet to complete, the initial value is used instead).
CultureInfo uiCulture = Thread.CurrentThread.CurrentUICulture;
TaskParams openDialogFilterTaskParams;
if (_tasks.TryGetValue(_fileDialogFilterTaskKey, out openDialogFilterTaskParams))
{
bool removeFromTasks = false;
//If we can, get the result, otherwise use the existing filter.
switch (openDialogFilterTaskParams.Task.Status)
{
case TaskStatus.RanToCompletion:
//Update the filter variable.
var odfTask = (Task<IDictionary<CultureInfo, FileDialogFilterList>>)openDialogFilterTaskParams.Task;
foreach (var kvPair in odfTask.Result)
{
//If any GetFileTypeName call fails, the resulting
//description is null, so we need to
//account for that here.
var updatedList = ReplaceNullDescriptions(kvPair.Value, kvPair.Key);
_openDialogFilterDict[kvPair.Key] = updatedList.CopyEmbellished().ToString();
}
removeFromTasks = true;
break;
case TaskStatus.Canceled:
case TaskStatus.Faulted:
removeFromTasks = true;
//Use the filter's initial value.
break;
default:
//Use the filter's initial value.
break;
}
if (removeFromTasks)
{
try
{
//The Wait() here is intentional to avoid
//making this event handler async.
openDialogFilterTaskParams.Task.Wait(openDialogFilterTaskParams.TokenSource.Token);
}
catch(OperationCanceledException)
{
//Swallow cancellation.
}
catch (AggregateException aggEx) when (aggEx.InnerExceptions.Any(ex => !(ex is OperationCanceledException)))
{
//Log and swallow the exception(s).
Logging.Exception(aggEx,
isRecoverableError: true,
includeStackTrace: true);
}
finally
{
openDialogFilterTaskParams.Task.Dispose();
openDialogFilterTaskParams.TokenSource.Dispose();
_tasks.TryRemove(_fileDialogFilterTaskKey, out _);
}
}
}
//Dialog box guarantees the user-specified file exists.
//No need to handle initial directory, it handles that on its own.
openFileDialog1.Filter = _openDialogFilterDict[uiCulture];
openFileDialog1.DefaultExt = _openDialogDefaultExtension;
//Program hangs on this next line.
if (openFileDialog1.ShowDialog(this) == DialogResult.OK)
{
//Handle the creation, dictionary addition and retrieval as one atomic operation.
var fileLoadTaskParams = _tasks.GetOrAdd(_fileLoadingTaskKey, (_, fnames) =>
{
TaskParams tParam = new TaskParams(
null,
null,
new Progress<ReportLoaderProgressChangedEventArgs>());
try
{
tParam.TokenSource = CancellationTokenSource.CreateLinkedTokenSource(_tidyShutdownCts.Token);
tParam.Task = new ReportLoader(fnames).LoadAsync(tParam.TokenSource.Token, (IProgress<ReportLoaderProgressChangedEventArgs>)tParam.Additional);
}
catch
{
tParam.TokenSource?.Dispose();
tParam.Task?.Dispose();
throw;
}
return tParam;
}, openFileDialog1.FileNames.AsEnumerable());
//Set the default scan-type mode
//(Search if multiple files were loaded,
//Verification if only one).
_scanModeSelected = openFileDialog1.FileNames.Length > 1
? tsBtnScanType_Search
: tsBtnScanType_Verification;
/* Hand off the contents of the retrieved tuple to
* LoadFile(...). This means LoadFile handles the display
* of any non-critical exceptions to the user, anything
* dangerous will bubble out of it and into FireAndForget at
* which point the application is allowed to die.*/
LoadFile(
(Task<OPEQReportDataSet>)fileLoadTaskParams.Task,
fileLoadTaskParams.TokenSource,
(Progress<ReportLoaderProgressChangedEventArgs>)fileLoadTaskParams.Additional)
.FireAndForget(null, null, () =>
{
fileLoadTaskParams.Task.Dispose();
fileLoadTaskParams.TokenSource?.Dispose();
_tasks.TryRemove(_fileLoadingTaskKey, out _);
});
} //if (openFileDialog1.ShowDialog(this) == DialogResult.OK)
}
2nd Progress Update:
...and it's now working again without explanation. I guess it was indeed a Windows Update. (?!)
I'm building simple win phone 8.1 app which using geofence api and background tasks for controlling, when user enter/leaves some area.
To register background task i implement RegisterBackgroundTask method in App class
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.Suspending += this.OnSuspending;
this.RegisterBackgroundTask();
}
private async void RegisterBackgroundTask()
{
const string name = "GeofenceBackgroundTask";
if (BackgroundTaskRegistration.AllTasks.Any(task => task.Value.Name == name))
{
return;
}
var loc = await new Geolocator().GetGeopositionAsync(
TimeSpan.FromMinutes(2),
TimeSpan.FromSeconds(5)); //needed to trig user acceptance
var backgroundAccessStatus =
await BackgroundExecutionManager.RequestAccessAsync();
if (backgroundAccessStatus != BackgroundAccessStatus.Denied)
{
var geofenceTaskBuilder = new BackgroundTaskBuilder()
{
Name = name,
TaskEntryPoint = "RingtoneManager.Background.GeofenceBackgroundTask"
};
geofenceTaskBuilder.SetTrigger(new LocationTrigger(LocationTriggerType.Geofence));
geofenceTaskBuilder.Register();
}
}
And this is the part, which raise the exception
new LocationTrigger(LocationTriggerType.Geofence)
Exception details:
System.InvalidCastException was unhandled by user code
HResult=-2147467262
Message=Unable to cast object of type 'System.__ComObject' to type 'Windows.ApplicationModel.Background.ILocationTriggerFactory'.
Source=mscorlib
StackTrace:
at System.StubHelpers.StubHelpers.GetCOMIPFromRCW_WinRT(Object objSrc, IntPtr pCPCMD, IntPtr& ppTarget)
at Windows.ApplicationModel.Background.LocationTrigger..ctor(LocationTriggerType triggerType)
at RingtoneManager3.App.<RegisterBackgroundTask>d__2.MoveNext()
What i have figure out so far:
Exception code is 80004002 (E_NOINTERFACE)
I've investigate what is this interface and found, that it declared in C:\Program Files (x86)\Windows Phone Kits\8.1\References\CommonConfiguration\Neutral\Windows.winmd and its actually there
And its referenced from visual studio project
Every other triggers (SystemTrigger, MaintenanceTrigger e.t.c) are instantiating fine
What i have already tried:
Reinstall VS
Clean/Rebuild solution
Annotating method RegisterBackgroundTask with [STAThread]
This is my first app on windows phone and on c#, so i may do some stupid mistakes in common place. I'm also dont understand how visual studio process those references from solution and how interfaces, that coded in referenced .winmd files, became available to code in project. Maybe there is where something goes wrong. So i need help in searching the root of problem and finding the solution.
Thank you in advance
It could be that "LocationTrigger" is a singleton? (sorry, don't know phone) and has (already) been activated elsewhere with a generic System.__ComObject RCW. If this is the case it cannot be cast. Try using Activator.CreateInstance instead.
Type t = Type.GetTypeFromProgID("WhateverTheProgIdIsHere");
System.Object obj = Activator.CreateInstance(t);
ILocationTriggerFactory tf = obj as ILocationTriggerFactory;
My application is running on Windows Embedded Standard 7 and launches when the OS boots up.
Sometimes on the first load, I will get an Unknown Hard Error, and after checking the Event Viewer, I see a message of
The application requested process termination through System.Environment.FailFast(string message).
Message: Unrecoverable system error.
Needless to say, I of course have no calls to this function. I only seem to see this happen on Windows Embedded, and haven't seen this reproduced on a standard install of Windows.
I'm unsure of how to diagnose this or what 'fix' would be appropriate as I don't really know why it happens.
Edit:
The entire log in Event Viewer:
Application: WinForm.exe
Framework Version: v4.0.30319
Description: The application requested process termination through System.Environment.FailFast(string message).
Message: Unrecoverable system error.
Stack:
at System.Environment.FailFast(System.String)
at MS.Internal.Invariant.FailFast(System.String, System.String)
at System.IO.Packaging.Package.AddIfNoPrefixCollisionDetected(ValidatedPartUri,
System.IO.Packaging.PackagePart) at System.IO.Packaging.Package.GetPartHelper(System.Uri)
at System.IO.Packaging.Package.GetPart(System.Uri)
at System.Windows.Application.GetResourceOrContentPart(System.Uri)
at System.Windows.Application.LoadComponent(System.Object, System.Uri)
at Pms.PmControl.InitializeComponent()
at Pms.PmControl..ctor(Boolean)
at Pms.PmAppControl.StartWpfThread()
at System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
at System.Threading.ThreadHelper.ThreadStart()
If you look at the code with a decompiler you will find
// System.IO.Packaging.Package
private void AddIfNoPrefixCollisionDetected(PackUriHelper.ValidatedPartUri partUri, PackagePart part)
{
this._partList.Add(partUri, part);
int num = this._partList.IndexOfKey(partUri);
Invariant.Assert(num >= 0, "Given uri must be present in the dictionary");**
string normalizedPartUriString = partUri.NormalizedPartUriString;
string text = null;
string text2 = null;
if (num > 0)
{
text = this._partList.Keys[num - 1].NormalizedPartUriString;
}
if (num < this._partList.Count - 1)
{
text2 = this._partList.Keys[num + 1].NormalizedPartUriString;
}
if ((text != null && normalizedPartUriString.StartsWith(text, StringComparison.Ordinal) && normalizedPartUriString.Length > text.Length && normalizedPartUriString[text.Length] == PackUriHelper.ForwardSlashChar) || (text2 != null && text2.StartsWith(normalizedPartUriString, StringComparison.Ordinal) && text2.Length > normalizedPartUriString.Length && text2[normalizedPartUriString.Length] == PackUriHelper.ForwardSlashChar))
{
this._partList.Remove(partUri);
throw new InvalidOperationException(SR.Get("PartNamePrefixExists"));
}
}
The code fails at the assert because that is the only method which calls FailFast
internal static void Assert(bool condition, string invariantMessage)
{
if (!condition)
{
Invariant.FailFast(invariantMessage, null);
}
}
Now the question remains why you package could not be found in the PartList array. WPF fills some things out for you. Could it be that you reference from your XAML some resource via an internet addresses or a network share which could fail if the network subsystem of your Windows embedded is not yet ready?
In my Windows 8.1 (WinRT) app I am using SQLite v 3.8.9 with SQLite-net as my database, and SemaphoreSlim as my synchronization enforcer. It usually works, but sometimes it crashes in the SQLite's C++ code when I try to delete a table entry.
First-chance exception at (code) in (project name): Microsoft C++ exception: _com_error at memory location (location).
Delete table entry
private static SemaphoreSlim _mutex = new SemaphoreSlim(1,5);
public void DeleteItem(Item item)
{
_mutex.Wait();
using (var connection = new SQLiteConnection(Path))
{
connection.Delete(item);
}
_mutex.Release();
}
SQLite.cs
public SQLiteConnection (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = false)
{
...
#if NETFX_CORE
SQLite3.SetDirectory(/*temp directory type*/2, Windows.Storage.ApplicationData.Current.TemporaryFolder.Path);
#endif
...
}
The crash happens when SQLite3.SetDirectory is called.
Exception:
System.AccessViolationException
Message:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Stacktrace:
at SQLite.SQLite3.SetDirectory(UInt32 directoryType, String directoryPath)
at SQLite.SQLiteConnection..ctor(String databasePath, SQLiteOpenFlags openFlags, Boolean storeDateTimeAsTicks)
at SQLite.SQLiteConnection..ctor(String databasePath, Boolean storeDateTimeAsTicks)
Question
I am guessing that this must be a Threading issue because it usually works and has irregular crashes; but I am unable to find anything.
What can be the cause of the exception and what can I do to fix it?
I do not think it is corrupted memory, maybe protected, but I am fairly sure that only one of my threads are accessing it
In the end, even though I was confident that only one thread was accessing the SQLite db at a time, it turned out that I had some minor code that I didn't expect was called at the same time; this caused the AccessViolationException
To ensure that this problem does not arise in the future, I did the following:
Moved the whole SQLite code to a different project in an attempt to isolate it.
Implemented a semi-factory pattern in the project to ensure that only one thread uses it (this also made the code look better)
Factory Database class
public class DataBaseConnection: SQLite.SQLiteConnection
{
private const string _path = "MySQlitePath";
private static SemaphoreSlim _contextMutex = new SemaphoreSlim(1, 5);
private DataBaseConnection() : base(_path)
{
}
public static DataBaseConnection CreateDataBaseConnection()
{
_contextMutex.Wait();
return new DataBaseConnection();
}
private bool disposed = false;
protected override void Dispose(bool disposing)
{
if (disposed)
return;
if(disposing)
{
}
disposed = true;
base.Dispose(disposing);
_contextMutex.Release();
}
}
Using the factory
using (var connection = DataBaseConnection.CreateDataBaseConnection())
{
connection.Delete(item);
}
Since using this, I've never seen the AccessViolationException again.
Using Multiple DB connections
If you are using Multiple Connections (i.e. different DB paths) such as:
MySQlitePath_1
MySQlitePath_2
...
MySQlitePath_N
You should use the same Synchronization Mechanism (in my case SemaphoreSlim) throughout to ensure only one SQLite Connection is open at any given time; even though they are accessing different files.
When adding or removing a job to/from the scheduler, Quartz sporadically throws a JobPersistenceException (following a preceding SQLiteException).
Things that seem noteworthy:
Quartz.NET 2.01 + System.Data.SQLite 1.0.66 (both latest version at time of writing just noticed that there is a binary package for SQLite 1.0.82 available)
the exception is also thrown if there is currently no job/trigger beeing executed (i am monitoring the Quartz listeners)
the Jobs are manually added from UI context (i require about 10-20 repetitions to cause the error but it seems totally random)
Everything seems to be running fine (multiple jobs, parallel execution, persistance after application restart) as long as i don't touch AddJob()/DeleteJob() After extended testing i am sure its not related to adding/removing jobs. The db locking/access problems are a general problem.
Is there any recommended procedure i am not aware that must be followed when adding/deleting jobs?
Is there anything wrong with my ISchedulerFactory configuration? (see below)
Supplemental
I tried using System.Data.SQLite 1.0.82 which made things worse. I get "SQLite error (5): database is locked" almost constantly as soon as Quartz is executing a Job.
Quartz.NET list System.Data.SQLite 1.0.56 as supported db provider so one might expect problems using a newer version. However, i don't consider going back from 1.0.66 as an option since there were lots of improvements/fixes IIRC.
I took a look at the development trunk of Quartz.NET between the 2.0.1 release revision (624) and current head revision (669). There seem to be no related fixes.
I suspect that its a System.Data.SQLite issue. I stumbled over several posts (concerning different SQLite versions) mentioning that there might be some issues with internal disposing of resources, keeping the DB file locked.
Supplemental 2
For now, I gave up on this. I tried a lot of things, but development has to go on. I switched to another database type (Firebird) which so far seems to work fine with Quartz.
If somebody gets this working i would love to hear about it anyway.
-
Exception details:
Quartz.JobPersistenceException: "Couldn't commit ADO.NET transaction. The database file is locked\r\ndatabase is locked"
Stack
bei Quartz.Impl.AdoJobStore.JobStoreSupport.CommitConnection(ConnectionAndTransactionHolder cth, Boolean openNewTransaction)
bei Quartz.Impl.AdoJobStore.JobStoreSupport.ExecuteInNonManagedTXLock(String lockName, Func`2 txCallback)
bei Quartz.Impl.AdoJobStore.JobStoreTX.ExecuteInLock(String lockName, Func`2 txCallback)
bei Quartz.Impl.AdoJobStore.JobStoreSupport.RemoveJob(JobKey jobKey)
bei Quartz.Core.QuartzScheduler.DeleteJob(JobKey jobKey)
bei Quartz.Impl.StdScheduler.DeleteJob(JobKey jobKey)
InnerException SQLiteException: "The database file is locked\r\ndatabase is locked"
Stack
bei System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
bei System.Data.SQLite.SQLiteDataReader.NextResult()
bei System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
bei System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
bei System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
bei System.Data.SQLite.SQLiteTransaction.Commit()
bei Quartz.Impl.AdoJobStore.JobStoreSupport.CommitConnection(ConnectionAndTransactionHolder cth, Boolean openNewTransaction)
The source of the exception is "cth.Transaction.Commit();" in this Quartz.NET method.
/// <summary>
/// Commit the supplied connection.
/// </summary>
/// <param name="cth">The CTH.</param>
/// <param name="openNewTransaction">if set to <c>true</c> opens a new transaction.</param>
/// <throws>JobPersistenceException thrown if a SQLException occurs when the </throws>
protected virtual void CommitConnection(ConnectionAndTransactionHolder cth, bool openNewTransaction)
{
CheckNotZombied(cth);
if (cth.Transaction != null)
{
try
{
IsolationLevel il = cth.Transaction.IsolationLevel;
cth.Transaction.Commit();
if (openNewTransaction)
{
// open new transaction to go with
cth.Transaction = cth.Connection.BeginTransaction(il);
}
}
catch (Exception e)
{
throw new JobPersistenceException("Couldn't commit ADO.NET transaction. " + e.Message, e);
}
}
}
This is how i create the ISchedulerFactory:
public static ISchedulerFactory CreateSQLiteSchedFactory(SQLiteConnection sqlConn, string tablePrefix) {
// db provider hinzufügen
var metaData = new DbMetadata();
metaData.AssemblyName = "System.Data.SQLite,Version=1.0.66.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139";
metaData.BindByName = true;
metaData.CommandBuilderType = typeof(SQLiteCommandBuilder);
metaData.CommandType = typeof(SQLiteCommand);
metaData.ConnectionType = typeof(SQLiteConnection);
metaData.ExceptionType = typeof(SQLiteException);
metaData.ParameterDbType = typeof(TypeAffinity);
metaData.ParameterDbTypePropertyName = "DbType";
metaData.ParameterNamePrefix = "#";
metaData.ParameterType = typeof(SQLiteParameter);
metaData.UseParameterNamePrefixInParameterCollection = true;
DbProvider.RegisterDbMetadata("SQLite-1066", metaData);
// konfiguration für factory erstellen
NameValueCollection properties = new NameValueCollection();
properties["quartz.scheduler.instanceName"] = "TestScheduler";
properties["quartz.scheduler.instanceId"] = "instance_one";
properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
properties["quartz.threadPool.threadCount"] = "5";
properties["quartz.threadPool.threadPriority"] = "Normal";
properties["quartz.jobStore.misfireThreshold"] = "60000";
properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";
properties["quartz.jobStore.useProperties"] = "false";
properties["quartz.jobStore.dataSource"] = "default";
properties["quartz.jobStore.tablePrefix"] = tablePrefix;
properties["quartz.jobStore.clustered"] = "true";
properties["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz";
properties["quartz.dataSource.default.connectionString"] = sqlConn.ConnectionString;
properties["quartz.dataSource.default.provider"] = "SQLite-1066";
// factory erzeugen
return new StdSchedulerFactory(properties);
}
The SQLiteConnection is created with connectionstring similar to "Data Source=c:\mydb.db;Version=3;" and all the quartz tables are initialized using the supplied SQL script
You have to set this one to true in the properties:
properties["quartz.jobStore.txIsolationLevelSerializable"] = "true";
The cause of this error is most likely to be because of multiple concurrent writes on the SQLite db, sqlite can accept multiple read-only connections only, but the can't accept simultaneous writes!
http://www.sqlite.org/faq.html#q5