Reactos
at master 345 lines 9.2 kB view raw
1// 2// per_thread_data.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// Per-Thread Data (PTD) used by the AppCRT. 7// 8#include <corecrt_internal.h> 9#include <corecrt_internal_ptd_propagation.h> 10#include <stddef.h> 11 12 13 14#ifdef _CRT_GLOBAL_STATE_ISOLATION 15 extern "C" DWORD __crt_global_state_mode_flsindex = FLS_OUT_OF_INDEXES; 16#endif 17 18 19 20static void WINAPI destroy_fls(void*) throw(); 21 22 23 24static unsigned long __acrt_flsindex = FLS_OUT_OF_INDEXES; 25 26 27 28extern "C" bool __cdecl __acrt_initialize_ptd() 29{ 30 __acrt_flsindex = __acrt_FlsAlloc(destroy_fls); 31 if (__acrt_flsindex == FLS_OUT_OF_INDEXES) 32 { 33 return false; 34 } 35 36 if (__acrt_getptd_noexit() == nullptr) 37 { 38 __acrt_uninitialize_ptd(false); 39 return false; 40 } 41 42 return true; 43} 44 45extern "C" bool __cdecl __acrt_uninitialize_ptd(bool) 46{ 47 if (__acrt_flsindex != FLS_OUT_OF_INDEXES) 48 { 49 __acrt_FlsFree(__acrt_flsindex); 50 __acrt_flsindex = FLS_OUT_OF_INDEXES; 51 } 52 53 return true; 54} 55 56 57 58static void __cdecl replace_current_thread_locale_nolock( 59 __acrt_ptd* const ptd, 60 __crt_locale_data* const new_locale_info 61 ) throw() 62{ 63 if (ptd->_locale_info) 64 { 65 __acrt_release_locale_ref(ptd->_locale_info); 66 if (ptd->_locale_info != __acrt_current_locale_data.value() && 67 ptd->_locale_info != &__acrt_initial_locale_data && 68 ptd->_locale_info->refcount == 0) 69 { 70 __acrt_free_locale(ptd->_locale_info); 71 } 72 } 73 74 ptd->_locale_info = new_locale_info; 75 if (ptd->_locale_info) 76 { 77 __acrt_add_locale_ref(ptd->_locale_info); 78 } 79} 80 81 82 83// Constructs a single PTD object, copying the given 'locale_data' if provided. 84static void __cdecl construct_ptd( 85 __acrt_ptd* const ptd, 86 __crt_locale_data** const locale_data 87 ) throw() 88{ 89 ptd->_rand_state = 1; 90 ptd->_pxcptacttab = const_cast<__crt_signal_action_t*>(__acrt_exception_action_table); 91 92 // It is necessary to always have GLOBAL_LOCALE_BIT set in perthread data 93 // because when doing bitwise or, we won't get __UPDATE_LOCALE to work when 94 // global per thread locale is set. 95 // See _configthreadlocale() and __acrt_should_sync_with_global_locale(). 96 ptd->_own_locale = _GLOBAL_LOCALE_BIT; 97 98 ptd->_multibyte_info = &__acrt_initial_multibyte_data; 99 100 // Initialize _setloc_data. These are the only valuse that need to be 101 // initialized. 102 ptd->_setloc_data._cachein[0] = L'C'; 103 ptd->_setloc_data._cacheout[0] = L'C'; 104 105 // Downlevel data is not initially used 106 ptd->_setloc_downlevel_data = nullptr; 107 108 __acrt_lock_and_call(__acrt_multibyte_cp_lock, [&] 109 { 110 _InterlockedIncrement(&ptd->_multibyte_info->refcount); 111 }); 112 113 // We need to make sure that ptd->ptlocinfo in never nullptr, this saves us 114 // perf counts when UPDATING locale. 115 __acrt_lock_and_call(__acrt_locale_lock, [&] 116 { 117 replace_current_thread_locale_nolock(ptd, *locale_data); 118 }); 119} 120 121// Constructs each of the 'state_index_count' PTD objects in the array of PTD 122// objects pointed to by 'ptd'. 123static void __cdecl construct_ptd_array(__acrt_ptd* const ptd) throw() 124{ 125 for (size_t i = 0; i != __crt_state_management::state_index_count; ++i) 126 { 127 construct_ptd(&ptd[i], &__acrt_current_locale_data.dangerous_get_state_array()[i]); 128 } 129} 130 131// Cleans up all resources used by a single PTD; does not free the PTD structure 132// itself. 133static void __cdecl destroy_ptd(__acrt_ptd* const ptd) throw() 134{ 135 if (ptd->_pxcptacttab != __acrt_exception_action_table) 136 { 137 _free_crt(ptd->_pxcptacttab); 138 } 139 140 _free_crt(ptd->_cvtbuf); 141 _free_crt(ptd->_asctime_buffer); 142 _free_crt(ptd->_wasctime_buffer); 143 _free_crt(ptd->_gmtime_buffer); 144 _free_crt(ptd->_tmpnam_narrow_buffer); 145 _free_crt(ptd->_tmpnam_wide_buffer); 146 _free_crt(ptd->_strerror_buffer); 147 _free_crt(ptd->_wcserror_buffer); 148 _free_crt(ptd->_beginthread_context); 149 150 __acrt_lock_and_call(__acrt_multibyte_cp_lock, [&] 151 { 152 __crt_multibyte_data* const multibyte_data = ptd->_multibyte_info; 153 if (!multibyte_data) 154 { 155 return; 156 } 157 158 if (_InterlockedDecrement(&multibyte_data->refcount) != 0) 159 { 160 return; 161 } 162 163 if (multibyte_data == &__acrt_initial_multibyte_data) 164 { 165 return; 166 } 167 168 _free_crt(multibyte_data); 169 }); 170 171 __acrt_lock_and_call(__acrt_locale_lock, [&] 172 { 173 replace_current_thread_locale_nolock(ptd, nullptr); 174 }); 175} 176 177// Destroys each of the 'state_index_count' PTD objects in the array of PTD 178// objects pointed to by 'ptd'. 179static void __cdecl destroy_ptd_array(__acrt_ptd* const ptd) throw() 180{ 181 for (size_t i = 0; i != __crt_state_management::state_index_count; ++i) 182 { 183 destroy_ptd(&ptd[i]); 184 } 185} 186 187// This function is called by the operating system when a thread is being 188// destroyed, to allow us the opportunity to clean up. 189static void WINAPI destroy_fls(void* const pfd) throw() 190{ 191 if (!pfd) 192 { 193 return; 194 } 195 196 destroy_ptd_array(static_cast<__acrt_ptd*>(pfd)); 197 _free_crt(pfd); 198} 199 200static __forceinline __acrt_ptd* try_get_ptd_head() throw() 201{ 202 // If we haven't allocated per-thread data for this module, return failure: 203 if (__acrt_flsindex == FLS_OUT_OF_INDEXES) 204 { 205 return nullptr; 206 } 207 208 __acrt_ptd* const ptd_head = static_cast<__acrt_ptd*>(__acrt_FlsGetValue(__acrt_flsindex)); 209 if (!ptd_head) 210 { 211 return nullptr; 212 } 213 214 return ptd_head; 215} 216 217_Success_(return != nullptr) 218static __forceinline __acrt_ptd* internal_get_ptd_head() throw() 219{ 220 // We use the CRT heap to allocate the PTD. If the CRT heap fails to 221 // allocate the requested memory, it will attempt to set errno to ENOMEM, 222 // which will in turn attempt to acquire the PTD, resulting in infinite 223 // recursion that causes a stack overflow. 224 // 225 // We set the PTD to this sentinel value for the duration of the allocation 226 // in order to detect this case. 227 static void* const reentrancy_sentinel = reinterpret_cast<void*>(SIZE_MAX); 228 229 __acrt_ptd* const existing_ptd_head = try_get_ptd_head(); 230 if (existing_ptd_head == reentrancy_sentinel) 231 { 232 return nullptr; 233 } 234 else if (existing_ptd_head != nullptr) 235 { 236 return existing_ptd_head; 237 } 238 239 if (!__acrt_FlsSetValue(__acrt_flsindex, reentrancy_sentinel)) 240 { 241 return nullptr; 242 } 243 244 __crt_unique_heap_ptr<__acrt_ptd> new_ptd_head(_calloc_crt_t(__acrt_ptd, __crt_state_management::state_index_count)); 245 if (!new_ptd_head) 246 { 247 __acrt_FlsSetValue(__acrt_flsindex, nullptr); 248 return nullptr; 249 } 250 251 if (!__acrt_FlsSetValue(__acrt_flsindex, new_ptd_head.get())) 252 { 253 __acrt_FlsSetValue(__acrt_flsindex, nullptr); 254 return nullptr; 255 } 256 257 construct_ptd_array(new_ptd_head.get()); 258 return new_ptd_head.detach(); 259} 260 261// This functionality has been split out of __acrt_getptd_noexit so that we can 262// force it to be inlined into both __acrt_getptd_noexit and __acrt_getptd. These 263// functions are performance critical and this change has substantially improved 264// __acrt_getptd performance. 265static __forceinline __acrt_ptd* __cdecl internal_getptd_noexit( 266 __crt_scoped_get_last_error_reset const& last_error_reset, 267 size_t const global_state_index 268 ) throw() 269{ 270 UNREFERENCED_PARAMETER(last_error_reset); 271 __acrt_ptd* const ptd_head = internal_get_ptd_head(); 272 if (!ptd_head) 273 { 274 return nullptr; 275 } 276 277 return ptd_head + global_state_index; 278} 279 280static __forceinline __acrt_ptd* __cdecl internal_getptd_noexit() throw() 281{ 282 __crt_scoped_get_last_error_reset const last_error_reset; 283 return internal_getptd_noexit(last_error_reset, __crt_state_management::get_current_state_index(last_error_reset)); 284} 285 286__acrt_ptd* __cdecl __acrt_getptd_noexit_explicit(__crt_scoped_get_last_error_reset const& last_error_reset, size_t const global_state_index) 287{ // An extra function to grab the PTD while a GetLastError() reset guard is already in place 288 // and the global state index is already known. 289 290 return internal_getptd_noexit(last_error_reset, global_state_index); 291} 292 293extern "C" __acrt_ptd* __cdecl __acrt_getptd_noexit() 294{ 295 return internal_getptd_noexit(); 296} 297 298extern "C" __acrt_ptd* __cdecl __acrt_getptd() 299{ 300 __acrt_ptd* const ptd = internal_getptd_noexit(); 301 if (!ptd) 302 { 303 abort(); 304 } 305 306 return ptd; 307} 308 309extern "C" __acrt_ptd* __cdecl __acrt_getptd_head() 310{ 311 __acrt_ptd* const ptd_head = internal_get_ptd_head(); 312 if (!ptd_head) 313 { 314 abort(); 315 } 316 317 return ptd_head; 318} 319 320 321 322extern "C" void __cdecl __acrt_freeptd() 323{ 324 __acrt_ptd* const ptd_head = try_get_ptd_head(); 325 if (!ptd_head) 326 { 327 return; 328 } 329 330 __acrt_FlsSetValue(__acrt_flsindex, nullptr); 331 destroy_fls(ptd_head); 332} 333 334 335 336// These functions are simply wrappers around the Windows API functions. 337extern "C" unsigned long __cdecl __threadid() 338{ 339 return GetCurrentThreadId(); 340} 341 342extern "C" uintptr_t __cdecl __threadhandle() 343{ 344 return reinterpret_cast<uintptr_t>(GetCurrentThread()); 345}