Multithreading in Meadow

This document contains considerations about if and how to use multithreading in Meadow. Currently it is maintained by Tim Enderling.

Could Meadow use multithreading?

This is a warrantable question, because Meadow has to be:

The first one is easy to achieve due to the use of SDL, which supports multi-threading. This is at least true for Linux, BSD, Win32 and BeOS but not for MacOS. To support MacOS there has to be an option by design not to use multithreading in Meadow and applications based on it. In addition one have to pay attention to the restrictions using SDL from separate threads. (I.e., never do it.)

Simplicity and stability are in case of multithreading incompatible goals. If you want to have stability you have to ensure that no conflicts in accessing the (internal and external) data occur. Therefore you have to use mutezes, semaphores and/or signals to synchronize all concurrent threads, which sledges down the simplicity. Since there is no alternative of being stable, simplicity has to be subordinated. Nevertheless you can keep the threading troubles low by strictly separating threading issues from the rest of the code. However this is not the most (runtime) efficient way.

Should Meadow use multithreading?

One can say: "Multithreading, what a neat feature! Why not including it in our library?" But most programmers I know would sigh "Is it really necessary? It is quite a lot of work and boosts up the complexity of the architecture." In fact it is. But seeing so many programs definitely needing and not using it I've sworn never to be to lazy to include this 'neat feature' in my own code.

The simple reason is, that one can become crazy from waiting for a calculation, a load or some other time intensive process not able to use the GUI anymore. Wouldn't it be fun, if one just could read some output, scroll back to some important messages or typing some input while the CPU scoops MBs of data through the memory? At least redrawing should work.

Next question is, if Meadow needs to spend an extra effort on additional thread management or just passing the functions provided by SDL to the application. The problem that appears when doing it the latter way is that there is no (at least no uncomplicated) way not to use C-like callbacks, which are wanted to be avoided in OOP. Otherwise we also could have used ParaGUI (however not really due to other lacks...). In conclusion Meadow is aimed to provide an easy and C++-like way of asynchronous processing to the application.

How should Meadow make use of multithreading?

For convenience it should be possible to process every event notification in an extra thread. I. e., the call of virtual overridables like OnMouseMove(), OnMouseDown(), OnDraw() should be done by a worker thread.

Another feature needing support is to present the state of a long lasting procedure. This can include something like percentage of completition in a progress line or simply a message in a status bar or changing an icon when it has finished.

Like data structures has to be locked in the program, components has to be switched to some kind of disabled state to avoid accidantelly fired event notifications. Although this should not be done for all controls and all events of the disabled controls. E.g. OnDraw() or OnScroll() should do their jobs all the time.

How can Meadow implement the desired behaviour?

Since it would be nonsense to create a new thread each time a worker thread is needed to execute a notification message an application should present a threading profile to Meadow at time of instantiation. This profile is not fixed and can be changed thereafter.

The threading profile consists of:

And here is how it works: Each thread has a message queue which is processed on the lowest level (the basic thread function). Via a mutex that is locked, if there are no new messages available, the thread waits and processes all events given. If some other (mostly the main) thread adds a new message, it unlocks the mutex (if not already unlocked). Due to the use of GUI object chains for processing input/other events the thread procedure has to check, wether the GUI object used up the event notification. If not it has to post a new event (possibly) in another thread. In C++ it could look the following:

static CMeadowThread::Work(void  pData)
  CMeadowThread  pThis = (CMeadowThread*)pData;

//mutex has been created in CMeadowThread::CMeadowThread()

BOOL bContinue = TRUE;

while (bContinue) //loop until MM_QUIT is received { //Wait if there is no message/Set to waiting state afterwards SDL_mutexP(pThis->m_mxNoMessage);

while (!pThis->m_quMsg.empty()) { //process all messages CMeadowMsg * pMsg = pThis->m_quMsg.front();

switch (pMsg->m_nType) { case MM_QUIT: //this is the signal to finish bContinue = FALSE; break;

case MM_DRAW: //example of a message

//if the message is not used up, forward it to the parent/next //GUI object in the chain if (!pMsg->m_pGUIObj->OnDraw((CDrawContext*)pMsg->m_Param1)) { pMsg->m_pGUIObj->GetParent()->ForwardMsg(pMsg); } break;

//... other messages

default: //custom message

//if the message is not used up, forward it to the parent/next //GUI object in the chain if (!pMsg->m_pGUIObj->OnCustom(pMsg->m_nType, pMsg->m_Param1)) { pMsg->m_pGUIObj->GetParent()->ForwardMsg(pMsg); } }

//test if some thread is waiting for the end of this if (pMsg->m_mxWaitFor != NULL) { //the thread can continue SDL_MutexV(pMsg->m_mxWaitFor); }

delete pMsg; pThis->m_quMsg.pop(); } } }

Concerning SDL calls from Meadow they all have to be wrapped. The wrapper function then has to take mutual possesion of the CPU by locking a mutex shared by all wrapper functions to make sure that no library call is interrupted by another one, which can result in indefinite behaviour.

How can applications based on Meadow make use of multithreading?

Besides providing a threading profile applications have to make use of per data-complex mutezes that protect properties of simultaneous access by multiple threads. Furthermore they should lock all events/whole controls not allowed to occur during processing. Functions fulfilling this purpose has to be provided by Meadow.

Since all calls to event notification handlers (e.g. OnDraw() on new data arrival) has to be done by sending messages instead of calling them directly, a function to wait for the calls return becomes useful. This function uses a per message mutex trying to lock it two times, so the second time it waits for the unlock from the end of message processing. The case of a call to the same thread has to get a special handling. (Otherwise its a deadlock.)

Problems of showing the progress are not considered to be hard to implement and are let to the application programmers.

What a plenty of code only to implement multithreading! Do we really need this?

Yes we do! Better to include it just from beginning rather than failing to do it afterwards.