Signed-off-by: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/kernel32/tests/Makefile.in | 2 +
dlls/kernel32/tests/actctx.c | 2 +-
dlls/kernel32/tests/loader.c | 579 +++++++++++++++++++++++++++
dlls/kernel32/tests/locking_dll.c | 50 +++
dlls/kernel32/tests/locking_dll.spec | 1 +
5 files changed, 633 insertions(+), 1 deletion(-)
create mode 100644 dlls/kernel32/tests/locking_dll.c
create mode 100644 dlls/kernel32/tests/locking_dll.spec
diff --git a/dlls/kernel32/tests/Makefile.in b/dlls/kernel32/tests/Makefile.in
index e9516603ce9..be3a8fc8dfa 100644
--- a/dlls/kernel32/tests/Makefile.in
+++ b/dlls/kernel32/tests/Makefile.in
@@ -21,6 +21,8 @@ SOURCES = \
heap.c \
loader.c \
locale.c \
+ locking_dll.c \
+ locking_dll.spec \
mailslot.c \
module.c \
path.c \
diff --git a/dlls/kernel32/tests/actctx.c b/dlls/kernel32/tests/actctx.c
index 163ea405222..f37796a6aba 100644
--- a/dlls/kernel32/tests/actctx.c
+++ b/dlls/kernel32/tests/actctx.c
@@ -2584,7 +2584,7 @@ static void delete_manifest_file(const char *filename)
DeleteFileA(path);
}
-static void extract_resource(const char *name, const char *type, const char *path)
+void extract_resource(const char *name, const char *type, const char *path)
{
DWORD written;
HANDLE file;
diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c
index 4f1b11338a6..df530055db4 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -70,6 +70,10 @@ static NTSTATUS (WINAPI *pLdrLockLoaderLock)(ULONG, ULONG *, ULONG_PTR *);
static NTSTATUS (WINAPI *pLdrUnlockLoaderLock)(ULONG, ULONG_PTR);
static NTSTATUS (WINAPI *pLdrLoadDll)(LPCWSTR,DWORD,const UNICODE_STRING *,HMODULE*);
static NTSTATUS (WINAPI *pLdrUnloadDll)(HMODULE);
+
+typedef void (CALLBACK *LDRENUMPROC)(LDR_DATA_TABLE_ENTRY *, void *, BOOLEAN *);
+static NTSTATUS (WINAPI *pLdrEnumerateLoadedModules)( void *unknown, LDRENUMPROC callback, void *context );
+
static void (WINAPI *pRtlInitUnicodeString)(PUNICODE_STRING,LPCWSTR);
static void (WINAPI *pRtlAcquirePebLock)(void);
static void (WINAPI *pRtlReleasePebLock)(void);
@@ -88,6 +92,8 @@ static BOOL (WINAPI *pWow64DisableWow64FsRedirection)(void **);
static BOOL (WINAPI *pWow64RevertWow64FsRedirection)(void *);
static HMODULE (WINAPI *pLoadPackagedLibrary)(LPCWSTR lpwLibFileName, DWORD Reserved);
+void extract_resource(const char *name, const char *type, const char *path);
+
static PVOID RVAToAddr(DWORD_PTR rva, HMODULE module)
{
if (rva == 0)
@@ -4036,6 +4042,574 @@ static void test_Wow64Transition(void)
debugstr_wn(name->SectionFileName.Buffer, name->SectionFileName.Length / sizeof(WCHAR)));
}
+static BOOL test_loader_lock_repeat_lock;
+static DWORD test_loader_lock_expected_wait_result;
+static unsigned int test_loader_notification_count;
+static unsigned int test_loader_expected_notification_count;
+static const WCHAR *ldr_notify_track_dll;
+static HMODULE lock_dll_handle;
+static HANDLE lock_ready_event, next_test_event;
+static BOOL test_loader_lock_test_failed;
+static volatile LONG test_loader_lock_timeout_count;
+
+#define BLOCKING_TESTS_ENABLED 1
+
+static void CALLBACK test_loader_lock_module_enum_locking_callback(LDR_DATA_TABLE_ENTRY *mod,
+ void *context, BOOLEAN *stop)
+{
+ DWORD *result = context;
+
+ SetEvent(lock_ready_event);
+ *result = WaitForSingleObject(next_test_event, 3000);
+ *stop = TRUE;
+}
+
+static void CALLBACK test_loader_lock_dummy_callback(LDR_DATA_TABLE_ENTRY *mod,
+ void *context, BOOLEAN *stop)
+{
+ *stop = TRUE;
+}
+
+static void CALLBACK test_loader_lock_ldr_notify_locking(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+ DWORD *result = context;
+ NTSTATUS status;
+ HMODULE hmodule;
+ void *cookie;
+ BOOL bret;
+
+ if (reason != LDR_DLL_NOTIFICATION_REASON_UNLOADED) return;
+
+ status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify_locking, NULL, &cookie);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ status = LdrUnregisterDllNotification( cookie );
+ ok(!status, "Got unexpected status %#x.\n", status);
+
+ hmodule = LoadLibraryW(L"winmm.dll");
+ ok(!!hmodule, "GetModuleHandleW failed, err %u.\n", GetLastError());
+ bret = FreeLibrary(hmodule);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+ bret = GetModuleHandleExW(0, L"lock.dll", &hmodule);
+ ok(bret, "GetModuleHandleExW failed, err %u.\n", GetLastError());
+ ok(!!hmodule, "GetModuleHandleW failed, err %u.\n", GetLastError());
+ bret = FreeLibrary(hmodule);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+ hmodule = LoadLibraryW(L"authz.dll");
+ ok(!hmodule, "LoadLibraryW succeeded.\n");
+ ok(GetLastError() == ERROR_NOT_FOUND, "Got unexpected error %u.\n", GetLastError());
+
+ status = pLdrEnumerateLoadedModules(NULL, test_loader_lock_dummy_callback, NULL);
+ ok(!status, "Got unexpected status %#x.\n", status);
+
+ SetEvent(lock_ready_event);
+ *result = WaitForSingleObject(next_test_event, 3000);
+}
+
+static DWORD WINAPI test_loader_lock_thread(void *param)
+{
+ static const WCHAR env_var[] = L"test_wait_reason_value";
+ void (WINAPI *p_set_lock_result_addr)( DWORD *addr );
+ unsigned int test, iter;
+ ULONG_PTR magic;
+ NTSTATUS status;
+ HMODULE hmodule;
+ void *cookie;
+ DWORD result;
+ WCHAR str[8];
+ BOOL bret;
+
+ WaitForSingleObject(next_test_event, INFINITE);
+
+ test = 1;
+ /* 1. Test with loader lock held. */
+ iter = 0;
+ do
+ {
+ status = pLdrLockLoaderLock(0, NULL, &magic);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ SetEvent(lock_ready_event);
+
+ result = WaitForSingleObject(next_test_event, 3000);
+
+ if (!iter && result == WAIT_TIMEOUT)
+ {
+ /* First test timed out, likely the old loader. */
+ test_loader_lock_test_failed = TRUE;
+ status = pLdrUnlockLoaderLock(0, magic);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ SetEvent(lock_ready_event);
+ return 0;
+ }
+
+ ok(result == test_loader_lock_expected_wait_result,
+ "Got unexpected wait result %#x, expected %#x, test %u.%u.\n",
+ result, test_loader_lock_expected_wait_result, test, iter);
+
+ todo_wine_if(test && iter == 1)
+ ok(test_loader_notification_count == test_loader_expected_notification_count,
+ "Got unexpected notification count %u, expected %u, test %u.%u.\n",
+ test_loader_notification_count, test_loader_expected_notification_count, test, iter);
+
+ status = pLdrUnlockLoaderLock(0, magic);
+ ok(!status, "Got unexpected status %#x.\n", status);
+
+ if (result == WAIT_TIMEOUT)
+ WaitForSingleObject(next_test_event, INFINITE);
+ ++iter;
+ } while (test_loader_lock_repeat_lock);
+
+ SetEvent(lock_ready_event);
+ WaitForSingleObject(next_test_event, INFINITE);
+ /* 2. Test with the thread blocked in DLL entry point during process attach. */
+ swprintf(str, ARRAY_SIZE(str), L"%u", DLL_PROCESS_ATTACH);
+ bret = SetEnvironmentVariableW(env_var, str);
+ ok(bret, "SetEnvironmentVariableW failed.\n");
+ ++test;
+ iter = 0;
+ do
+ {
+ hmodule = LoadLibraryA("lock.dll");
+ ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+ p_set_lock_result_addr = (void *)GetProcAddress(hmodule, "set_lock_result_addr");
+ p_set_lock_result_addr(&result);
+
+ if (result == WAIT_TIMEOUT)
+ InterlockedIncrement(&test_loader_lock_timeout_count);
+
+ bret = FreeLibrary(hmodule);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+ ok(result == test_loader_lock_expected_wait_result,
+ "Got unexpected wait result %#x, expected %#x, test %u.%u.\n",
+ result, test_loader_lock_expected_wait_result, test, iter);
+
+ if (result == WAIT_TIMEOUT)
+ WaitForSingleObject(next_test_event, INFINITE);
+ ++iter;
+ } while (test_loader_lock_repeat_lock);
+
+ SetEvent(lock_ready_event);
+ WaitForSingleObject(next_test_event, INFINITE);
+
+ /* 3. Test with the thread blocked in DLL entry point during process detach. */
+ swprintf(str, ARRAY_SIZE(str), L"%u", DLL_PROCESS_DETACH);
+ bret = SetEnvironmentVariableW(env_var, str);
+ ok(bret, "SetEnvironmentVariableW failed.\n");
+ ++test;
+ iter = 0;
+ do
+ {
+ lock_dll_handle = hmodule = LoadLibraryA("lock.dll");
+ ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+ p_set_lock_result_addr = (void *)GetProcAddress(hmodule, "set_lock_result_addr");
+ ok(!!p_set_lock_result_addr, "Got NULL p_set_lock_result_addr.\n");
+ p_set_lock_result_addr(&result);
+ bret = FreeLibrary(hmodule);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+ lock_dll_handle = NULL;
+
+ if (result == WAIT_TIMEOUT)
+ InterlockedIncrement(&test_loader_lock_timeout_count);
+
+ ok(result == test_loader_lock_expected_wait_result,
+ "Got unexpected wait result %#x, expected %#x, test %u.%u.\n",
+ result, test_loader_lock_expected_wait_result, test, iter);
+
+ if (result == WAIT_TIMEOUT)
+ WaitForSingleObject(next_test_event, INFINITE);
+ ++iter;
+ } while (test_loader_lock_repeat_lock);
+
+ SetEvent(lock_ready_event);
+ WaitForSingleObject(next_test_event, INFINITE);
+
+ /* 4. Test with the thread blocked in LdrEnumerateLoadedModules callback. */
+ ++test;
+ iter = 0;
+ do
+ {
+ pLdrEnumerateLoadedModules(NULL, test_loader_lock_module_enum_locking_callback, &result);
+
+ if (result == WAIT_TIMEOUT)
+ InterlockedIncrement(&test_loader_lock_timeout_count);
+
+ ok(result == test_loader_lock_expected_wait_result,
+ "Got unexpected wait result %#x, expected %#x, test %u.%u.\n",
+ result, test_loader_lock_expected_wait_result, test, iter);
+ ok(test_loader_notification_count == test_loader_expected_notification_count,
+ "Got unexpected notification count %u, expected %u, test %u.%u.\n",
+ test_loader_notification_count, test_loader_expected_notification_count, test, iter);
+
+ if (result == WAIT_TIMEOUT)
+ WaitForSingleObject(next_test_event, INFINITE);
+ ++iter;
+ } while (test_loader_lock_repeat_lock);
+
+ SetEvent(lock_ready_event);
+ WaitForSingleObject(next_test_event, INFINITE);
+
+ /* 5. Test with the thread blocked in LDR notification callback. */
+ LdrRegisterDllNotification(0, test_loader_lock_ldr_notify_locking, &result, &cookie);
+ /* lock.dll will return FALSE from the DLL entry point. */
+ bret = SetEnvironmentVariableW(env_var, L"-1");
+ ok(bret, "SetEnvironmentVariableW failed.\n");
+
+ ++test;
+ iter = 0;
+ do
+ {
+ hmodule = LoadLibraryA("lock.dll");
+ ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+ bret = FreeLibrary(hmodule);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+ if (result == WAIT_TIMEOUT)
+ InterlockedIncrement(&test_loader_lock_timeout_count);
+
+ ok(result == test_loader_lock_expected_wait_result,
+ "Got unexpected wait result %#x, expected %#x, test %u.%u.\n",
+ result, test_loader_lock_expected_wait_result, test, iter);
+ ok(test_loader_notification_count == test_loader_expected_notification_count,
+ "Got unexpected notification count %u, expected %u, test %u.%u.\n",
+ test_loader_notification_count, test_loader_expected_notification_count, test, iter);
+
+ if (result == WAIT_TIMEOUT)
+ WaitForSingleObject(next_test_event, INFINITE);
+ ++iter;
+ } while (test_loader_lock_repeat_lock);
+ LdrUnregisterDllNotification( cookie );
+ SetEvent(lock_ready_event);
+ return 0;
+}
+
+static void CALLBACK test_loader_lock_ldr_notify(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+ if (!lstrcmpW(data->Loaded.BaseDllName->Buffer, ldr_notify_track_dll))
+ ++test_loader_notification_count;
+}
+
+static void CALLBACK test_loader_lock_enum_callback(LDR_DATA_TABLE_ENTRY *mod, void *context, BOOLEAN *stop)
+{
+ *stop = TRUE;
+}
+
+static void test_loader_lock(void)
+{
+#define CHECK ok(!test_loader_lock_timeout_count, "Got timeout count %d.\n", test_loader_lock_timeout_count)
+ static const WCHAR not_loaded_dll_name[] = L"authz.dll";
+ static const WCHAR preloaded_dll_name[] = L"winmm.dll";
+ HMODULE hmodule_preloaded, hmodule;
+ ULONG_PTR magic;
+ NTSTATUS status;
+ HANDLE thread;
+ void *cookie;
+ void *proc;
+ BOOL bret;
+
+ extract_resource("locking_dll.dll", "TESTDLL", "lock.dll");
+
+ lock_ready_event = CreateEventA(NULL, FALSE, FALSE, "test_lock_ready_event");
+ next_test_event = CreateEventA(NULL, FALSE, FALSE, "test_next_test_event");
+
+ thread = CreateThread(NULL, 0, test_loader_lock_thread, NULL, 0, NULL);
+
+ hmodule_preloaded = LoadLibraryW(preloaded_dll_name);
+ ok(!!hmodule_preloaded, "LoadLibrary failed, err %u.\n", GetLastError());
+ hmodule = GetModuleHandleW(not_loaded_dll_name);
+ ok(!hmodule, "%s is already loaded.\n", not_loaded_dll_name);
+
+ test_loader_notification_count = 0;
+
+ /* 1. Test with loader lock held. */
+ trace("Test 1.\n");
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+ test_loader_lock_expected_wait_result = WAIT_OBJECT_0;
+
+ status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ CHECK;
+ ldr_notify_track_dll = not_loaded_dll_name;
+
+ bret = GetModuleHandleExW(0, preloaded_dll_name, &hmodule);
+ ok(bret, "GetModuleHandleEx failed, err %u.\n", GetLastError());
+ ok(hmodule == hmodule_preloaded, "Got unexpected hmodule %p, expected %p.\n", hmodule, hmodule_preloaded);
+ CHECK;
+
+ bret = FreeLibrary(hmodule);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+ CHECK;
+
+ ok(!test_loader_notification_count, "Got unexpected test_loader_notification_count %u.\n", test_loader_notification_count);
+
+ if (BLOCKING_TESTS_ENABLED)
+ {
+ CHECK;
+ test_loader_lock_repeat_lock = TRUE;
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+
+ if (test_loader_lock_test_failed)
+ {
+ win_skip("Old loader, tests skipped.\n");
+ goto done;
+ }
+
+ /* With loader lock held notification callback is called which should mean that:
+ * - LDR notifications themselves do not wait on loader lock;
+ * - The library load goes far enough to call the LDR notification until it blocks on the loader lock.
+ */
+ test_loader_expected_notification_count = 1;
+ test_loader_lock_expected_wait_result = WAIT_TIMEOUT;
+ hmodule = LoadLibraryW(not_loaded_dll_name);
+ ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+
+ test_loader_notification_count = 0;
+ test_loader_expected_notification_count = 0;
+ bret = FreeLibrary(hmodule);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+ ok(test_loader_notification_count == 1, "Got unexpected notification count %u, expected %u.\n",
+ test_loader_notification_count, 1);
+ test_loader_notification_count = 0;
+ test_loader_expected_notification_count = 0;
+
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+ pLdrEnumerateLoadedModules(NULL, test_loader_lock_enum_callback, NULL);
+
+ test_loader_lock_repeat_lock = FALSE;
+ }
+
+ LdrUnregisterDllNotification( cookie );
+
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+
+ if (test_loader_lock_test_failed)
+ {
+ win_skip("Old loader, tests skipped.\n");
+ goto done;
+ }
+
+ /* 2. Test with the thread blocked in DLL entry point during process attach. */
+ trace("Test 2.\n");
+ SetEvent(next_test_event);
+
+ WaitForSingleObject(lock_ready_event, INFINITE);
+ test_loader_lock_expected_wait_result = WAIT_OBJECT_0;
+
+ status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ CHECK;
+
+ bret = GetModuleHandleExW(0, preloaded_dll_name, &hmodule);
+ CHECK;
+ ok(bret, "GetModuleHandleEx failed, err %u.\n", GetLastError());
+ ok(hmodule == hmodule_preloaded, "Got unexpected hmodule %p, expected %p.\n", hmodule, hmodule_preloaded);
+
+ proc = GetProcAddress(hmodule, "timeGetTime");
+ CHECK;
+ ok(!!proc, "GetProcAddress failed.\n");
+
+ bret = FreeLibrary(hmodule);
+ CHECK;
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+ hmodule = GetModuleHandleA("lock.dll");
+ CHECK;
+ ok(!!hmodule, "GetModuleHandleA failed, err %u.\n", GetLastError());
+
+ bret = GetModuleHandleExW(0, L"lock.dll", &hmodule);
+ CHECK;
+ ok(bret, "GetModuleHandleEx failed, err %u.\n", GetLastError());
+
+ lock_dll_handle = hmodule;
+
+ status = LdrAddRefDll(0, hmodule);
+ CHECK;
+ ok(!status, "Got unexpected status %#x.\n", status);
+
+ bret = FreeLibrary(hmodule);
+ CHECK;
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+ bret = FreeLibrary(hmodule);
+ CHECK;
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+ hmodule = LoadLibraryW(preloaded_dll_name);
+ CHECK;
+ ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+
+ bret = FreeLibrary(hmodule);
+ CHECK;
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+ if (BLOCKING_TESTS_ENABLED)
+ {
+ test_loader_lock_repeat_lock = TRUE;
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+
+ test_loader_lock_expected_wait_result = WAIT_TIMEOUT;
+
+ ok(!!lock_dll_handle, "Got NULL lock_dll_handle.\n");
+ proc = GetProcAddress(lock_dll_handle, "unknown");
+ ok(!proc, "GetProcAddress succeeded.\n");
+
+ test_loader_lock_repeat_lock = FALSE;
+ }
+
+ LdrUnregisterDllNotification( cookie );
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+ /* 3. Test with the thread blocked in DLL entry point during process detach. */
+ trace("Test 3.\n");
+
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+ test_loader_lock_timeout_count = 0;
+ test_loader_lock_expected_wait_result = WAIT_OBJECT_0;
+
+ status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ CHECK;
+
+ bret = GetModuleHandleExW(0, preloaded_dll_name, &hmodule);
+ ok(bret, "GetModuleHandleEx failed, err %u.\n", GetLastError());
+ ok(hmodule == hmodule_preloaded, "Got unexpected hmodule %p, expected %p.\n", hmodule, hmodule_preloaded);
+ CHECK;
+
+ proc = GetProcAddress(hmodule, "timeGetTime");
+ ok(!!proc, "GetProcAddress failed.\n");
+ CHECK;
+
+ bret = FreeLibrary(hmodule);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+ CHECK;
+
+ ok(!!lock_dll_handle, "Got NULL lock_dll_handle.\n");
+
+ proc = GetProcAddress(lock_dll_handle, "set_lock_result_addr");
+ CHECK;
+
+ ok(!proc, "GetProcAddress failed, err %u.\n", GetLastError());
+ ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %u.\n", GetLastError());
+
+ status = LdrAddRefDll(0, lock_dll_handle);
+ CHECK;
+ ok(status == STATUS_DLL_NOT_FOUND, "Got unexpected status %#x.\n", status);
+
+ if (BLOCKING_TESTS_ENABLED)
+ {
+ test_loader_lock_repeat_lock = TRUE;
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+
+ test_loader_lock_expected_wait_result = WAIT_TIMEOUT;
+ hmodule = GetModuleHandleW(L"lock.dll");
+ ok(!hmodule, "GetModuleHandleW succeeded.\n", GetLastError());
+ ok(GetLastError() == ERROR_MOD_NOT_FOUND, "Got unexpected error %u.\n", GetLastError());
+ test_loader_lock_repeat_lock = FALSE;
+ }
+
+ LdrUnregisterDllNotification( cookie );
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+
+ /* 4. Test with the thread blocked in LdrEnumerateLoadedModules callback. */
+ trace("Test 4.\n");
+
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+ test_loader_lock_timeout_count = 0;
+ test_loader_lock_expected_wait_result = WAIT_OBJECT_0;
+
+ status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ CHECK;
+
+ hmodule = GetModuleHandleW(preloaded_dll_name);
+ ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+ CHECK;
+
+ if (BLOCKING_TESTS_ENABLED)
+ {
+ test_loader_lock_repeat_lock = TRUE;
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+
+ test_loader_lock_expected_wait_result = WAIT_TIMEOUT;
+
+ status = pLdrLockLoaderLock(0, NULL, &magic);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ status = pLdrUnlockLoaderLock(0, magic);
+ ok(!status, "Got unexpected status %#x.\n", status);
+
+ test_loader_lock_repeat_lock = FALSE;
+ }
+
+ LdrUnregisterDllNotification( cookie );
+
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+
+ /* 5. Test with the thread blocked in LDR notification callback. */
+ trace("Test 5.\n");
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+
+ test_loader_lock_timeout_count = 0;
+ test_loader_lock_expected_wait_result = WAIT_OBJECT_0;
+
+ hmodule = GetModuleHandleW(preloaded_dll_name);
+ ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+ CHECK;
+
+ if (BLOCKING_TESTS_ENABLED)
+ {
+ test_loader_lock_repeat_lock = TRUE;
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+ test_loader_lock_expected_wait_result = WAIT_TIMEOUT;
+
+ status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ LdrUnregisterDllNotification( cookie );
+
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+
+ /* This doesn't block in load notifications on Windows, only unload. */
+ status = pLdrLockLoaderLock(0, NULL, &magic);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ status = pLdrUnlockLoaderLock(0, magic);
+ ok(!status, "Got unexpected status %#x.\n", status);
+
+ test_loader_lock_repeat_lock = FALSE;
+ }
+
+ SetEvent(next_test_event);
+ WaitForSingleObject(lock_ready_event, INFINITE);
+
+
+ WaitForSingleObject(thread, INFINITE);
+ CloseHandle(thread);
+
+done:
+ DeleteFileA("lock.dll");
+ FreeLibrary(hmodule_preloaded);
+ CloseHandle(lock_ready_event);
+ CloseHandle(next_test_event);
+}
+
START_TEST(loader)
{
int argc;
@@ -4060,6 +4634,7 @@ START_TEST(loader)
pLdrUnlockLoaderLock = (void *)GetProcAddress(ntdll, "LdrUnlockLoaderLock");
pLdrLoadDll = (void *)GetProcAddress(ntdll, "LdrLoadDll");
pLdrUnloadDll = (void *)GetProcAddress(ntdll, "LdrUnloadDll");
+ pLdrEnumerateLoadedModules = (void *)GetProcAddress(ntdll, "LdrEnumerateLoadedModules");
pRtlInitUnicodeString = (void *)GetProcAddress(ntdll, "RtlInitUnicodeString");
pRtlAcquirePebLock = (void *)GetProcAddress(ntdll, "RtlAcquirePebLock");
pRtlReleasePebLock = (void *)GetProcAddress(ntdll, "RtlReleasePebLock");
@@ -4099,6 +4674,7 @@ START_TEST(loader)
return;
}
+if(0){
test_filenames();
test_ResolveDelayLoadedAPI();
test_ImportDescriptors();
@@ -4113,6 +4689,9 @@ START_TEST(loader)
test_dll_file( "advapi32.dll" );
test_dll_file( "user32.dll" );
test_Wow64Transition();
+}
+ test_loader_lock();
+return;
/* loader test must be last, it can corrupt the internal loader state on Windows */
test_Loader();
}
diff --git a/dlls/kernel32/tests/locking_dll.c b/dlls/kernel32/tests/locking_dll.c
new file mode 100644
index 00000000000..31aff05fe84
--- /dev/null
+++ b/dlls/kernel32/tests/locking_dll.c
@@ -0,0 +1,50 @@
+#include <stdarg.h>
+#include <stdlib.h>
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+
+static DWORD last_lock_result, *lock_result_addr;
+
+BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, LPVOID reserved )
+{
+ HANDLE lock_ready_event, next_test_event;
+ DWORD wait_reason;
+ NTSTATUS status;
+ char str[8];
+
+ GetEnvironmentVariableA("test_wait_reason_value", str, sizeof(str));
+ wait_reason = atoi(str);
+
+ if (reason != wait_reason) return TRUE;
+
+ lock_ready_event = OpenEventA(EVENT_ALL_ACCESS, FALSE, "test_lock_ready_event");
+ next_test_event = OpenEventA(EVENT_ALL_ACCESS, FALSE, "test_next_test_event");
+
+ SetEvent( lock_ready_event );
+ last_lock_result = WaitForSingleObject(next_test_event, 2000);
+
+ if (0)
+ {
+ if (reason == DLL_PROCESS_DETACH)
+ {
+ status = LdrAddRefDll(0, instance);
+ if (status) last_lock_result = ~0u;
+ status = LdrAddRefDll(LDR_ADDREF_DLL_PIN, instance);
+ if (status != STATUS_UNSUCCESSFUL) last_lock_result = ~0u - 1;
+ }
+ }
+ if (lock_result_addr) *lock_result_addr = last_lock_result;
+
+ CloseHandle(lock_ready_event);
+ CloseHandle(next_test_event);
+ return TRUE;
+}
+
+void WINAPI set_lock_result_addr( DWORD *addr )
+{
+ lock_result_addr = addr;
+ *addr = last_lock_result;
+}
diff --git a/dlls/kernel32/tests/locking_dll.spec b/dlls/kernel32/tests/locking_dll.spec
new file mode 100644
index 00000000000..327515590c3
--- /dev/null
+++ b/dlls/kernel32/tests/locking_dll.spec
@@ -0,0 +1 @@
+@ stdcall set_lock_result_addr(ptr)
--
2.31.1