1 /*
2  * PROJECT:     ReactOS api tests
3  * LICENSE:     LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
4  * PURPOSE:     Tests for QueueUserAPC, SleepEx, WaitForSingleObjectEx etc.
5  * COPYRIGHT:   Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6  */
7 #include "precomp.h"
8 
9 #define MAX_RECORD 30
10 
11 static LONG s_record_count = 0;
12 static DWORD s_record[MAX_RECORD + 1] = { 0 };
13 static BOOL s_terminate_all = FALSE;
14 
15 static const DWORD s_expected[] =
16 {
17     0, 1, 7, 8, 4,
18     2, 1, 9, 10, 5,
19     2, 1, 11, 12, 13,
20     6, 2, 3, 14, 15,
21     16
22 };
23 static const SIZE_T s_expected_count = _countof(s_expected);
24 
AddValueToRecord(DWORD dwValue)25 static void AddValueToRecord(DWORD dwValue)
26 {
27     LONG next = InterlockedIncrement(&s_record_count) - 1;
28     if (next < MAX_RECORD)
29         s_record[next] = dwValue;
30 }
31 
CheckRecord(void)32 static VOID CheckRecord(void)
33 {
34     SIZE_T i, count = min(s_record_count, s_expected_count);
35 
36     for (i = 0; i < count; ++i)
37     {
38         ok(s_record[i] == s_expected[i], "s_record[%u]: got %lu vs expected %lu\n",
39            (INT)i, s_record[i], s_expected[i]);
40     }
41 
42     count = abs((int)s_record_count - (int)s_expected_count);
43     for (i = 0; i < count; ++i)
44     {
45         ok(s_record_count == s_expected_count,
46            "s_record_count: got %u vs expected %u\n",
47            (int)s_record_count, (int)s_expected_count);
48     }
49 }
50 
ThreadFunc1(LPVOID arg)51 static DWORD WINAPI ThreadFunc1(LPVOID arg)
52 {
53     AddValueToRecord(0);
54     while (!s_terminate_all)
55     {
56         AddValueToRecord(1);
57         ok_long(SleepEx(INFINITE, TRUE), WAIT_IO_COMPLETION);
58         AddValueToRecord(2);
59     }
60     AddValueToRecord(3);
61     return 0;
62 }
63 
ThreadFunc2(LPVOID arg)64 static DWORD WINAPI ThreadFunc2(LPVOID arg)
65 {
66     AddValueToRecord(0);
67     while (!s_terminate_all)
68     {
69         AddValueToRecord(1);
70         ok_long(WaitForSingleObjectEx(GetCurrentThread(), INFINITE, TRUE), WAIT_IO_COMPLETION);
71         AddValueToRecord(2);
72     }
73     AddValueToRecord(3);
74     return 0;
75 }
76 
DoUserAPC1(ULONG_PTR Parameter)77 static VOID NTAPI DoUserAPC1(ULONG_PTR Parameter)
78 {
79     ok_int((int)Parameter, 1);
80     AddValueToRecord(4);
81 }
82 
DoUserAPC2(ULONG_PTR Parameter)83 static VOID NTAPI DoUserAPC2(ULONG_PTR Parameter)
84 {
85     ok_int((int)Parameter, 2);
86     AddValueToRecord(5);
87 }
88 
DoUserAPC3(ULONG_PTR Parameter)89 static VOID NTAPI DoUserAPC3(ULONG_PTR Parameter)
90 {
91     ok_int((int)Parameter, 3);
92     AddValueToRecord(6);
93     s_terminate_all = TRUE;
94 }
95 
JustDoIt(LPTHREAD_START_ROUTINE fn)96 static void JustDoIt(LPTHREAD_START_ROUTINE fn)
97 {
98     HANDLE hThread;
99     DWORD dwThreadId;
100 
101     s_terminate_all = FALSE;
102     s_record_count = 0;
103     ZeroMemory(s_record, sizeof(s_record));
104 
105     hThread = CreateThread(NULL, 0, fn, NULL, 0, &dwThreadId);
106     ok(hThread != NULL, "hThread was NULL\n");
107 
108     Sleep(100);
109 
110     AddValueToRecord(7);
111     ok_long(QueueUserAPC(DoUserAPC1, hThread, 1), 1);
112     AddValueToRecord(8);
113 
114     Sleep(100);
115 
116     AddValueToRecord(9);
117     ok_long(QueueUserAPC(DoUserAPC2, hThread, 2), 1);
118     AddValueToRecord(10);
119 
120     Sleep(100);
121 
122     AddValueToRecord(11);
123     ok_long(QueueUserAPC(DoUserAPC3, hThread, 3), 1);
124     AddValueToRecord(12);
125 
126     AddValueToRecord(13);
127     ok_long(WaitForSingleObject(hThread, 5 * 1000), WAIT_OBJECT_0);
128     AddValueToRecord(14);
129 
130     AddValueToRecord(15);
131     ok_int(CloseHandle(hThread), TRUE);
132     hThread = NULL;
133     AddValueToRecord(16);
134 
135     CheckRecord();
136 }
137 
TestForSleepEx(void)138 static void TestForSleepEx(void)
139 {
140     JustDoIt(ThreadFunc1);
141 }
142 
TestForWaitForSingleObjectEx(void)143 static void TestForWaitForSingleObjectEx(void)
144 {
145     JustDoIt(ThreadFunc2);
146 }
147 
ThreadFunc3(LPVOID arg)148 static DWORD WINAPI ThreadFunc3(LPVOID arg)
149 {
150     return 0;
151 }
152 
TestMultipleUserAPCs(void)153 static void TestMultipleUserAPCs(void)
154 {
155     HANDLE hThread;
156     DWORD dwThreadId;
157 
158     s_record_count = 0;
159 
160     hThread = CreateThread(NULL, 0, ThreadFunc3, NULL, CREATE_SUSPENDED, &dwThreadId);
161     ok(hThread != NULL, "hThread was NULL\n");
162 
163     ok_long(QueueUserAPC(DoUserAPC1, hThread, 1), 1);
164     ok_long(QueueUserAPC(DoUserAPC2, hThread, 2), 1);
165     ok_long(QueueUserAPC(DoUserAPC3, hThread, 3), 1);
166 
167     ok_long(s_record_count, 0);
168 
169     ResumeThread(hThread);
170 
171     ok_long(WaitForSingleObject(hThread, 5 * 1000), WAIT_OBJECT_0);
172     ok_int(CloseHandle(hThread), TRUE);
173 
174     ok_long(s_record_count, 3);
175     ok_long(s_record[0], 4);
176     ok_long(s_record[1], 5);
177     ok_long(s_record[2], 6);
178 }
179 
START_TEST(QueueUserAPC)180 START_TEST(QueueUserAPC)
181 {
182     TestForSleepEx();
183     TestForWaitForSingleObjectEx();
184     TestMultipleUserAPCs();
185 }
186