Reputation: 14075
I'm trying to get APC to work in my C++ code but I fail. I'm lost for words. In C# it works all fine (logically the same code). I want thread 2 to inject a call into thread 1. But in my C++ project it wont execute for some reason. What am I doing wrong ?
#include "stdio.h"
#include "windows.h"
#define TIME 2500
#define LAST_ERROR printf("last error: %i\r\n", GetLastError());
HANDLE handle1, handle2;
void ThreadInfo(char* prefix = "")
{
printf("%sthread id: %i\r\n", prefix, GetCurrentThreadId());
}
VOID CALLBACK apc( _In_ ULONG_PTR data)
{
ThreadInfo(" -> apc: 2 -> 1: ");
}
void run1()
{
while (true)
{
Sleep(TIME);
ThreadInfo("1: ");
// apc
//QueueUserAPC(apc, handle2, (ULONG_PTR) NULL);
}
}
void run2()
{
while (true)
{
Sleep(TIME);
ThreadInfo("2: ");
// apc
QueueUserAPC(apc, handle1, (ULONG_PTR) NULL);
}
}
void TestThreads()
{
DWORD threadId;
SECURITY_ATTRIBUTES a;
a.nLength = 12;
a.lpSecurityDescriptor = NULL;
a.bInheritHandle = 1;
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &handle1, 0, true, 2);
LAST_ERROR
handle2 = CreateThread(NULL, 100000, (LPTHREAD_START_ROUTINE)run2, NULL, 0, &threadId);
printf("handles (1, 2): %i, %i\r\n", handle1, handle2);
printf("ids (1, 2): %i, %i\r\n", threadId, GetCurrentThreadId());
printf("--------------------------------\r\n");
run1();
}
int main()
{
TestThreads();
printf("done.");
getchar();
return 0;
}
Upvotes: 7
Views: 2116
Reputation: 941455
Sleep(TIME);
That's your problem statement. APCs are pretty dangerous, they permit code re-entrancy. The rough equivalent to the infamous Application.DoEvents() statement that got so many VB programmers in trouble. Windows doesn't just let them run, you have to be explicit that your code is re-entrant so that the APC can safely run without screwing up your program state.
The specific requirement is that your thread is in an "alertable wait state". Getting into a wait state is not the issue, the Sleep() call does that. It is however not an alertable state. You have to use this instead:
SleepEx(TIME, TRUE);
Modify the run1() function in your test program and you'll now see the APC callback getting called. Compare to GetOverlappedResultEx(), SignalObjectAndWait(), WaitForSingleObjectEx() and WaitForMultipleObjectsEx(), other winapi calls that can put a thread in an alertable wait state. And yes, Thread.Sleep() in a managed program is alertable, the CLR calls SleepEx() under the hood.
Upvotes: 11