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 
20 static void WINAPI destroy_fls(void*) throw();
21 
22 
23 
24 static unsigned long __acrt_flsindex = FLS_OUT_OF_INDEXES;
25 
26 
27 
__acrt_initialize_ptd()28 extern "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 
__acrt_uninitialize_ptd(bool)45 extern "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 
replace_current_thread_locale_nolock(__acrt_ptd * const ptd,__crt_locale_data * const new_locale_info)58 static 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.
construct_ptd(__acrt_ptd * const ptd,__crt_locale_data ** const locale_data)84 static 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'.
construct_ptd_array(__acrt_ptd * const ptd)123 static 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.
destroy_ptd(__acrt_ptd * const ptd)133 static 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'.
destroy_ptd_array(__acrt_ptd * const ptd)179 static 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.
destroy_fls(void * const pfd)189 static 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 
try_get_ptd_head()200 static __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)
internal_get_ptd_head()218 static __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.
internal_getptd_noexit(__crt_scoped_get_last_error_reset const & last_error_reset,size_t const global_state_index)265 static __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 
internal_getptd_noexit()280 static __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 
__acrt_getptd_noexit_explicit(__crt_scoped_get_last_error_reset const & last_error_reset,size_t const global_state_index)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 
__acrt_getptd_noexit()293 extern "C" __acrt_ptd* __cdecl __acrt_getptd_noexit()
294 {
295     return internal_getptd_noexit();
296 }
297 
__acrt_getptd()298 extern "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 
__acrt_getptd_head()309 extern "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 
__acrt_freeptd()322 extern "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.
__threadid()337 extern "C" unsigned long __cdecl __threadid()
338 {
339     return GetCurrentThreadId();
340 }
341 
__threadhandle()342 extern "C" uintptr_t __cdecl __threadhandle()
343 {
344     return reinterpret_cast<uintptr_t>(GetCurrentThread());
345 }
346