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