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 { 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 75 template <typename ThreadProcedure, bool Ex> 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 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 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 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 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 271 extern "C" void __cdecl _endthread() 272 { 273 return common_end_thread(0); 274 } 275 276 extern "C" void __cdecl _endthreadex(unsigned int const return_code) 277 { 278 return common_end_thread(return_code); 279 } 280