185 lines
6.6 KiB
C
185 lines
6.6 KiB
C
/*
|
|
* Copyright 2021 Arkadiusz Hiler for CodeWeavers
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <process.h>
|
|
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
#include "wine/test.h"
|
|
|
|
#include "threaddll.h"
|
|
|
|
enum beginthread_method
|
|
{
|
|
use_beginthread,
|
|
use_beginthreadex
|
|
};
|
|
|
|
static char *get_thread_dll_path(void)
|
|
{
|
|
static char path[MAX_PATH];
|
|
const char dll_name[] = "threaddll.dll";
|
|
DWORD written;
|
|
HANDLE file;
|
|
HRSRC res;
|
|
void *ptr;
|
|
|
|
GetTempPathA(ARRAY_SIZE(path), path);
|
|
strcat(path, dll_name);
|
|
|
|
file = CreateFileA(path, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
|
|
ok(file != INVALID_HANDLE_VALUE, "Failed to create file %s: %u.\n",
|
|
debugstr_a(path), GetLastError());
|
|
|
|
res = FindResourceA(NULL, dll_name, "TESTDLL");
|
|
ok(!!res, "Failed to load resource: %u\n", GetLastError());
|
|
ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res));
|
|
WriteFile(file, ptr, SizeofResource( GetModuleHandleA(NULL), res), &written, NULL);
|
|
ok(written == SizeofResource(GetModuleHandleA(NULL), res), "Failed to write resource\n");
|
|
CloseHandle(file);
|
|
|
|
return path;
|
|
}
|
|
|
|
static void set_thead_dll_detach_event(HANDLE dll, HANDLE event)
|
|
{
|
|
void WINAPI (*_set_detach_event)(HANDLE event);
|
|
_set_detach_event = (void*) GetProcAddress(dll, "set_detach_event");
|
|
ok(_set_detach_event != NULL, "Failed to get set_detach_event: %u\n", GetLastError());
|
|
_set_detach_event(event);
|
|
}
|
|
|
|
static void test_thread_library_reference(char *thread_dll,
|
|
enum beginthread_method beginthread_method,
|
|
enum thread_exit_method exit_method)
|
|
{
|
|
HANDLE detach_event;
|
|
HMODULE dll;
|
|
DWORD ret;
|
|
uintptr_t thread_handle;
|
|
struct threaddll_args args;
|
|
|
|
args.exit_method = exit_method;
|
|
|
|
detach_event = CreateEventA(NULL, FALSE, FALSE, NULL);
|
|
ok(detach_event != NULL, "Failed to create an event: %u\n", GetLastError());
|
|
args.confirm_running = CreateEventA(NULL, FALSE, FALSE, NULL);
|
|
ok(args.confirm_running != NULL, "Failed to create an event: %u\n", GetLastError());
|
|
args.past_free = CreateEventA(NULL, FALSE, FALSE, NULL);
|
|
ok(args.past_free != NULL, "Failed to create an event: %u\n", GetLastError());
|
|
|
|
dll = LoadLibraryA(thread_dll);
|
|
ok(dll != NULL, "Failed to load the test dll: %u\n", GetLastError());
|
|
|
|
set_thead_dll_detach_event(dll, detach_event);
|
|
|
|
if (beginthread_method == use_beginthreadex)
|
|
{
|
|
_beginthreadex_start_routine_t proc = (void*) GetProcAddress(dll, "stdcall_thread_proc");
|
|
ok(proc != NULL, "Failed to get stdcall_thread_proc: %u\n", GetLastError());
|
|
thread_handle = _beginthreadex(NULL, 0, proc, &args, 0, NULL);
|
|
}
|
|
else
|
|
{
|
|
_beginthread_start_routine_t proc = (void*) GetProcAddress(dll, "cdecl_thread_proc");
|
|
ok(proc != NULL, "Failed to get stdcall_thread_proc: %u\n", GetLastError());
|
|
thread_handle = _beginthread(proc, 0, &args);
|
|
}
|
|
|
|
ok(thread_handle != -1 && thread_handle != 0, "Failed to begin thread: %u\n", errno);
|
|
|
|
ret = FreeLibrary(dll);
|
|
ok(ret, "Failed to free the library: %u\n", GetLastError());
|
|
|
|
ret = WaitForSingleObject(args.confirm_running, 200);
|
|
ok(ret == WAIT_OBJECT_0, "Event was not signaled, ret: %u, err: %u\n", ret, GetLastError());
|
|
|
|
ret = WaitForSingleObject(detach_event, 0);
|
|
ok(ret == WAIT_TIMEOUT, "Thread detach happened unexpectedly signaling an event, ret: %d, err: %u\n", ret, GetLastError());
|
|
|
|
ret = SetEvent(args.past_free);
|
|
ok(ret, "Failed to signal event: %d\n", GetLastError());
|
|
|
|
if (beginthread_method == use_beginthreadex)
|
|
{
|
|
ret = WaitForSingleObject((HANDLE)thread_handle, 200);
|
|
ok(ret == WAIT_OBJECT_0, "Thread has not exited, ret: %d, err: %u\n", ret, GetLastError());
|
|
}
|
|
|
|
ret = WaitForSingleObject(detach_event, 200);
|
|
ok(ret == WAIT_OBJECT_0, "Detach event was not signaled, ret: %d, err: %u\n", ret, GetLastError());
|
|
|
|
if (beginthread_method == use_beginthreadex)
|
|
CloseHandle((HANDLE)thread_handle);
|
|
|
|
CloseHandle(args.past_free);
|
|
CloseHandle(args.confirm_running);
|
|
CloseHandle(detach_event);
|
|
}
|
|
|
|
static BOOL handler_called;
|
|
|
|
void CDECL test_invalid_parameter_handler(const wchar_t *expression,
|
|
const wchar_t *function_name,
|
|
const wchar_t *file_name,
|
|
unsigned line_number,
|
|
uintptr_t reserved)
|
|
{
|
|
handler_called = TRUE;
|
|
}
|
|
|
|
static void test_thread_invalid_params(void)
|
|
{
|
|
uintptr_t hThread;
|
|
_invalid_parameter_handler old = _set_invalid_parameter_handler(test_invalid_parameter_handler);
|
|
|
|
errno = 0;
|
|
handler_called = FALSE;
|
|
hThread = _beginthreadex(NULL, 0, NULL, NULL, 0, NULL);
|
|
ok(hThread == 0, "_beginthreadex unexpected ret: %d\n", hThread);
|
|
ok(errno == EINVAL, "_beginthreadex unexpected errno: %d\n", errno);
|
|
ok(handler_called, "Expected invalid_parameter_handler to be called\n");
|
|
|
|
errno = 0;
|
|
handler_called = FALSE;
|
|
hThread = _beginthread(NULL, 0, NULL);
|
|
ok(hThread == -1, "_beginthread unexpected ret: %d\n", hThread);
|
|
ok(errno == EINVAL, "_beginthread unexpected errno: %d\n", errno);
|
|
ok(handler_called, "Expected invalid_parameter_handler to be called\n");
|
|
|
|
_set_invalid_parameter_handler(old);
|
|
}
|
|
|
|
START_TEST(thread)
|
|
{
|
|
BOOL ret;
|
|
char *thread_dll = get_thread_dll_path();
|
|
|
|
test_thread_library_reference(thread_dll, use_beginthread, thread_exit_return);
|
|
test_thread_library_reference(thread_dll, use_beginthread, thread_exit_endthread);
|
|
test_thread_library_reference(thread_dll, use_beginthreadex, thread_exit_return);
|
|
test_thread_library_reference(thread_dll, use_beginthreadex, thread_exit_endthreadex);
|
|
|
|
ret = DeleteFileA(thread_dll);
|
|
ok(ret, "Failed to remove the test dll, err: %u\n", GetLastError());
|
|
|
|
test_thread_invalid_params();
|
|
}
|