Reactos
1/*
2 * PROJECT: ReactOS API tests
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Tests for One-Time initialization APIs
5 * COPYRIGHT: Copyright 2023 Ratin Gao <ratin@knsoft.org>
6 */
7
8#include "precomp.h"
9
10typedef
11VOID
12WINAPI
13FN_InitOnceInitialize(_Out_ PINIT_ONCE InitOnce);
14
15typedef
16BOOL
17WINAPI
18FN_InitOnceExecuteOnce(
19 _Inout_ PINIT_ONCE InitOnce,
20 _In_ __callback PINIT_ONCE_FN InitFn,
21 _Inout_opt_ PVOID Parameter,
22 _Outptr_opt_result_maybenull_ LPVOID *Context);
23
24typedef
25BOOL
26WINAPI
27FN_InitOnceBeginInitialize(
28 _Inout_ LPINIT_ONCE lpInitOnce,
29 _In_ DWORD dwFlags,
30 _Out_ PBOOL fPending,
31 _Outptr_opt_result_maybenull_ LPVOID *lpContext);
32
33typedef
34BOOL
35WINAPI
36FN_InitOnceComplete(
37 _Inout_ LPINIT_ONCE lpInitOnce,
38 _In_ DWORD dwFlags,
39 _In_opt_ LPVOID lpContext);
40
41static ULONG g_ulRandom;
42
43static
44VOID
45InitWorker(_Inout_ PULONG InitCount, _Out_ PULONG_PTR Context)
46{
47 /* Increase the initialization count */
48 (*InitCount)++;
49
50 /* Output context data */
51 *Context = (ULONG_PTR)g_ulRandom;
52}
53
54static
55_Success_(return != FALSE)
56BOOL
57WINAPI
58InitOnceProc(
59 _Inout_ PINIT_ONCE InitOnce,
60 _Inout_opt_ PVOID Parameter,
61 _Outptr_opt_result_maybenull_ PVOID* Context)
62{
63 if (!Parameter || !Context)
64 {
65 return FALSE;
66 }
67
68 InitWorker(Parameter, (PULONG_PTR)Context);
69 return TRUE;
70}
71
72START_TEST(InitOnce)
73{
74 BOOL bRet, fPending;
75 ULONG i, ulInitCount, ulSeed;
76 ULONG_PTR ulContextData, ulTempContext;
77 DWORD dwError;
78
79 HMODULE hKernel32;
80 FN_InitOnceInitialize* pfnInitOnceInitialize;
81 FN_InitOnceExecuteOnce* pfnInitOnceExecuteOnce;
82 FN_InitOnceBeginInitialize* pfnInitOnceBeginInitialize;
83 FN_InitOnceComplete* pfnInitOnceComplete;
84
85 /* Load functions */
86 hKernel32 = GetModuleHandleW(L"kernel32.dll");
87 if (!hKernel32)
88 {
89 skip("Module kernel32 not found\n");
90 return;
91 }
92 pfnInitOnceInitialize = (FN_InitOnceInitialize*)GetProcAddress(hKernel32, "InitOnceInitialize");
93 pfnInitOnceExecuteOnce = (FN_InitOnceExecuteOnce*)GetProcAddress(hKernel32, "InitOnceExecuteOnce");
94 pfnInitOnceBeginInitialize = (FN_InitOnceBeginInitialize*)GetProcAddress(hKernel32, "InitOnceBeginInitialize");
95 pfnInitOnceComplete = (FN_InitOnceComplete*)GetProcAddress(hKernel32, "InitOnceComplete");
96
97 /*
98 * Use a random as output context data,
99 * which the low-order INIT_ONCE_CTX_RESERVED_BITS bits should be zero.
100 */
101 ulSeed = (ULONG)(ULONG_PTR)&ulSeed ^ GetTickCount();
102 g_ulRandom = RtlRandom(&ulSeed);
103 for (i = 0; i < INIT_ONCE_CTX_RESERVED_BITS; i++)
104 {
105 g_ulRandom &= (~(1 << i));
106 }
107
108 /* Initialize One-Time initialization structure */
109 INIT_ONCE InitOnce = { (PVOID)(ULONG_PTR)0xDEADBEEF };
110 if (pfnInitOnceInitialize)
111 {
112 pfnInitOnceInitialize(&InitOnce);
113 }
114 else
115 {
116 skip("InitOnceInitialize not found\n");
117 InitOnce = (INIT_ONCE)INIT_ONCE_STATIC_INIT;
118 }
119
120 if (!pfnInitOnceExecuteOnce)
121 {
122 skip("InitOnceExecuteOnce not found\n");
123 goto _test_sync;
124 }
125
126 /*
127 * Perform synchronous initialization by using InitOnceExecuteOnce,
128 * which executes user-defined callback to initialize.
129 * Call InitOnceExecuteOnce twice will success,
130 * initialization count should be 1 and retrieve correct context data.
131 */
132 ulInitCount = 0;
133 ulContextData = MAXULONG;
134 bRet = pfnInitOnceExecuteOnce(&InitOnce, InitOnceProc, &ulInitCount, (LPVOID*)&ulContextData);
135 ok(bRet, "InitOnceExecuteOnce failed with %lu\n", GetLastError());
136 if (bRet)
137 {
138 /* Call InitOnceExecuteOnce again and check output values if the first call succeeded */
139 bRet = pfnInitOnceExecuteOnce(&InitOnce,
140 InitOnceProc,
141 &ulInitCount,
142 (LPVOID*)&ulContextData);
143 ok(bRet, "InitOnceExecuteOnce failed with %lu\n", GetLastError());
144 ok(ulInitCount == 1, "ulInitCount is not 1\n");
145 ok(ulContextData == g_ulRandom, "Output ulContextData is incorrect\n");
146 }
147
148_test_sync:
149 if (!pfnInitOnceBeginInitialize || !pfnInitOnceComplete)
150 {
151 skip("InitOnceBeginInitialize or InitOnceComplete not found\n");
152 return;
153 }
154
155 /* Re-initialize One-Time initialization structure by using INIT_ONCE_STATIC_INIT */
156 InitOnce = (INIT_ONCE)INIT_ONCE_STATIC_INIT;
157 ulContextData = 0xdeadbeef;
158
159 /* Perform synchronous initialization by using InitOnceBeginInitialize */
160 fPending = FALSE;
161 bRet = pfnInitOnceBeginInitialize(&InitOnce, 0, &fPending, (LPVOID*)&ulContextData);
162 ok(bRet, "InitOnceBeginInitialize failed with %lu\n", GetLastError());
163 if (!bRet)
164 {
165 goto _test_async;
166 }
167 ok(fPending, "fPending is not TRUE after the first success InitOnceBeginInitialize\n");
168 if (!fPending)
169 {
170 goto _test_async;
171 }
172
173 /* Call again to check whether initialization has completed */
174 fPending = 0xdeadbeef;
175 bRet = pfnInitOnceBeginInitialize(&InitOnce,
176 INIT_ONCE_CHECK_ONLY,
177 &fPending,
178 (LPVOID*)&ulContextData);
179 ok(bRet == FALSE, "InitOnceBeginInitialize should fail\n");
180 ok(fPending == 0xdeadbeef, "fPending should be unmodified\n");
181 ok(ulContextData == 0xdeadbeef, "ulContextData should be unmodified\n");
182
183 /* Complete the initialization */
184 InitWorker(&ulInitCount, &ulTempContext);
185 bRet = pfnInitOnceComplete(&InitOnce, 0, (LPVOID)ulTempContext);
186 ok(bRet, "InitOnceComplete failed with %lu\n", GetLastError());
187 if (!bRet)
188 {
189 goto _test_async;
190 }
191
192 /*
193 * Initialization is completed, call InitOnceBeginInitialize with
194 * INIT_ONCE_CHECK_ONLY should retrieve status and context data successfully
195 */
196 bRet = pfnInitOnceBeginInitialize(&InitOnce,
197 INIT_ONCE_CHECK_ONLY,
198 &fPending,
199 (LPVOID*)&ulContextData);
200 ok(bRet && !fPending && ulContextData == g_ulRandom,
201 "InitOnceBeginInitialize returns incorrect result for a completed initialization\n");
202
203_test_async:
204 InitOnce = (INIT_ONCE)INIT_ONCE_STATIC_INIT;
205
206 /* Perform asynchronous initialization */
207 fPending = FALSE;
208 bRet = pfnInitOnceBeginInitialize(&InitOnce,
209 INIT_ONCE_ASYNC,
210 &fPending,
211 (LPVOID*)&ulContextData);
212 ok(bRet, "InitOnceBeginInitialize failed with %lu\n", GetLastError());
213 if (!bRet)
214 {
215 return;
216 }
217 ok(fPending, "fPending is not TRUE after a success InitOnceBeginInitialize\n");
218 if (!fPending)
219 {
220 return;
221 }
222
223 /*
224 * Now the initialization is in progress but not completed yet,
225 * call InitOnceBeginInitialize again without INIT_ONCE_ASYNC is invalid,
226 * should fail with ERROR_INVALID_PARAMETER
227 */
228 bRet = pfnInitOnceBeginInitialize(&InitOnce, 0, &fPending, (LPVOID*)&ulContextData);
229 ok(!bRet, "InitOnceBeginInitialize should not success\n");
230 if (!bRet)
231 {
232 dwError = GetLastError();
233 ok(dwError == ERROR_INVALID_PARAMETER,
234 "Last error is %lu, but %u is expected\n",
235 dwError,
236 ERROR_INVALID_PARAMETER);
237 }
238
239 /*
240 * Call InitOnceBeginInitialize again with INIT_ONCE_ASYNC
241 * should success because initialization could be executed in parallel
242 */
243 bRet = pfnInitOnceBeginInitialize(&InitOnce,
244 INIT_ONCE_ASYNC,
245 &fPending,
246 (LPVOID*)&ulContextData);
247 ok(bRet, "InitOnceBeginInitialize failed with %lu\n", GetLastError());
248 if (!bRet)
249 {
250 return;
251 }
252 ok(fPending, "fPending is not TRUE after a success InitOnceBeginInitialize\n");
253 if (!fPending)
254 {
255 return;
256 }
257
258 /* Complete the initialization once */
259 InitWorker(&ulInitCount, &ulTempContext);
260 bRet = pfnInitOnceComplete(&InitOnce, INIT_ONCE_ASYNC, (LPVOID)ulTempContext);
261 ok(bRet, "InitOnceComplete failed with %lu\n", GetLastError());
262 if (!bRet)
263 {
264 return;
265 }
266
267 /* Subsequent InitOnceComplete should fail with ERROR_GEN_FAILURE */
268 bRet = pfnInitOnceComplete(&InitOnce, INIT_ONCE_ASYNC, (LPVOID)ulTempContext);
269 ok(!bRet, "InitOnceComplete should not success\n");
270 if (!bRet)
271 {
272 dwError = GetLastError();
273 ok(dwError == ERROR_GEN_FAILURE,
274 "Last error is %lu, but %u is expected\n",
275 dwError,
276 ERROR_GEN_FAILURE);
277 }
278
279 /*
280 * Initialization is completed, call InitOnceBeginInitialize with
281 * INIT_ONCE_CHECK_ONLY should retrieve status and context data successfully
282 */
283 bRet = pfnInitOnceBeginInitialize(&InitOnce,
284 INIT_ONCE_CHECK_ONLY,
285 &fPending,
286 (LPVOID*)&ulContextData);
287 ok(bRet && !fPending && ulContextData == g_ulRandom,
288 "InitOnceBeginInitialize returns incorrect result for a completed initialization\n");
289
290 return;
291}