xref: /reactos/sdk/lib/ucrt/startup/thread.cpp (revision e98e9000)
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         &parameter.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