Sign in to follow this  
janta

Basic win32 thread Q: Suspend/Resume thread

Recommended Posts

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

Share this post


Link to post
Share on other sites
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 id
static 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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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).

Share this post


Link to post
Share on other sites
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.


Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this