Reactos
1/*
2 * Copyright 2021 Arkadiusz Hiler for CodeWeavers
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19#include <errno.h>
20#include <stdarg.h>
21#include <process.h>
22
23#include <windef.h>
24#include <winbase.h>
25#include "wine/test.h"
26
27#include "threaddll.h"
28
29typedef void (__cdecl *_beginthread_start_routine_t)(void *);
30typedef unsigned int (__stdcall *_beginthreadex_start_routine_t)(void *);
31
32enum beginthread_method
33{
34 use_beginthread,
35 use_beginthreadex
36};
37
38static char *get_thread_dll_path(void)
39{
40 static char path[MAX_PATH];
41 const char dll_name[] = "threaddll.dll";
42 DWORD written;
43 HANDLE file;
44 HRSRC res;
45 void *ptr;
46
47 GetTempPathA(ARRAY_SIZE(path), path);
48 strcat(path, dll_name);
49
50 file = CreateFileA(path, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
51 ok(file != INVALID_HANDLE_VALUE, "Failed to create file %s: %lu.\n",
52 debugstr_a(path), GetLastError());
53
54 res = FindResourceA(NULL, dll_name, "TESTDLL");
55 ok(!!res, "Failed to load resource: %lu\n", GetLastError());
56 ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res));
57 WriteFile(file, ptr, SizeofResource( GetModuleHandleA(NULL), res), &written, NULL);
58 ok(written == SizeofResource(GetModuleHandleA(NULL), res), "Failed to write resource\n");
59 CloseHandle(file);
60
61 return path;
62}
63
64static void set_thead_dll_detach_event(HANDLE dll, HANDLE event)
65{
66 void (WINAPI *_set_detach_event)(HANDLE event);
67 _set_detach_event = (void*) GetProcAddress(dll, "set_detach_event");
68 ok(_set_detach_event != NULL, "Failed to get set_detach_event: %lu\n", GetLastError());
69 _set_detach_event(event);
70}
71
72static void test_thread_library_reference(char *thread_dll,
73 enum beginthread_method beginthread_method,
74 enum thread_exit_method exit_method)
75{
76 HANDLE detach_event;
77 HMODULE dll;
78 DWORD ret;
79 uintptr_t thread_handle;
80 struct threaddll_args args;
81
82 args.exit_method = exit_method;
83
84 detach_event = CreateEventA(NULL, FALSE, FALSE, NULL);
85 ok(detach_event != NULL, "Failed to create an event: %lu\n", GetLastError());
86 args.confirm_running = CreateEventA(NULL, FALSE, FALSE, NULL);
87 ok(args.confirm_running != NULL, "Failed to create an event: %lu\n", GetLastError());
88 args.past_free = CreateEventA(NULL, FALSE, FALSE, NULL);
89 ok(args.past_free != NULL, "Failed to create an event: %lu\n", GetLastError());
90
91 dll = LoadLibraryA(thread_dll);
92 ok(dll != NULL, "Failed to load the test dll: %lu\n", GetLastError());
93
94 set_thead_dll_detach_event(dll, detach_event);
95
96 if (beginthread_method == use_beginthreadex)
97 {
98 _beginthreadex_start_routine_t proc = (void*) GetProcAddress(dll, "stdcall_thread_proc");
99 ok(proc != NULL, "Failed to get stdcall_thread_proc: %lu\n", GetLastError());
100 thread_handle = _beginthreadex(NULL, 0, proc, &args, 0, NULL);
101 }
102 else
103 {
104 _beginthread_start_routine_t proc = (void*) GetProcAddress(dll, "cdecl_thread_proc");
105 ok(proc != NULL, "Failed to get stdcall_thread_proc: %lu\n", GetLastError());
106 thread_handle = _beginthread(proc, 0, &args);
107 }
108
109 ok(thread_handle != -1 && thread_handle != 0, "Failed to begin thread: %u\n", errno);
110
111 ret = FreeLibrary(dll);
112 ok(ret, "Failed to free the library: %lu\n", GetLastError());
113
114 ret = WaitForSingleObject(args.confirm_running, 200);
115 ok(ret == WAIT_OBJECT_0, "Event was not signaled, ret: %lu, err: %lu\n", ret, GetLastError());
116
117 ret = WaitForSingleObject(detach_event, 0);
118 ok(ret == WAIT_TIMEOUT, "Thread detach happened unexpectedly signaling an event, ret: %ld, err: %lu\n", ret, GetLastError());
119
120 ret = SetEvent(args.past_free);
121 ok(ret, "Failed to signal event: %ld\n", GetLastError());
122
123 if (beginthread_method == use_beginthreadex)
124 {
125 ret = WaitForSingleObject((HANDLE)thread_handle, 200);
126 ok(ret == WAIT_OBJECT_0, "Thread has not exited, ret: %ld, err: %lu\n", ret, GetLastError());
127 }
128
129 ret = WaitForSingleObject(detach_event, 200);
130 ok(ret == WAIT_OBJECT_0, "Detach event was not signaled, ret: %ld, err: %lu\n", ret, GetLastError());
131
132 if (beginthread_method == use_beginthreadex)
133 CloseHandle((HANDLE)thread_handle);
134
135 CloseHandle(args.past_free);
136 CloseHandle(args.confirm_running);
137 CloseHandle(detach_event);
138}
139
140static BOOL handler_called;
141
142void CDECL test_invalid_parameter_handler(const wchar_t *expression,
143 const wchar_t *function_name,
144 const wchar_t *file_name,
145 unsigned line_number,
146 uintptr_t reserved)
147{
148 handler_called = TRUE;
149}
150
151static void test_thread_invalid_params(void)
152{
153 uintptr_t hThread;
154 _invalid_parameter_handler old = _set_invalid_parameter_handler(test_invalid_parameter_handler);
155
156 errno = 0;
157 handler_called = FALSE;
158 hThread = _beginthreadex(NULL, 0, NULL, NULL, 0, NULL);
159 ok(hThread == 0, "_beginthreadex unexpected ret: %Iu\n", hThread);
160 ok(errno == EINVAL, "_beginthreadex unexpected errno: %d\n", errno);
161 ok(handler_called, "Expected invalid_parameter_handler to be called\n");
162
163 errno = 0;
164 handler_called = FALSE;
165 hThread = _beginthread(NULL, 0, NULL);
166 ok(hThread == -1, "_beginthread unexpected ret: %Iu\n", hThread);
167 ok(errno == EINVAL, "_beginthread unexpected errno: %d\n", errno);
168 ok(handler_called, "Expected invalid_parameter_handler to be called\n");
169
170 _set_invalid_parameter_handler(old);
171}
172
173START_TEST(thread)
174{
175 BOOL ret;
176 char *thread_dll = get_thread_dll_path();
177
178 test_thread_library_reference(thread_dll, use_beginthread, thread_exit_return);
179 test_thread_library_reference(thread_dll, use_beginthread, thread_exit_endthread);
180 test_thread_library_reference(thread_dll, use_beginthreadex, thread_exit_return);
181 test_thread_library_reference(thread_dll, use_beginthreadex, thread_exit_endthreadex);
182
183 ret = DeleteFileA(thread_dll);
184 ok(ret, "Failed to remove the test dll, err: %lu\n", GetLastError());
185
186 test_thread_invalid_params();
187}