/** * FreeRDP: A Remote Desktop Protocol Implementation * TestSyncAPC * * Copyright 2021 David Fort * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include typedef struct { BOOL error; BOOL called; } UserApcArg; void CALLBACK userApc(ULONG_PTR arg) { UserApcArg* userArg = (UserApcArg*)arg; userArg->called = TRUE; } static DWORD WINAPI uncleanThread(LPVOID lpThreadParameter) { /* this thread post an APC that will never get executed */ UserApcArg* userArg = (UserApcArg*)lpThreadParameter; if (!QueueUserAPC((PAPCFUNC)userApc, _GetCurrentThread(), (ULONG_PTR)lpThreadParameter)) { userArg->error = TRUE; return 1; } return 0; } static DWORD WINAPI cleanThread(LPVOID lpThreadParameter) { Sleep(500); SleepEx(500, TRUE); return 0; } typedef struct { HANDLE timer1; DWORD timer1Calls; HANDLE timer2; DWORD timer2Calls; BOOL endTest; } UncleanCloseData; static VOID CALLBACK Timer1APCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue) { UncleanCloseData* data = (UncleanCloseData*)lpArg; data->timer1Calls++; CloseHandle(data->timer2); data->endTest = TRUE; } static VOID CALLBACK Timer2APCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue) { UncleanCloseData* data = (UncleanCloseData*)lpArg; data->timer2Calls++; } static DWORD /*WINAPI*/ closeHandleTest(LPVOID lpThreadParameter) { LARGE_INTEGER dueTime; UncleanCloseData* data = (UncleanCloseData*)lpThreadParameter; data->endTest = FALSE; dueTime.QuadPart = -500; if (!SetWaitableTimer(data->timer1, &dueTime, 0, Timer1APCProc, lpThreadParameter, FALSE)) return 1; dueTime.QuadPart = -900; if (!SetWaitableTimer(data->timer2, &dueTime, 0, Timer2APCProc, lpThreadParameter, FALSE)) return 1; while (!data->endTest) { SleepEx(100, TRUE); } return 0; } int TestSynchAPC(int argc, char* argv[]) { HANDLE thread = NULL; UserApcArg userApcArg; UncleanCloseData uncleanCloseData; userApcArg.error = FALSE; userApcArg.called = FALSE; WINPR_UNUSED(argc); WINPR_UNUSED(argv); /* first post an APC and check it is executed during a SleepEx */ if (!QueueUserAPC((PAPCFUNC)userApc, _GetCurrentThread(), (ULONG_PTR)&userApcArg)) return 1; if (SleepEx(100, FALSE) != 0) return 2; if (SleepEx(100, TRUE) != WAIT_IO_COMPLETION) return 3; if (!userApcArg.called) return 4; userApcArg.called = FALSE; /* test that the APC is cleaned up even when not called */ thread = CreateThread(NULL, 0, uncleanThread, &userApcArg, 0, NULL); if (!thread) return 10; WaitForSingleObject(thread, INFINITE); CloseHandle(thread); if (userApcArg.called || userApcArg.error) return 11; /* test a remote APC queuing */ thread = CreateThread(NULL, 0, cleanThread, &userApcArg, 0, NULL); if (!thread) return 20; if (!QueueUserAPC((PAPCFUNC)userApc, thread, (ULONG_PTR)&userApcArg)) return 21; WaitForSingleObject(thread, INFINITE); CloseHandle(thread); if (!userApcArg.called) return 22; #if 0 /* test cleanup of timer completions */ memset(&uncleanCloseData, 0, sizeof(uncleanCloseData)); uncleanCloseData.timer1 = CreateWaitableTimerA(NULL, FALSE, NULL); if (!uncleanCloseData.timer1) return 31; uncleanCloseData.timer2 = CreateWaitableTimerA(NULL, FALSE, NULL); if (!uncleanCloseData.timer2) return 32; thread = CreateThread(NULL, 0, closeHandleTest, &uncleanCloseData, 0, NULL); if (!thread) return 33; WaitForSingleObject(thread, INFINITE); CloseHandle(thread); if (uncleanCloseData.timer1Calls != 1 || uncleanCloseData.timer2Calls != 0) return 34; CloseHandle(uncleanCloseData.timer1); #endif return 0; }