Reactos
1//
2// thread.cpp
3//
4// Copyright (c) Microsoft Corporation. All rights reserved.
5//
6// The _beginthread(), _beginthreadex(), _endthread(), and _endthreadex().
7//
8// There are several key differences in behavior between _beginthread() and
9// _beginthreadex():
10//
11// * _beginthreadex() takes three additional parameters, which are passed on to
12// CreateThread(): a security descriptor for the new thread, the initial
13// thread state (running or asleep), and an optional out parameter to which
14// the thread id of the newly created thread will be stored.
15//
16// * The procedure passed to _beginthread() must be __cdecl and has no return
17// code. The routine passed to _beginthreadex() must be __stdcall and must
18// return a return code, which will be used as the thread exit code.
19// Likewise, _endthread() takes no parameter and always returns a thread exit
20// code of 0 if the thread exits without error, whereas _endthreadex() takes
21// an exit code.
22//
23// * _endthread() calls CloseHandle() on the handle returned from CreateThread().
24// Note that this means that a caller should not use this handle, since it is
25// possible that the thread will have terminated and the handle will have been
26// closed by the time that _beginthread() returns.
27//
28// _endthreadex() does not call CloseHandle() to close the handle: the caller
29// of _beginthreadex() is required to close the handle.
30//
31// * _beginthread() returns -1 on failure. _beginthreadex() returns zero on
32// failure (just as CreateThread() does).
33//
34#include <corecrt_internal.h>
35#include <process.h>
36#include <roapi.h>
37
38// In some compilation models, the compiler is able to detect that the return
39// statement at the end of thread_start is unreachable. We cannot suppress the
40// warning locally because it is a backend warning.
41#pragma warning(disable: 4702) // unreachable code
42#pragma warning(disable: 4984) // 'if constexpr' is a C++17 language extension
43
44
45namespace
46{
47 struct thread_parameter_free_policy
48 {
49 void operator()(__acrt_thread_parameter* const parameter) throw()
50 {
51 if (!parameter)
52 {
53 return;
54 }
55
56 if (parameter->_thread_handle)
57 {
58 CloseHandle(parameter->_thread_handle);
59 }
60
61 if (parameter->_module_handle)
62 {
63 FreeLibrary(parameter->_module_handle);
64 }
65
66 _free_crt(parameter);
67 }
68 };
69
70 using unique_thread_parameter = __crt_unique_heap_ptr<
71 __acrt_thread_parameter,
72 thread_parameter_free_policy>;
73}
74
75template <typename ThreadProcedure, bool Ex>
76static unsigned long WINAPI thread_start(void* const parameter) throw()
77{
78 if (!parameter)
79 {
80 ExitThread(GetLastError());
81 }
82
83 __acrt_thread_parameter* const context = static_cast<__acrt_thread_parameter*>(parameter);
84
85 __acrt_getptd()->_beginthread_context = context;
86
87 if (__acrt_get_begin_thread_init_policy() == begin_thread_init_policy_ro_initialize)
88 {
89 context->_initialized_apartment = __acrt_RoInitialize(RO_INIT_MULTITHREADED) == S_OK;
90 }
91
92 __try
93 {
94 ThreadProcedure const procedure = reinterpret_cast<ThreadProcedure>(context->_procedure);
95 if constexpr (Ex)
96 {
97 _endthreadex(procedure(context->_context));
98 }
99 else
100 {
101 procedure(context->_context);
102 _endthreadex(0);
103 }
104 }
105 __except (_seh_filter_exe(GetExceptionCode(), GetExceptionInformation()))
106 {
107 // Execution should never reach here:
108 _exit(GetExceptionCode());
109 }
110 __endtry
111
112 // This return statement will never be reached. All execution paths result
113 // in the thread or process exiting.
114 return 0;
115}
116
117
118
119static __acrt_thread_parameter* __cdecl create_thread_parameter(
120 void* const procedure,
121 void* const context
122 ) throw()
123{
124 unique_thread_parameter parameter(_calloc_crt_t(__acrt_thread_parameter, 1).detach());
125 if (!parameter)
126 {
127 return nullptr;
128 }
129
130 parameter.get()->_procedure = reinterpret_cast<void*>(procedure);
131 parameter.get()->_context = context;
132
133 // Attempt to bump the reference count of the module in which the user's
134 // thread procedure is defined, to ensure that the module will stay loaded
135 // as long as the thread is executing. We will release this HMDOULE when
136 // the thread procedure returns or _endthreadex is called.
137 GetModuleHandleExW(
138 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
139 reinterpret_cast<LPCWSTR>(procedure),
140 ¶meter.get()->_module_handle);
141
142 return parameter.detach();
143}
144
145extern "C" uintptr_t __cdecl _beginthread(
146 _beginthread_proc_type const procedure,
147 unsigned int const stack_size,
148 void* const context
149 )
150{
151 _VALIDATE_RETURN(procedure != nullptr, EINVAL, reinterpret_cast<uintptr_t>(INVALID_HANDLE_VALUE));
152
153 unique_thread_parameter parameter(create_thread_parameter(reinterpret_cast<void*>(procedure), context));
154 if (!parameter)
155 {
156 return reinterpret_cast<uintptr_t>(INVALID_HANDLE_VALUE);
157 }
158
159 // We create the new thread in a suspended state so that we can update
160 // the parameter structure with the thread handle. The newly created
161 // thread is responsible for closing this handle.
162 DWORD thread_id{};
163 HANDLE const thread_handle = CreateThread(
164 nullptr,
165 stack_size,
166 thread_start<_beginthread_proc_type, false>,
167 parameter.get(),
168 CREATE_SUSPENDED,
169 &thread_id);
170
171 if (!thread_handle)
172 {
173 __acrt_errno_map_os_error(GetLastError());
174 return reinterpret_cast<uintptr_t>(INVALID_HANDLE_VALUE);
175 }
176
177 parameter.get()->_thread_handle = thread_handle;
178
179 // Now we can start the thread...
180 if (ResumeThread(thread_handle) == static_cast<DWORD>(-1))
181 {
182 __acrt_errno_map_os_error(GetLastError());
183 return reinterpret_cast<uintptr_t>(INVALID_HANDLE_VALUE);
184 }
185
186 // If we successfully created the thread, the thread now owns its parameter:
187 parameter.detach();
188
189 return reinterpret_cast<uintptr_t>(thread_handle);
190}
191
192extern "C" uintptr_t __cdecl _beginthreadex(
193 void* const security_descriptor,
194 unsigned int const stack_size,
195 _beginthreadex_proc_type const procedure,
196 void* const context,
197 unsigned int const creation_flags,
198 unsigned int* const thread_id_result
199 )
200{
201 _VALIDATE_RETURN(procedure != nullptr, EINVAL, 0);
202
203 unique_thread_parameter parameter(create_thread_parameter((void*)procedure, context));
204 if (!parameter)
205 {
206 return 0;
207 }
208
209 DWORD thread_id;
210 HANDLE const thread_handle = CreateThread(
211 reinterpret_cast<LPSECURITY_ATTRIBUTES>(security_descriptor),
212 stack_size,
213 thread_start<_beginthreadex_proc_type, true>,
214 parameter.get(),
215 creation_flags,
216 &thread_id);
217
218 if (!thread_handle)
219 {
220 __acrt_errno_map_os_error(GetLastError());
221 return 0;
222 }
223
224 if (thread_id_result)
225 {
226 *thread_id_result = thread_id;
227 }
228
229 // If we successfully created the thread, the thread now owns its parameter:
230 parameter.detach();
231
232 return reinterpret_cast<uintptr_t>(thread_handle);
233}
234
235
236
237static void __cdecl common_end_thread(unsigned int const return_code) throw()
238{
239 __acrt_ptd* const ptd = __acrt_getptd_noexit();
240 if (!ptd)
241 {
242 ExitThread(return_code);
243 }
244
245 __acrt_thread_parameter* const parameter = ptd->_beginthread_context;
246 if (!parameter)
247 {
248 ExitThread(return_code);
249 }
250
251 if (parameter->_initialized_apartment)
252 {
253 __acrt_RoUninitialize();
254 }
255
256 if (parameter->_thread_handle != INVALID_HANDLE_VALUE && parameter->_thread_handle != nullptr)
257 {
258 CloseHandle(parameter->_thread_handle);
259 }
260
261 if (parameter->_module_handle != INVALID_HANDLE_VALUE && parameter->_module_handle != nullptr)
262 {
263 FreeLibraryAndExitThread(parameter->_module_handle, return_code);
264 }
265 else
266 {
267 ExitThread(return_code);
268 }
269}
270
271extern "C" void __cdecl _endthread()
272{
273 return common_end_thread(0);
274}
275
276extern "C" void __cdecl _endthreadex(unsigned int const return_code)
277{
278 return common_end_thread(return_code);
279}