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
45 namespace
46 {
47 struct thread_parameter_free_policy
48 {
operator ()__anon16db2dce0111::thread_parameter_free_policy49 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
75 template <typename ThreadProcedure, bool Ex>
thread_start(void * const parameter)76 static 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
create_thread_parameter(void * const procedure,void * const context)119 static __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
_beginthread(_beginthread_proc_type const procedure,unsigned int const stack_size,void * const context)145 extern "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
_beginthreadex(void * const security_descriptor,unsigned int const stack_size,_beginthreadex_proc_type const procedure,void * const context,unsigned int const creation_flags,unsigned int * const thread_id_result)192 extern "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
common_end_thread(unsigned int const return_code)237 static 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
_endthread()271 extern "C" void __cdecl _endthread()
272 {
273 return common_end_thread(0);
274 }
275
_endthreadex(unsigned int const return_code)276 extern "C" void __cdecl _endthreadex(unsigned int const return_code)
277 {
278 return common_end_thread(return_code);
279 }
280