#include <Windows.h>
#include <stdio.h>
#define NUM_PRODUCER_THREADS 3
#define MSG_PRODUCER_STARTED (WM_USER + 1)
#define MSG_PRODUCER_TICK (WM_USER + 2)
struct ProducerParams
{
DWORD dwConsumerThread;
HANDLE hShutdownEvent;
};
static DWORD WINAPI ProducerThread(LPVOID lpvData);
int main(int argc, char** argv)
{
HANDLE hShutdownEvent;
struct ProducerParams threadParams;
SIZE_T i;
HANDLE* hProducerThreads;
BOOL bRes;
MSG msg;
// Make sure the message queue exists before we start the producers
PeekMessageW(&msg, (HWND)-1, 0, 0, PM_NOREMOVE);
// Shutdown event, set in this thread when WM_QUIT was recived
hShutdownEvent = CreateEventW(NULL, TRUE, FALSE, L"app_shutdown_event");
threadParams.dwConsumerThread = GetCurrentThreadId();
threadParams.hShutdownEvent = hShutdownEvent;
hProducerThreads = (HANDLE*)malloc(sizeof(HANDLE) * NUM_PRODUCER_THREADS);
for (i = 0;i < NUM_PRODUCER_THREADS;++ i)
{
hProducerThreads[i] = CreateThread(NULL, 0, &ProducerThread, &threadParams, 0, NULL);
}
ZeroMemory(&msg, sizeof(msg));
// -1 as third parameter ensures we only get thread messages with hwnd=NULL
// GetMessage returns 0 when it recives the WM_QUIT message
// GetMessage returns -1 when an error occured
while (bRes = GetMessage(&msg, (HWND)-1, 0, 0))
{
switch (msg.message)
{
case MSG_PRODUCER_STARTED:
// wParam is set by the producer thread to it's own thread id
printf("Producer started: %u\n", msg.wParam);
break;
case MSG_PRODUCER_TICK:
// wParam is set by the producer thread to it's own thread id
printf("Producer tick: %u\n", msg.wParam);
break;
default:
printf("Unknown message.\n");
break;
}
}
SetEvent(hShutdownEvent);
WaitForMultipleObjects(NUM_PRODUCER_THREADS, hProducerThreads, TRUE, INFINITE);
free(hProducerThreads);
// -1 indicates an error with GetMessage
if (bRes == -1)
{
// message loop exit with error
printf("Message loop terminated with error %u!\n", GetLastError());
return 1;
}
return 0;
}
DWORD WINAPI ProducerThread(LPVOID lpvData)
{
struct ProducerParams* threadParams;
SIZE_T i;
threadParams = (struct ProducerParams*)lpvData;
PostThreadMessageW(threadParams->dwConsumerThread, MSG_PRODUCER_STARTED, GetCurrentThreadId(), NULL);
for (i = 0;i < 10;++ i)
{
if (WaitForSingleObject(threadParams->hShutdownEvent, 0) != WAIT_TIMEOUT)
{
break;
}
PostThreadMessage(threadParams->dwConsumerThread, MSG_PRODUCER_TICK, GetCurrentThreadId(), NULL);
}
PostThreadMessageW(threadParams->dwConsumerThread, WM_QUIT, 0, 0);
WaitForSingleObject(threadParams->hShutdownEvent, INFINITE);
return 0;
}