Basic win32 thread Q: Suspend/Resume thread

Started by
8 comments, last by Shannon Barber 17 years, 10 months ago
Hey! As I was reading the docs about win32 threads, I found that it was possible to suspend and resume threads quite easilly calling SuspendThread and ResumeThread. Before I take a step further and try cooking something of my own, I wanted to know If someone would like to comment on the following outlines: I'll make a resource loader that will do it's job in a separate thread. Other thread will post messages to him using a thread-safe meassage queue. Those mesages will mainly consist of asking for loading/unloading file binary content into some structs, etc. And now my particular point: Instead of having the thread wait idle for some incoming message, could it possible that the thread sets itself into a suspended state, until another threads post a new message ? The thread would then resume, process the messages(s) and return to it's suspended state till next message. Do you think this is a valid approach ? (Just after I wrote this post I realised that MSDN doc mentions Suspend/Resume are not primarily meant for thread syncing. But I still would like some good folks opinions about it) Any advice on what to use instead / Any good resource or tutorial / any well-explained code sample are most welcome (I'm not looking for a librairy though) Thanks! Janta
Advertisement
Here's some crude sample code that might point to a possible solution. It's straight C. YMMV.

#include <windows.h>#include <tchar.h>// global thread idstatic DWORD g_tid = 0;// ---------------------------------------------------------------------------void _stdcall PrintError(LPTSTR szMsg, DWORD ErrorCode){    WCHAR *errMsg;    FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,                    NULL, ErrorCode,                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),                    (LPWSTR) &errMsg, 0, NULL );#ifdef UNICODE    LPWSTR szFmt = L"%s - %08X - %s"; // \n#else    LPSTR szFmt = "%s - %08X - %S"; // \n#endif    printf(szFmt, szMsg, ErrorCode, errMsg );    LocalFree( errMsg );}// ---------------------------------------------------------------------------BOOL WINAPI CtrlHandler(DWORD fdwCtrlType){   switch (fdwCtrlType)   {      // Handle the CTRL+C signal.      case CTRL_C_EVENT:      case CTRL_CLOSE_EVENT: // uncomment this to capture sysmenu close         if ( g_tid != 0 ) {            while ( !PostThreadMessage(g_tid,WM_QUIT,0,0) ) { Sleep(10); }         }         return TRUE;	// indicate handled      default:         return FALSE;      }}BOOL _stdcall InstallSignalHandler(PHANDLER_ROUTINE pfnCtrlHandler){   BOOL fSuccess = SetConsoleCtrlHandler(pfnCtrlHandler, TRUE);   if ( !fSuccess ) {      PrintError(_T("Error InstallSignalHandler\n"), GetLastError());   }   return fSuccess;}BOOL _stdcall UnInstallSignalHandler(PHANDLER_ROUTINE pfnCtrlHandler){   BOOL fSuccess = SetConsoleCtrlHandler(pfnCtrlHandler, FALSE);   if ( !fSuccess ) {      PrintError(_T("Error UnInstallSignalHandler\n"), GetLastError());   }   return fSuccess;}// ---------------------------------------------------------------------------void _stdcall SuperMsgLoop(void){   MSG msg;   // Start the message loop   while ( msg.message != WM_QUIT )   {      // Check if a message is waiting for processing      if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) // PM_REMOVE  | PM_QS_POSTMESSAGE)      {         // Change the format of certain messages         // TranslateMessage( &msg );         // Pass the message to WndProc() for processing         // messages not associated with a window can not be dispatched         // DispatchMessage( &msg );      }      else {         // do stuff      }   }}DWORD WINAPI ThreadFunction(LPVOID lpParameter){   // without this we can not exit msgloop   g_tid = GetCurrentThreadId();   SuperMsgLoop();   return 0;}// ---------------------------------------------------------------------------int main(void){   DWORD dwThread, exThread;   InstallSignalHandler(CtrlHandler);   // create the thread that launches the message loop   // CreateThreadEx   HANDLE hThread = CreateThread(NULL, (DWORD)NULL,            (LPTHREAD_START_ROUTINE)ThreadFunction,            (LPVOID) NULL, (DWORD)NULL, &dwThread);   // suspend main thread until new thread completes   WaitForSingleObject(hThread,INFINITE);   // examine create thread response   if ( !GetExitCodeThread(hThread, &exThread) ) {      PrintError(_T("GetExitCodeThread\n"),GetLastError());   }   // close thread handle   CloseHandle(hThread);   UnInstallSignalHandler(CtrlHandler);   return 0;}


It's probably better to just launch a thread to do the work as needed. Definitely easier.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
Quote:Original post by janta
Instead of having the thread wait idle for some incoming message, could it possible that the thread sets itself into a suspended state, until another threads post a new message


The basic problem with this approach is that suspend and create have to be matched and can only be called at "safe" times. e.g. imagine this, assume A is the primary thread and B is the resource thread:

A: Put something on the queue
A: ResumeThread(B)
B: Grab the queued item
B: Process the item
B: Check the queue, no items
A: Put something on the queue
A: ResumeThread(B)
B: SuspendThread(B)

Now you're in a state where the queue is not empty but the thread is still suspended. You could probably work around this but it would likely be more effort then just using an actual sycronization object in the first place. Plus calling ResumeThread on a thread that is not suspended is just nasty IMHO (although MSDN does seem to say that it is well defined what will happen).

The usual way to do this is to create an auto-reset event. The primary thread then calls SetEvent to wake up the resource thread and the resource thread uses WaitForSingleObject (or one of it's kin) to sleep whenever the queue is empty. There are also more esoteric methods such as QueueUserAPC but IMHO the event method is the simplest to understand.
-Mike
Two interesting blog entries you should read:
Why you should never call Suspend/TerminateThread (Part I)
Why you should never call Suspend/TerminateThread (Part II)
Arguing on the internet is like running in the Special Olympics: Even if you win, you're still retarded.[How To Ask Questions|STL Programmer's Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]
Thanks for the replies guys. I think that by what I'v read so far, i'v made up my mind and wont use Suspend/Resume for sync purpose.

However,a dead lock would have been less likely to occur since the thread would have put itself into a suspended state. Of course I'm neither saying that would prevent every bad situation from occuring, nor arguing on the fact that using S/R for sync is most obviously a bad thing :)

------
Now I got another question, and I think its best if I dont generate another thread (^^) again.

Basically (if I understand correctly) a class that has to share a resource with two or more thread has to share them in a thread safe fashion, that is, prevent them form doing certain things at the same time which would result in data corruption (among other bad things) I wonder to which extent does that holds (if it holds at all of course)
I mean, do I have to create a mutex lock/release I read/write a dingle integer in a class field ? I sure dont (I sure must not actually) but why is that ? is it because just reading a class member is some kind of atomic operation that another thread cannot interrupt ?

Basically (I know things dont happen that way but just to make a trivial example on what I mean), say Thread A wants to read the value of int m_var. could it be that A reads the first 16bits, at wich point a thread B comes in, put another value in m_var, than A reads the last 16bits and end up with a completely incorrect value ?

While I hope I'm not dumber than the average man, I'd still like to be sure to have a good understanding on how things happen before doing anything stupid. And more than anything I wanna learn :)

Cheers.
Janta
What you want is a simple request/service mechanism. This is the bread and butter of asynchronous threaded programming, and has rich support in the kernels of the world already.

You'll want a message queue (perhaps a pipe? or implement with an event and a critical section) to put messages to the servicing thread.

You'll want another way of posting notifications of completion back to the original thread (typically, use DPC-style notification for this -- or perhaps just post a message to some window).
enum Bool { True, False, FileNotFound };
You may want to have a look at ZThread, which is a platform-independent, open-source, threading library which supports many advanced multi-threading concepts such as scoped mutex locks, blocking queues, thread conditions, etc ... all wrapped up in a relatively clean C++ interface.
This might be preferable over having to implement your own synchronized queue, if you decide to go that route.


Thanks, I have browsed the source a little bit and it is indeed clear enough (for what I've seen). I'll probably be able to learn some things from that resource.

However I'm not using that lib, I'd rather have my own should it be bad or anything. I'm just doing that at home after work, I report to no one and no one depends on my work, so I better take some time, make mistakes and learn from them :) I'll be sure to post a sample of my sourcecode to get your feedback some day.
Hey, that's cool .... I did my own thing, too, before I started looking for premanufactured solutions. It's definitely a good way to learn more.

If you want you can take a look at my own little threading library too.

http://www.geocities.com/red_ant_11/Utils.zip

The threading stuff is all under Utils/Threading. I'm not using it anymore since I've discovered ZThread, but it has served me well for a year.
The standard Windows message pump will do what you want. GetMessage blocks until a message is sent to the thread. You might still need additional mutex/critical-section syncronization to access shared data.
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara

This topic is closed to new replies.

Advertisement