Synchronizing two threads - winapi - c++

Program below is a synchronization between two threads using a Mutex.
It compiles, works and prints what I want in order(alternating R/W for the 2 threads), but it crashes after it's done. Any idea why?
I think it has to do with closing TName handle, if I comment that part it doesn't crash, but I'd like to close opened handles.
HANDLE hMutex, hWriteDone, hReadDone;
int num, state;
void Writer()
{
for(int x=10; x>=0; x--)
{
while (true)
{
if (WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED)
{
std::cout<<"In writing loop, no mutex!\n";
ExitThread(0);
}
if (state == 0)
{
ReleaseMutex(hMutex);
WaitForSingleObject(hReadDone, INFINITE);
continue;
}
break;
}
std::cout<<"Write done\n";
num= x;
state= 0;
ReleaseMutex(hMutex);
PulseEvent(hWriteDone);
}
}
void Reader()
{
while(true)
{
if (WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED)
{
std::cout<<"In reader, no mutex!\n";
ExitThread(0);
}
if (state == 1)
{
ReleaseMutex(hMutex);
WaitForSingleObject(hWriteDone, INFINITE);
continue;
}
if (num == 0)
{
std::cout<<"End of data\n";
ReleaseMutex(hMutex);
ExitThread(0);
}
else {
std::cout<<"Read done\n";
state=1;
ReleaseMutex(hMutex);
PulseEvent(hReadDone);
}
}
}
void main()
{
HANDLE TName[2];
DWORD ThreadID;
state= 1;
hMutex= CreateMutex(NULL, FALSE, NULL);
hWriteDone= CreateEvent(NULL, TRUE, FALSE, NULL);
hReadDone= CreateEvent(NULL, TRUE, FALSE, NULL);
TName[0]= CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)Writer,
NULL, 0, &ThreadID);
TName[1]= CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)Reader,
NULL, 0, &ThreadID);
WaitForMultipleObjects(2, TName, TRUE, INFINITE);
CloseHandle(TName);
getchar();
}

You should never cast a function pointer. Remove the (LPTHREAD_START_ROUTINE) casts from your code, fix the compiler errors, and try again. Never use casts to quell compiler errors.

The lpStartAddress parameter of CreateThread is of type LPTHREAD_START_ROUTINE. Which is a function pointer compatible with this signature:
DWORD WINAPI ThreadProc(LPVOID lpParameter);
So you need to supply what the function expects. Your function Reader does not fit the bill. Change its signature to be like this:
DWORD WINAPI Reader(LPVOID lpParameter)
{
....
}
And likewise for Writer.
Every time you cast something to suppress a compiler warning you are trading an easy to diagnose compile time error for a hard to diagnose run time error. That's a very bad trade. So, as a general rule, don't use casts. Sometimes you'll need to break that rule, but do so in full understanding of what you are doing.
Your main function also has a somewhat bogus signature. If you don't want to process arguments, then you should declare it like this:
int main()
Since you ignore the thread ID, you may as well pass NULL for the final parameter of CreateThread.
This also is wrong:
CloseHandle(TName);
The parameter of CloseHandle is of type HANDLE. You are passing a pointer to an array. You need to do this:
CloseHandle(TName[0]);
CloseHandle(TName[1]);
The Writer function does not return a value. The compiler warns you about that, if you enable sufficient warnings. You should certainly do so.

Related

Why does GetExitCodeThread() return FALSE here?

I have a small test code. My assumption is in below code, since I didn't set flag to stop the thread, then in the line of GetExitCodeThread(). it should return TRUE and return code is STILL_ACTIVE.
While in actual test, the result is:
Every time, the return value of GetExitCodeThread() is FALSE, so in main(), the while loop never entered. Could somebody please tell me the reason? What's wrong in my code. Thanks.
// ConsoleApplication1.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "afxwin.h"
bool bExit = false;
HANDLE hOriginalThread;
static UINT ThreadFunc(LPVOID pParam)
{
int iCount = 0;
printf("start thread--ThreadFunc\n");
printf("Thread loop start: --ThreadFunc");
while (!bExit)
{
iCount++;
if (iCount % 50 == 0)
printf(".");
}
printf("Thread loop end: %d--ThreadFunc\n", iCount++);
printf("end thread--ThreadFunc\n");
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
hOriginalThread = AfxBeginThread(ThreadFunc, (LPVOID)0, THREAD_PRIORITY_NORMAL, 0, 0);
Sleep(500);
DWORD dwEC;
int iTry = 0;
BOOL bStatus;
bStatus = GetExitCodeThread(hOriginalThread, &dwEC);
if (!bStatus)
{
printf("error GetExitCodeThread: %d--Main\n", GetLastError());
}
while (bStatus && dwEC == STILL_ACTIVE)
{
printf("Check Thread in active: %d--Main\n", iTry);
Sleep(1);
iTry++;
if (iTry>5)
{
printf("Try to terminate Thread loop: %d--Main\n", iTry++);
TerminateThread(hOriginalThread, 0);// Force thread exit
}
bStatus = GetExitCodeThread(hOriginalThread, &dwEC);
}
hThread = NULL;
printf("End Main --Main\n");
return 0;
}
AfxBeginThread() returns a CWinThread* object pointer, not a Win32 HANDLE like CreateThread() does. So GetExitCodeThread() fails due to an invalid thread handle, which GetLastError() should have told you.
CWinThread has an operator HANDLE() to get the proper Win32 handle of the thread, eg:
CWinThread *pThread = AfxBeginThread(...);
if (!pThread) ... // error handling
hOriginalThread = *pThread;
The reason your code even compiles is because you are likely not compiling with STRICT Type Checking enabled, so HANDLE is just a simple void*, which any kind of pointer can be assigned to. If you enable STRICT, HANDLE will not be void* and assigning the return value of AfxBeginThread() directly to hOriginalThread will cause a compiler error due to a type incompatibility.

Using ReadDirectoryChangesW asynchronously in a loop

INTRODUCTION:
I am trying to use ReadDirectoryChangesW asynchronously in a loop.
Below snippet illustrates what I am trying to achieve:
DWORD example()
{
DWORD error = 0;
OVERLAPPED ovl = { 0 };
ovl.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == ovl.hEvent) return ::GetLastError();
char buffer[1024];
while(1)
{
process_list_of_existing_files();
error = ::ReadDirectoryChangesW(
m_hDirectory, // I have added FILE_FLAG_OVERLAPPED in CreateFile
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL);
// we have new files, append them to the list
if(error) append_new_files_to_the_list(buffer);
// just continue with the loop
else if(::GetLastError() == ERROR_IO_PENDING) continue;
// RDCW error, this is critical -> exit
else return ::GetLastError();
}
}
PROBLEM:
I do not know how to handle the case when ReadDirectoryChangesW returns FALSE with GetLastError() code being ERROR_IO_PENDING.
In that case I should just continue with the loop and keep looping until ReadDirectoryChangesW returns buffer I can process.
MY EFFORTS TO SOLVE THIS:
I have tried using WaitForSingleObject(ovl.hEvent, 1000) but it crashes with error 1450 ERROR_NO_SYSTEM_RESOURCES. Below is the MVCE that reproduces this behavior:
#include <iostream>
#include <Windows.h>
DWORD processDirectoryChanges(const char *buffer)
{
DWORD offset = 0;
char fileName[MAX_PATH] = "";
FILE_NOTIFY_INFORMATION *fni = NULL;
do
{
fni = (FILE_NOTIFY_INFORMATION*)(&buffer[offset]);
// since we do not use UNICODE,
// we must convert fni->FileName from UNICODE to multibyte
int ret = ::WideCharToMultiByte(CP_ACP, 0, fni->FileName,
fni->FileNameLength / sizeof(WCHAR),
fileName, sizeof(fileName), NULL, NULL);
switch (fni->Action)
{
case FILE_ACTION_ADDED:
{
std::cout << fileName << std::endl;
}
break;
default:
break;
}
::memset(fileName, '\0', sizeof(fileName));
offset += fni->NextEntryOffset;
} while (fni->NextEntryOffset != 0);
return 0;
}
int main()
{
HANDLE hDir = ::CreateFile("C:\\Users\\nenad.smiljkovic\\Desktop\\test",
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
if (INVALID_HANDLE_VALUE == hDir) return ::GetLastError();
OVERLAPPED ovl = { 0 };
ovl.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == ovl.hEvent) return ::GetLastError();
DWORD error = 0, br;
char buffer[1024];
while (1)
{
error = ::ReadDirectoryChangesW(hDir,
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL);
if (0 == error)
{
error = ::GetLastError();
if (ERROR_IO_PENDING != error)
{
::CloseHandle(ovl.hEvent);
::CloseHandle(hDir);
return error;
}
}
error = ::WaitForSingleObject(ovl.hEvent, 0);
switch (error)
{
case WAIT_TIMEOUT:
break;
case WAIT_OBJECT_0:
{
error = processDirectoryChanges(buffer);
if (error > 0)
{
::CloseHandle(ovl.hEvent);
::CloseHandle(hDir);
return error;
}
if (0 == ::ResetEvent(ovl.hEvent))
{
error = ::GetLastError();
::CloseHandle(ovl.hEvent);
::CloseHandle(hDir);
return error;
}
}
break;
default:
error = ::GetLastError();
::CloseHandle(ovl.hEvent);
::CloseHandle(hDir);
return error;
break;
}
}
return 0;
}
Reading through the documentation, it seems that I need GetOverlappedResult with last parameter set to FALSE but I do not know how to use this API properly.
QUESTION:
Since the MVCE illustrates very well what I am trying to do (print the names of the newly added files), can you show me what must be fixed in the while loop in order for it to work?
Again, the point is to use ReadDirectoryChangesW asynchronously, in a loop, as shown in the snippet from the INTRODUCTION.
The basic structure of your program looks more or less OK, you're just using the asynchronous I/O calls incorrectly. Whenever there are no new files, the wait on the event handle times out immediately, which is fine, but you then issue a brand new I/O request, which isn't.
That's why you're running out of system resources; you're issuing I/O requests full tilt without waiting for any of them to complete. You should only issue a new request after the existing request has completed.
(Also, you should be calling GetOverlappedResult to check whether the I/O was successful or not.)
So your loop should look more like this:
::ReadDirectoryChangesW(hDir,
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL);
while (1)
{
DWORD dw;
DWORD result = ::WaitForSingleObject(ovl.hEvent, 0);
switch (result)
{
case WAIT_TIMEOUT:
processBackgroundTasks();
break;
case WAIT_OBJECT_0:
::GetOverlappedResult(hDir, &ovl, &dw, FALSE);
processDirectoryChanges(buffer);
::ResetEvent(ovl.hEvent);
::ReadDirectoryChangesW(hDir,
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL);
break;
}
}
Notes:
The error handling has been elided for simplicity; I have not done any testing or checked your code for any other problems.
If there might not be any background tasks to perform, you should test for that case and set the timeout to INFINITE rather than 0 when it occurs, otherwise you will be spinning.
I wanted to only show the minimal changes necessary to make it work, but calling WaitForSingleObject followed by GetOverlappedResult is redundant; a single call to GetOverlappedResult can both check whether the I/O is complete and retrieve the results if it is.
As requested, the modified version using only GetOverlappedResult and with minimal error checking. I've also added an example of how you might deal with the case where you've run out of work to do; if whatever processing you're doing on the files really does run forever, you don't need that bit.
::ResetEvent(ovl.hEvent);
if (!::ReadDirectoryChangesW(hDir,
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL))
{
error = GetLastError();
if (error != ERROR_IO_PENDING) fail();
}
while (1)
{
BOOL wait;
result = process_list_of_existing_files();
if (result == MORE_WORK_PENDING)
{
wait = FALSE;
}
else if (result == NO_MORE_WORK_PENDING)
{
wait = TRUE;
}
if (!::GetOverlappedResult(hDir, &ovl, &dw, wait))
{
error = GetLastError();
if (error == ERROR_IO_INCOMPLETE) continue;
fail();
}
processDirectoryChanges(buffer);
::ResetEvent(ovl.hEvent);
if (!::ReadDirectoryChangesW(hDir,
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL))
{
error = GetLastError();
if (error != ERROR_IO_PENDING) fail();
}
}
Variant of indirect using IOCP
Create a class/struct inherited (containing) OVERLAPPED (or
IO_STATUS_BLOCK), a reference counter, directory handle and data which
you need
Call BindIoCompletionCallback (RtlSetIoCompletionCallback) for
directory handle, for setup your callback
Have a DoRead() routine, which we'll call first-time from the main thread, and then from the callback
In DoRead(), before every call to ReadDirectoryChangesW call
AddRef(); because we pass reference (across OVERLAPPED) to our
struct to kernel
Main (say GUI thread) can continue to do own task after the initial call
to DoRead(), unlike the APC variant, we do not need to wait in alertable state
In the callback, we got a pointer to our struct from inherited (containing)
OVERLAPPED. Do any tasks (processDirectoryChanges), if need
continue spy - call DoRead() and finally call Release()
If ReadDirectoryChangesW from DoRead() fails (as result will be no callback) - we need direct call callback
with error code
For stopping we can simply close the directory handle - as a result, we got
STATUS_NOTIFY_CLEANUP in callback
==================================
//#define _USE_NT_VERSION_
class SPYDATA :
#ifdef _USE_NT_VERSION_
IO_STATUS_BLOCK
#else
OVERLAPPED
#endif
{
HANDLE _hFile;
LONG _dwRef;
union {
FILE_NOTIFY_INFORMATION _fni;
UCHAR _buf[PAGE_SIZE];
};
void DumpDirectoryChanges()
{
union {
PVOID buf;
PBYTE pb;
PFILE_NOTIFY_INFORMATION pfni;
};
buf = _buf;
for (;;)
{
DbgPrint("%x <%.*S>\n", pfni->Action, pfni->FileNameLength >> 1, pfni->FileName);
ULONG NextEntryOffset = pfni->NextEntryOffset;
if (!NextEntryOffset)
{
break;
}
pb += NextEntryOffset;
}
}
#ifdef _USE_NT_VERSION_
static VOID WINAPI _OvCompRoutine(
_In_ NTSTATUS dwErrorCode,
_In_ ULONG_PTR dwNumberOfBytesTransfered,
_Inout_ PIO_STATUS_BLOCK Iosb
)
{
static_cast<SPYDATA*>(Iosb)->OvCompRoutine(dwErrorCode, (ULONG)dwNumberOfBytesTransfered);
}
#else
static VOID WINAPI _OvCompRoutine(
_In_ DWORD dwErrorCode, // really this is NTSTATUS
_In_ DWORD dwNumberOfBytesTransfered,
_Inout_ LPOVERLAPPED lpOverlapped
)
{
static_cast<SPYDATA*>(lpOverlapped)->OvCompRoutine(dwErrorCode, dwNumberOfBytesTransfered);
}
#endif
VOID OvCompRoutine(NTSTATUS status, DWORD dwNumberOfBytesTransfered)
{
DbgPrint("[%x,%x]\n", status, dwNumberOfBytesTransfered);
if (0 <= status)
{
if (status != STATUS_NOTIFY_CLEANUP)
{
if (dwNumberOfBytesTransfered) DumpDirectoryChanges();
process_list_of_existing_files();// so hard do this here ?!?
DoRead();
}
else
{
DbgPrint("\n---- NOTIFY_CLEANUP -----\n");
}
}
Release();
MyReleaseRundownProtection();
}
~SPYDATA()
{
Cancel();
}
public:
void DoRead()
{
if (MyAcquireRundownProtection())
{
AddRef();
#ifdef _USE_NT_VERSION_
NTSTATUS status = ZwNotifyChangeDirectoryFile(_hFile, 0, 0, this, this, &_fni, sizeof(_buf), FILE_NOTIFY_VALID_MASK, TRUE);
if (NT_ERROR(status))
{
OvCompRoutine(status, 0);
}
#else
if (!ReadDirectoryChangesW(_hFile, _buf, sizeof(_buf), TRUE, FILE_NOTIFY_VALID_MASK, (PDWORD)&InternalHigh, this, 0))
{
OvCompRoutine(RtlGetLastNtStatus(), 0);
}
#endif
}
}
SPYDATA()
{
_hFile = 0;// ! not INVALID_HANDLE_VALUE because use ntapi for open file
_dwRef = 1;
#ifndef _USE_NT_VERSION_
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
#endif
}
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef))
{
delete this;
}
}
BOOL Create(POBJECT_ATTRIBUTES poa)
{
IO_STATUS_BLOCK iosb;
NTSTATUS status = ZwOpenFile(&_hFile, FILE_GENERIC_READ, poa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_DIRECTORY_FILE);
if (0 <= status)
{
return
#ifdef _USE_NT_VERSION_
0 <= RtlSetIoCompletionCallback(_hFile, _OvCompRoutine, 0);
#else
BindIoCompletionCallback(_hFile, _OvCompRoutine, 0);
#endif
}
return FALSE;
}
void Cancel()
{
if (HANDLE hFile = InterlockedExchangePointer(&_hFile, 0))
{
NtClose(hFile);
}
}
};
void DemoF()
{
if (MyInitializeRundownProtection())
{
STATIC_OBJECT_ATTRIBUTES(oa, "<SOME_DIRECTORY>");
if (SPYDATA* p = new SPYDATA)
{
if (p->Create(&oa))
{
p->DoRead();
}
//++ GUI thread run
MessageBoxW(0, L"wait close program...", L"", MB_OK);
//-- GUI thread end
p->Cancel();
p->Release();
}
MyWaitForRundownProtectionRelease();
}
}

Why isn't the mutex being aquired?

I have been looking into all of the different syncronization primitives available in the WinAPI, but have been struggling with what should have been something simple. Why doesn't the following code work?
class MultiThreadedCounter
{
private:
int count; HANDLE hMutex;
public:
void IncrementCounter()
{
if (count == 0)
hMutex = CreateMutex(NULL, TRUE, NULL);
count++;
}
void DecrementCounter()
{
count--;
if (count == 0)
ReleaseMutex(hMutex);
}
void WaitForCounterToReachZero()
{
WaitForSingleObject(hMutex, INFINITE);
CloseHandle(hMutex);
}
};
MultiThreadedCounter extractionsInProgressCounter;
It's definitely getting called in the right order. First, IncrementCounter() is called by the main thread before the async task (here, a thread sleep). Then the main thread calls WaitForCounterToReachZero(). Finally, the background thread calls DecrementCounter() when it has completed its work, which should allow the main thread to proceed.
However, WaitForSingleObject is not waiting. It returns immediately, with WAIT_OBJECT_0. Why is it doing that? It's almost like the mutex was never initially aquired. However, in the call to CreateMutex, I set bInitialOwner to TRUE, which is why I don't understand why it doesn't seem to have been aquired. I guess I have misunderstood something.
Thank you.
EDIT 1:
OK, so to test, I changed IncrementCounter() to:
void IncrementCounter()
{
if (count == 0)
{
hMutex = CreateMutex(NULL, TRUE, NULL);
DWORD var1 = WaitForSingleObject(hMutex, INFINITE);
DWORD var2 = WaitForSingleObject(hMutex, INFINITE);
}
count++;
}
That really, really should have deadlocked it, but no, both calls to WaitForSingleObject returned immediately with var1 and var2 both equal to 0 (which according to the headers is WAIT_OBJECT_0).
The call to CreateMutex can't be working, can it? Yet hMutex gets set to a sensible value and GetLastError() remains at 0. So confused...
EDIT 2: Thank you all for your help. I never got this to work, however, I now realise that I was doing this the wrong way anyway. So I switched everything over to an Event, at which point it worked, then added a few conditionals to deal with out of order increments & decrements, then a critical section to protect the count variable. And it works :)
class MultiThreadedCounter
{
private:
int count; HANDLE hEvent; CRITICAL_SECTION criticalSection;
public:
void IncrementCounter()
{
EnterCriticalSection(&criticalSection);
if (count == 0)
ResetEvent(hEvent);
count++;
LeaveCriticalSection(&criticalSection);
}
void DecrementCounter()
{
EnterCriticalSection(&criticalSection);
if (count > 0)
count--;
if (count == 0)
SetEvent(hEvent);
LeaveCriticalSection(&criticalSection);
}
void WaitForCounterToReachZero()
{
WaitForSingleObject(hEvent, INFINITE);
}
MultiThreadedCounter()
{
hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
InitializeCriticalSection(&criticalSection);
count = 0;
}
~MultiThreadedCounter()
{
CloseHandle(hEvent);
DeleteCriticalSection(&criticalSection);
}
};
You don't show a constructor for MultiThreadedCounter. Without this, there is no place to initialise count to 0, meaning that the first call to IncrementCounter almost certainly won't call CreateMutex
Your constructor should look something like
MultiThreadedCounter()
: count(0)
, hMutex(NULL)
{
}
As an aside, if you need a lock that is used between threads in a single process, you could consider using a critical section instead.

Simple thread with wait function

I cannot understand why next code doesn't work. It compile correct, but doesn't output anything. Can you help me?
HANDLE hEvent;
unsigned int WINAPI MyThread(void *p)
{
WaitForSingleObject(hEvent, INFINITE);
_tprintf(TEXT("%s\n"),p);
return 0;
}
int _tmain(int argc, TCHAR *argv[])
{
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
unsigned int ThreadID;
HANDLE hThread1 = (HANDLE)_beginthreadex(NULL, 0, MyThread, L"hello world", 0, &ThreadID);
SetEvent(hEvent);
return 0;
}
My guess is that the application is exiting before the print statement even fires. You set the event and then instantly exit. Try waiting for the thread to complete before exiting. You should be able to use the handle that is returned from _beginthreadex.
HANDLE hThread1 = (HANDLE)_beginthreadex(NULL, 0, MyThread, L"hello world", 0, &ThreadID);
SetEvent(hEvent);
WaitForSingleObject( hThread1, INFINITE );
Note: I just used INFINITE as the timeout for an example, in general you probably don't want INFINITE which could lead to deadlocks, etc. Each case needs to be examined to determine the correct behavior. Here, since you are just writing to the console, if this hasn't returned in a few seconds there is probably something wrong. You could modify the code to check the return value of WaitForSingleObject to see if it exited due to timeout and then make a decision based on that (like log an error, or something that would help to diagnose what went wrong)
You programs stops before the thread even started.
Put this in front of your return 0; and you should get some results
DWORD retVal;
GetExitCodeThread(hThread1, &retVal);
while(retVal == STILL_ACTIVE) {
Sleep(1000);
GetExitCodeThread(hThread1, &retVal);
}
retVal will also help you to see how your thread ended (given that you have different exit codes e.g. _endthreadex(6);)

C++ thread termination with waiting window

So, the code goes somehow like this:
MAIN(){
/*waiting window class declaration*/
threadinfo* oThread=new threadinfo(); //An object that will help me know when to finish the thread
QueueUserWorkItem((LPTHREAD_START_ROUTINE)waitingWindow, (void*)mThread, WT_EXECUTELONGFUNCTION);
function_that_takes_time();
oThread->setTerminated(); //set member terminated to bool true
/*continue with other things*/
}
and waitingWindow function that will run on that thread
MSG msg;
hwndWaiting=CreateWindow(...) // here the window is created
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, null, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
if(oThread->isTerminated()) // isTerminated returns bool true if terminated
{
delete oThread;
ExitThread(0);
}
}
}
ExitThread(0);
Is ExitThread a good way to remove the waiting window, and safely remove the thread? (at least I'm 100% sure this way when to end it).
I'm asking this because this works nice in Windows XP, but will crash with "the application has stopped working" on Windows 7.
Thanks for the help.
The best way to end threads in general, is to let them "gracefully" finish up by themselves. You could tell the thread to end by setting an event, for example:
HANDLE hevent_die = CreateEvent(...);
HANDLE hthread_something = CreateThread(...); // or _beginthread()
...
DWORD WINAPI thread_func (LPVOID param)
{
while(working && WaitForSingleObject(hevent_die, 0)!=WAIT_OBJECT_0)
{
...
}
return 0;
}
while (msg.message != WM_QUIT)
{
...
if(WaitForSingleObject(hthread_something, 0) == WAIT_OBJECT_0)
{
// do things if needed
}
}
SetEvent(hevent_die);
WaitForSingleObject(hthread_something, INFINITE);
CloseHandle(hthread_something);
CloseHandle(hevent_die);
hthread_something = 0;
hevent_die = 0;
If you are using nested loops inside the thread function, they too will have to end if they receive the event.
You should exit your loop and thread cleanly so that any destructors are called correctly. Don't use ExitThread(), just use a flag to indicate when to exit the loop and then just exit your waitingWindow function at the end.