freerdp/winpr/libwinpr/synch/test/TestSynchAPC.c

175 lines
4.1 KiB
C

/**
* FreeRDP: A Remote Desktop Protocol Implementation
* TestSyncAPC
*
* Copyright 2021 David Fort <contact@hardening-consulting.com>
*
* 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 <winpr/wtypes.h>
#include <winpr/thread.h>
#include <winpr/synch.h>
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;
}