[KERNEL32][RTL] Implement One-Time initialization API and improve RTL support (#5046)

* [KERNEL32][RTL] Implement One-Time initialization API and improve RTL support
This commit is contained in:
Ratin Gao 2023-09-07 06:40:53 +08:00 committed by GitHub
parent 583be404dd
commit f491d7cc99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 472 additions and 65 deletions

View file

@ -18,6 +18,7 @@ list(APPEND SOURCE
GetDriveType.c
GetModuleFileName.c
GetVolumeInformation.c
InitOnce.c
interlck.c
IsDBCSLeadByteEx.c
JapaneseCalendar.c

View file

@ -0,0 +1,280 @@
/*
* PROJECT: ReactOS API tests
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Tests for One-Time initialization APIs
* COPYRIGHT: Copyright 2023 Ratin Gao <ratin@knsoft.org>
*/
#include "precomp.h"
typedef
VOID
WINAPI
FN_InitOnceInitialize(_Out_ PINIT_ONCE InitOnce);
typedef
BOOL
WINAPI
FN_InitOnceExecuteOnce(
_Inout_ PINIT_ONCE InitOnce,
_In_ __callback PINIT_ONCE_FN InitFn,
_Inout_opt_ PVOID Parameter,
_Outptr_opt_result_maybenull_ LPVOID *Context);
typedef
BOOL
WINAPI
FN_InitOnceBeginInitialize(
_Inout_ LPINIT_ONCE lpInitOnce,
_In_ DWORD dwFlags,
_Out_ PBOOL fPending,
_Outptr_opt_result_maybenull_ LPVOID *lpContext);
typedef
BOOL
WINAPI
FN_InitOnceComplete(
_Inout_ LPINIT_ONCE lpInitOnce,
_In_ DWORD dwFlags,
_In_opt_ LPVOID lpContext);
static ULONG g_ulRandom;
static
VOID
InitWorker(_Inout_ PULONG InitCount, _Out_ PULONG_PTR Context)
{
/* Increase the initialization count */
(*InitCount)++;
/* Output context data */
*Context = (ULONG_PTR)g_ulRandom;
}
static
_Success_(return != FALSE)
BOOL
WINAPI
InitOnceProc(
_Inout_ PINIT_ONCE InitOnce,
_Inout_opt_ PVOID Parameter,
_Outptr_opt_result_maybenull_ PVOID* Context)
{
if (!Parameter || !Context)
{
return FALSE;
}
InitWorker(Parameter, (PULONG_PTR)Context);
return TRUE;
}
START_TEST(InitOnce)
{
BOOL bRet, fPending;
ULONG i, ulInitCount, ulSeed, ulContextData;
ULONG_PTR ulTempContext;
DWORD dwError;
HMODULE hKernel32;
FN_InitOnceInitialize* pfnInitOnceInitialize;
FN_InitOnceExecuteOnce* pfnInitOnceExecuteOnce;
FN_InitOnceBeginInitialize* pfnInitOnceBeginInitialize;
FN_InitOnceComplete* pfnInitOnceComplete;
/* Load functions */
hKernel32 = GetModuleHandleW(L"kernel32.dll");
if (!hKernel32)
{
skip("Module kernel32 not found\n");
return;
}
pfnInitOnceInitialize = (FN_InitOnceInitialize*)GetProcAddress(hKernel32, "InitOnceInitialize");
pfnInitOnceExecuteOnce = (FN_InitOnceExecuteOnce*)GetProcAddress(hKernel32, "InitOnceExecuteOnce");
pfnInitOnceBeginInitialize = (FN_InitOnceBeginInitialize*)GetProcAddress(hKernel32, "InitOnceBeginInitialize");
pfnInitOnceComplete = (FN_InitOnceComplete*)GetProcAddress(hKernel32, "InitOnceComplete");
/*
* Use a random as output context data,
* which the low-order INIT_ONCE_CTX_RESERVED_BITS bits should be zero.
*/
ulSeed = (ULONG)(ULONG_PTR)&ulSeed ^ GetTickCount();
g_ulRandom = RtlRandom(&ulSeed);
for (i = 0; i < INIT_ONCE_CTX_RESERVED_BITS; i++)
{
g_ulRandom &= (~(1 << i));
}
/* Initialize One-Time initialization structure */
INIT_ONCE InitOnce = { (PVOID)(ULONG_PTR)0xDEADBEEF };
if (pfnInitOnceInitialize)
{
pfnInitOnceInitialize(&InitOnce);
}
else
{
skip("InitOnceInitialize not found\n");
InitOnce = (INIT_ONCE)INIT_ONCE_STATIC_INIT;
}
if (!pfnInitOnceExecuteOnce)
{
skip("InitOnceExecuteOnce not found\n");
goto _test_sync;
}
/*
* Perform synchronous initialization by using InitOnceExecuteOnce,
* which executes user-defined callback to initialize.
* Call InitOnceExecuteOnce twice will success,
* initialization count should be 1 and retrieve correct context data.
*/
ulInitCount = 0;
ulContextData = MAXULONG;
bRet = pfnInitOnceExecuteOnce(&InitOnce, InitOnceProc, &ulInitCount, (LPVOID*)&ulContextData);
ok(bRet, "InitOnceExecuteOnce failed with %lu\n", GetLastError());
if (bRet)
{
/* Call InitOnceExecuteOnce again and check output values if the first call succeeded */
bRet = pfnInitOnceExecuteOnce(&InitOnce,
InitOnceProc,
&ulInitCount,
(LPVOID*)&ulContextData);
ok(bRet, "InitOnceExecuteOnce failed with %lu\n", GetLastError());
ok(ulInitCount == 1, "ulInitCount is not 1\n");
ok(ulContextData == g_ulRandom, "Output ulContextData is incorrect\n");
}
_test_sync:
if (!pfnInitOnceBeginInitialize || !pfnInitOnceComplete)
{
skip("InitOnceBeginInitialize or InitOnceComplete not found\n");
return;
}
/* Re-initialize One-Time initialization structure by using INIT_ONCE_STATIC_INIT */
InitOnce = (INIT_ONCE)INIT_ONCE_STATIC_INIT;
/* Perform synchronous initialization by using InitOnceBeginInitialize */
fPending = FALSE;
bRet = pfnInitOnceBeginInitialize(&InitOnce, 0, &fPending, (LPVOID*)&ulContextData);
ok(bRet, "InitOnceBeginInitialize failed with %lu\n", GetLastError());
if (!bRet)
{
goto _test_async;
}
ok(fPending, "fPending is not TRUE after the first success InitOnceBeginInitialize\n");
if (!fPending)
{
goto _test_async;
}
/* Complete the initialization */
InitWorker(&ulInitCount, &ulTempContext);
bRet = pfnInitOnceComplete(&InitOnce, 0, (LPVOID)ulTempContext);
ok(bRet, "InitOnceComplete failed with %lu\n", GetLastError());
if (!bRet)
{
goto _test_async;
}
/*
* Initialization is completed, call InitOnceBeginInitialize with
* INIT_ONCE_CHECK_ONLY should retrieve status and context data successfully
*/
bRet = pfnInitOnceBeginInitialize(&InitOnce,
INIT_ONCE_CHECK_ONLY,
&fPending,
(LPVOID*)&ulContextData);
ok(bRet && !fPending && ulContextData == g_ulRandom,
"InitOnceBeginInitialize returns incorrect result for a completed initialization\n");
_test_async:
InitOnce = (INIT_ONCE)INIT_ONCE_STATIC_INIT;
/* Perform asynchronous initialization */
fPending = FALSE;
bRet = pfnInitOnceBeginInitialize(&InitOnce,
INIT_ONCE_ASYNC,
&fPending,
(LPVOID*)&ulContextData);
ok(bRet, "InitOnceBeginInitialize failed with %lu\n", GetLastError());
if (!bRet)
{
return;
}
ok(fPending, "fPending is not TRUE after a success InitOnceBeginInitialize\n");
if (!fPending)
{
return;
}
/*
* Now the initialization is in progress but not completed yet,
* call InitOnceBeginInitialize again without INIT_ONCE_ASYNC is invalid,
* should fail with ERROR_INVALID_PARAMETER
*/
bRet = pfnInitOnceBeginInitialize(&InitOnce, 0, &fPending, (LPVOID*)&ulContextData);
ok(!bRet, "InitOnceBeginInitialize should not success\n");
if (!bRet)
{
dwError = GetLastError();
ok(dwError == ERROR_INVALID_PARAMETER,
"Last error is %lu, but %u is expected\n",
dwError,
ERROR_INVALID_PARAMETER);
}
/*
* Call InitOnceBeginInitialize again with INIT_ONCE_ASYNC
* should success because initialization could be executed in parallel
*/
bRet = pfnInitOnceBeginInitialize(&InitOnce,
INIT_ONCE_ASYNC,
&fPending,
(LPVOID*)&ulContextData);
ok(bRet, "InitOnceBeginInitialize failed with %lu\n", GetLastError());
if (!bRet)
{
return;
}
ok(fPending, "fPending is not TRUE after a success InitOnceBeginInitialize\n");
if (!fPending)
{
return;
}
/* Complete the initialization once */
InitWorker(&ulInitCount, &ulTempContext);
bRet = pfnInitOnceComplete(&InitOnce, INIT_ONCE_ASYNC, (LPVOID)ulTempContext);
ok(bRet, "InitOnceComplete failed with %lu\n", GetLastError());
if (!bRet)
{
return;
}
/* Subsequent InitOnceComplete should fail with ERROR_GEN_FAILURE */
bRet = pfnInitOnceComplete(&InitOnce, INIT_ONCE_ASYNC, (LPVOID)ulTempContext);
ok(!bRet, "InitOnceComplete should not success\n");
if (!bRet)
{
dwError = GetLastError();
ok(dwError == ERROR_GEN_FAILURE,
"Last error is %lu, but %u is expected\n",
dwError,
ERROR_GEN_FAILURE);
}
/*
* Initialization is completed, call InitOnceBeginInitialize with
* INIT_ONCE_CHECK_ONLY should retrieve status and context data successfully
*/
bRet = pfnInitOnceBeginInitialize(&InitOnce,
INIT_ONCE_CHECK_ONLY,
&fPending,
(LPVOID*)&ulContextData);
ok(bRet && !fPending && ulContextData == g_ulRandom,
"InitOnceBeginInitialize returns incorrect result for a completed initialization\n");
return;
}

View file

@ -18,6 +18,7 @@ extern void func_GetCurrentDirectory(void);
extern void func_GetDriveType(void);
extern void func_GetModuleFileName(void);
extern void func_GetVolumeInformation(void);
extern void func_InitOnce(void);
extern void func_interlck(void);
extern void func_IsDBCSLeadByteEx(void);
extern void func_JapaneseCalendar(void);
@ -53,6 +54,7 @@ const struct test winetest_testlist[] =
{ "GetDriveType", func_GetDriveType },
{ "GetModuleFileName", func_GetModuleFileName },
{ "GetVolumeInformation", func_GetVolumeInformation },
{ "InitOnce", func_InitOnce },
{ "interlck", func_interlck },
{ "IsDBCSLeadByteEx", func_IsDBCSLeadByteEx },
{ "JapaneseCalendar", func_JapaneseCalendar },