While working with MyQuery 2.4, which should be ready for beta real soon now, I have gotten myself into an issue with Windows and Multithreading. Actually, this issue exists to some extent most message based systems I guess, assuming they also support multithreading.
If you have not developed with Win16/Win32 or some other message based GUI system, then let me fill you in on how Windows does this. This to a large extent dates back to when Windows wasn't truly multi-threading at all (such as Win 3.0, Win 3.11 etc.). Even in those old versions of Windows, you could switch from application to application, window to window anyway. And the application didn't seem to be blocked (I am ignoring any general performance issues with those old versions of Windows here).
How did that work? It worked as the applications weren't just running away and handling all keyboard and mouse input all on it's own, and wasn't blocking when no action was needed. Instead, an application has a "message loop" that handles messages. If a message is to be handled, then it is handled, and then control is handed back to Windows itself, until I have another message meant for yours truly. OK Fine, this is a cheap way of "multithreading" which is also reasonably lightweight and works quite well.
What is not so good is that we, at times, need to deal with blocking operations. As long as I do not return from handling a message, the application is blocked now. And this goes for ALL parts of the application, GUI included! No repaints, nothing, dead. And there are some blocking operations we need to deal with here.
And not only that. Let's say we have a database connection that is shared by several objects, active at the same time, in your application. In my case, I have a bunch on modeless dialogs that show database data that share one connection. Usually, you would think that just putting a mutex on that connection would be enough: One Window does it's thing:
- Wait for the Mutex until I have it.
- While I have the Mutex, do my database things.
- Release the Mutex when the database thing is done.
In Windows, a modeless Dialog by default use the applications main message loop. If I remember correctly MFC (which I do not like, and I am not alone in this it seems) does this by using a smarter than usual message loop. I could have done that in my case I guess, but I decided on a different approach:
- Send an application defined message to do the database processing.
- Get the Mutex, but do not wait for it.
- If I got, then do the database processing and release the Mutex and reset the flag that I am waiting for a Mutex.
- If I didn't get it, set a flag that I am waiting to get it.
- If the flag that I am waiting for the mutex, then send the User defined message to myself.
Is this a good an accepted way of doing things? Maybe, at least it seems to work. I can have a script running in one window, and another window using the same database connection, all protected by a mutex, but none of the windows blocking. Next time around, I will probably figure out a way to massage the message loop so I can do this smarter, but for now, this works.
And this is example of how locking always comes back at you. In particular when multiple locking systems interact (like a GUI, a threading system and a database. Or a database that at one place locks the table, and at another place locks a row).
Back to MyQuery. And I will not blog more about it until 2.4 is done. I promise...