Showing posts with label multithreading. Show all posts
Showing posts with label multithreading. Show all posts

Monday, July 09, 2007

Win32 HANDLEs

Each time when you use a win32 API which uses a HANDLE remember that the handle Must be valid (not closed).

It is important to understand the concepts of the handle being signaled and being closed.

Signaled state refers to the handle state. It can be signaled or unsignaled. Wait functions for example, like WaitForSingleObject, block current thread until the object gets signaled.

A handle is said to be closed when its reference count reaches zero value. When this happens the handle is closed and doing any waiting on it is error prone.

A handle is a pointer on an object. Wait functions check the state of the object, hence it is a must that the handler (the pointer) is valid. Otherwise, waiting functions wont work properly (while waiting on an invalid handle, you might wait indefinetly).

One example of bogus use is with MFC AfxBeginThread function. Its default behaviour is to auto 'delete' the thread, i.e to close its handle.
Its bad to use the returned handle after the thread is terminated and closed.
That its not a HANDLE. its a wild HANDLE.

This is described excellent in Chapter 12, Threads in the well known book by Jeff Prosise, Programming Windows with MFC, 2nd Edition.

Another missuse, without MFC, might be in an multithreaded application. One threads create a thread and share the handle others thread which use it.

Thursday, February 15, 2007

threading on GDI objects

When you dealing with multithreading in your code, every data you use in more than one thread it should be a subject to your attention in the way its used in those threads.

Similary, using GDI objects in multithreading does not require any special code beside some (natural) common-sense 'not to do' things, which is, dont read and modify un-synchronized in the same time. Also, remember that they have thread affinity (the thread which created the object should be the one who deletes it).

The following text is from the Raymond Chen wonderful blog:

Window objects: thread which created the window its said to be the window 'owner'.

Messages are dispatched to a window procedure only on the thread that owns it, and generally speaking, modifications to a window should be made only from the thread that owns it. Although the window manager permits any thread to access such things as window properties, styles, and other attributes such as the window procedure, and such accesses are thread safe from the window manager's point of view, load-modify-write sequences should typically be restricted to the owner thread. Otherwise you run into race conditions such as the following:

wpOld = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)newWndProc);
LRESULT CALLBACK newWndProc(...)
{
... CallWindowProc(wpOld, ...); ...
}
If modifications to the window procedure are made carelessly from any thread, then between the first two lines, a second thread may change the window procedure of the window, resulting in newWndProc passing the wrong "previous" window procedure to CallWindowProc.

Why, then, does Windows even allow a non-owner thread from changing the window procedure in the first place? Because, as we all know, 16-bit Windows was a co-operatively multi-tasked system, which means that one thread could do anything it wanted secure in the knowledge that no other thread would interrupt it until it explicitly relinquished control of the CPU. Therefore, the above code sequence was safe in 16-bit Windows. And for compatibility reasons, the code continues to be legal, even though it isn't safe any more. (Note, however, that in an attempt to limit the scope of the damage, the window manager allows only threads in the process that owns the window to change the window procedure. This is a reasonable limitation since separate address spaces mean that function addresses in other processes are meaningless in the process that owns the window anyway.)

Device contexts:

Device contexts (DCs) also have a certain degree of thread affinity. The thread that calls functions such as GetDC must also be the one that calls ReleaseDC, but as with window handles, during the lifetime of the DC, any thread can use it, but one at a time. If you choose to use a DC in a multi-threaded manner, it's your responsibility to coordinate the consumers of that device context so that only one thread uses it at a time. For example, to host windowless controls across multiple threads, the host obtains a DC on the host thread, then asks each control in sequence to draw itself into that DC. Only one control draws into the DC at a time, even if the control happens to be on a different thread.

Menus:

Menus do not have thread affinity. Any thread can use a menu. However, if two threads use a menu, it is the responsibility of those threads to coordinate among themselves how that menu will be used, so that one thread doesn't modify a menu while another is busy displaying it, for example.


Icons, cursors, and accelerator tables behave like menus. They do not have thread affinity. They are easier to manage than menus since they cannot be modified once created, so the only thing you have to worry about is not to use one after it has been destroyed

GDI objects are much simpler. As a general rule, they all have process affinity: They can be used by any thread in the process that created them. If you use a GDI object from multiple threads, it is your responsibility to coordinate the object's use.
Note that the window manager and GDI as a general rule keep their respective objects thread-safe. When I say that it is your responsibility to coordinate an object's use from multiple threads, I mean that you have to coordinate among your own threads if you're going to modify the object from one thread and read from it on another or modify it from two threads. For example, if one thread enumerates a menu while another is modifying it, the one doing the enumeration will get inconsistent results. Similarly, if two threads both try to change a menu item at the same time, the last writer will win.

http://blogs.msdn.com/479124.aspx

http://blogs.msdn.com/479587.aspx

http://blogs.msdn.com/480064.aspx

http://blogs.msdn.com/480569.aspx

http://blogs.msdn.com/481043.aspx

Monday, November 07, 2005

multithreading issue

ok, here is my task:
i have a dialog (main thread) and i have a worker thread running which updates a progressbar on the GUI.
when i press the close button i want the main thread and the worker thread to end cleanly (i want tthe threads to be closed nicely, without any TerminateThread or anything)

now, the problem stay in the fact that when i signal (using an event or something) the worker thread to end and then wait for it to end (using WaitForSingleObject for example) it is possible that in this time the worker thread to send a message to the GUI to update the progressbar(using SendMessage). Now, some may say why that SendMessage is a mistake but maybe not.

I want to be sure that the GUI and worker are synchronized perfectly (i dont want to use PostMessage; in some situations, for example when a buffer is needed to be sent, PostMessage only will not work (the buffer needs to be available when the message arrive to the receiver queue), hence, for PostMessage, some queue mechanism might be needed, if a text buffer for example is sent.

I came across a simple solution:
AtlWaitWithMessageLoop
its a function which use MsgWaitForMultipleObjects and does the job (in fact you can use your own implementation instead)

the idea is that, the main thread needs to be still getting messages for processing but in the same time, it will wait till the given synchronization object becomes invalid (beacause we may have 2 statements which can wait to eachother, a deadlock may appear; we can have the WaitForSingleObject(hWorkerThread); from main thread and the SendMessage from the workerthread)


//
//Stop the 'LoadLogs' thread, if its runnning
//
void CLogPage::StopLoadLogsThread()
{

if(m_hThreadLoadLogs)
{
//thread is accessing the log-file using CFile and it must be stopped before deleting the log-file
//stop the thread and wait till thread is stopped
InterlockedExchange(&m_snRunThread, 0);
AtlWaitWithMessageLoop(m_hThreadLoadLogs);
}

}