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 10 typedef 11 VOID 12 WINAPI 13 FN_InitOnceInitialize(_Out_ PINIT_ONCE InitOnce); 14 15 typedef 16 BOOL 17 WINAPI 18 FN_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 24 typedef 25 BOOL 26 WINAPI 27 FN_InitOnceBeginInitialize( 28 _Inout_ LPINIT_ONCE lpInitOnce, 29 _In_ DWORD dwFlags, 30 _Out_ PBOOL fPending, 31 _Outptr_opt_result_maybenull_ LPVOID *lpContext); 32 33 typedef 34 BOOL 35 WINAPI 36 FN_InitOnceComplete( 37 _Inout_ LPINIT_ONCE lpInitOnce, 38 _In_ DWORD dwFlags, 39 _In_opt_ LPVOID lpContext); 40 41 static ULONG g_ulRandom; 42 43 static 44 VOID 45 InitWorker(_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 54 static 55 _Success_(return != FALSE) 56 BOOL 57 WINAPI 58 InitOnceProc( 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 72 START_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 } 292