utelle/wxsqlite3

Weird behaviour when using Open() on an encrypted database in a secondary thread (Multithreading)

Opened this issue · 8 comments

A weird behaviour happens when attempting to Open() a database on a secondary thread on Windows (Doesn't happen in Linux). The behaviour is that one would need to use "Open()" twice for the database to open, otherwise it would give "file is not a database[26]: file is not a database".

Another weird thing is that it doesn't matter whether the first Open() is used for the same database or another one.

I think this would reproduce the behaviour on MSVC++ (Using wxThread):

wxThread::ExitCode MyThread::Entry()
{
      // wxSQLite3Database *db; //I have this declared somewhere else, like MyThread class private section for example

        try
	{
		db = new wxSQLite3Database();
		
		wxSQLite3CipherAes256 cipher;
		cipher.InitializeFromGlobalDefault();
		cipher.SetLegacy(false);

		db->Open("mydb.db", cipher, "some key", WXSQLITE_OPEN_READONLY);
		
		wxSQLite3Table resultTable = db->GetTable("SELECT * FROM table;");
	}
	catch(wxSQLite3Exception &e)
	{
		printf("%s\n", e.GetMessage().ToUTF8().data());
		return 0;
	}
}

To fix it, just add an Open() before the existing one like this:

db->Open("mydb.db", cipher, "some key", WXSQLITE_OPEN_READONLY);
db->Open("mydb.db", cipher, "some key", WXSQLITE_OPEN_READONLY);

And I think attempting to open any other database with the same cipher type would fix it too.

Tried "WXSQLITE_OPEN_FULLMUTEX" and "WXSQLITE_OPEN_NOMUTEX", didn't work.

The exception is not getting thrown on Open() itself I guess, because I tried printing something after it and it did print, and also tried IsOpen(), and it returned true. The exception happens on GetTable().

Also, I think that it occurs if Open() is called an odd number of times, for example:

Open("db1"....) <= fails
Open("db2"....) <= works
Open("db3"....) <= fails

But I can't be sure, because it is a threading issue. I searched wxsqlite3.cpp for any global variables but didn't find, so it is weird that such a thing occurs when multithreading because nothing actually is happening in the main thread behind the scenes (Or is there?).

Or it might be a placebo and this problem is not related to multithreading but is MSVC++ related.

Which version of wxWidgets are you using? Which version of MSVC++ are you using?

Do all database files already exist prior to executing your application? Or does your application create them on the fly?

Do you have the same or another database open in the main thread? Was it already opened when the secondary thread tries to open a database? Or is the order of opening undetermined?

Could you please provide code for a minimal sample application which exhibits the observed behaviour? (You can't expect me to implement a multi-threading sample application from scratch to analyze the issue.)

3.1.2. Tried with 2008 and 2010 (Whole rebuild of wxMSW done).

Exist prior.

No other databases open in main thread, nor any call to any function inside wxsqlite3. No, the thread attempts to open it for the first time. The order is not undetermined.

Are there any extra steps I need to do if I am going to use wxSQLite3 in a multi-threaded application? Especially that the whole database management is going to be happening in a secondary thread?

Correction: The exception is thrown because of Open() and not GetTable, but I missed that because I had everything placed in one try{} block. Even though IsOpen() still returns true.

I am so sorry, this bug doesn't have to do with wxSQLite3 or SQLite3 in multithreading (AFAIK), but a glitch my app. Closing this issue.

3.1.2. Tried with 2008 and 2010 (Whole rebuild of wxMSW done).

I just wanted to make sure I use a similar environment for looking into this issue. However, I rarely use VS 2008 anymore.

No other databases open in main thread, nor any call to any function inside wxsqlite3. No, the thread attempts to open it for the first time. The order is not undetermined.

Ok. It's always important to know the boundary conditions.

Are there any extra steps I need to do if I am going to use wxSQLite3 in a multi-threaded application? Especially that the whole database management is going to be happening in a secondary thread?

I'm not aware of any special handling being necessary when using SQLite in a multi-threading environment.

Correction: The exception is thrown because of Open() and not GetTable, but I missed that because I had everything placed in one try{} block. Even though IsOpen() still returns true.

I really can't understand the latter: If IsOpen returns true, then Open can't have thrown an exception - unless your application has somehow corrupted the memory belonging to the wxSQLite3Database instance.

I am so sorry, this bug doesn't have to do with wxSQLite3 or SQLite3 in multithreading (AFAIK), but a glitch in MSVC++.

Please be a bit more specific if possible. Is this a known glitch in MSVC++? If yes, please give a reference to it. If no, are you just guessing? Or do you have any hard evidence?

NVM, a glitch in my app.

I really can't understand the latter: If IsOpen returns true, then Open can't have thrown an exception - unless your application has somehow corrupted the memory belonging to the wxSQLite3Database instance.

Yes I guess the glitch did cause memory corruption which was handled differently in each OS, thats why it didn't happen (Or its side effects didn't appear) in Linux.

Thanks for clarifying.