Reactos
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}