ReactOS 0.4.16-dev-852-gcfcc8d8
corecrt_internal_ptd_propagation.h
Go to the documentation of this file.
1//
2// corecrt_internal_ptd_propagation.h
3//
4// Copyright (c) Microsoft Corporation. All rights reserved.
5//
6// Consistently querying for per-thread data puts a significant overhead
7// on runtime of the UCRT. Instead of re-querying the PTD every time it is
8// needed, the goal going forward is to propagate the PTD, locale, global state index,
9// and errno information between function calls via an argument.
10// This header contains support for PTD propagation, disables common
11// macros that invoke errno, and provides internal-only functions that
12// propagate the per-thread data.
13
14#pragma once
15#include <corecrt_internal.h>
16#include <stdlib.h>
17
19
20// To grab the PTD, we also must query the global state index. Both of the FlsGetValue calls must be protected
21// from modifying the Win32 error state, so must be guarded by GetLastError()/SetLastError().
22// This function is provided so that if we already know the global state index and already
23// have a Win32 error guard object, we can avoid doing it again for getting the PTD.
24__acrt_ptd* __cdecl __acrt_getptd_noexit_explicit(__crt_scoped_get_last_error_reset const&, size_t global_state_index);
25
26#ifdef __cplusplus
27extern "C++"
28{
29 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30 //
31 // PTD Data Host
32 //
33 // Upon public function entry, one of these objects should be instantiated in order
34 // to host all the per-thread data for the rest of the function call. If any per-thread
35 // data is required, it will be requested once for the full runtime of the function.
36 // Additionally, changes to errno and doserrno will be recorded here instead, so that
37 // the actual errno value will only be updated once, and will never be queried.
38 //
39 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
40
41 class __crt_cached_ptd_host
42 { // The goal of this class is to minimize the number of calls to FlsGetValue()/SetLastError().
43
44 // Each call into the UCRT can use this class to lazily get and update:
45 // * The __acrt_ptd:
46 // * errno / doserrno
47 // * locale / multibyte info
48 // * The global state index
49 // while calling GetFlsValue the minimum number of times.
50 //
51 // -- PTD and Global State Index --
52 // Upon the first request of the PTD, both of these are updated at once. The global state index
53 // is required for getting the correct PTD, and both need to be guarded with
54 // a __crt_scoped_get_last_error_reset to prevent affecting the Win32 error.
55 // If the global state index is requested prior to the PTD, only the global state index is updated.
56
57 // -- Locale and Multibyte Info --
58 // If given a valid _locale_t during construction, it is used for the locale data.
59 // If the locale has not yet been set by a call to setlocale(), the initial locale
60 // data for the C locale is used instead.
61 // Otherwise, we wait until get_locale() is first called before querying the PTD
62 // and locking the locale from any other changes until the destructor is called
63 // (i.e. when the UCRT function call is completed).
64
65 // -- errno and _doserrno --
66 // This can be accessed directly via get_raw_ptd()->_terrno / _tdoserrno, but
67 // checking errno results between internal function calls
68 // (for example, printf can call wctomb and then check errno)
69 // is a significant source of overhead.
70 // Instead, both errno and doserrno changes are recorded locally in this class
71 // and the values in the PTD are only updated once this class is destroyed and the
72 // UCRT function call is complete.
73 // This means that if you must check errno after calling another function, if errno
74 // is not set then no PTD access was required. Using this instead of accessing errno
75 // directly removed all PTD access in many printf scenarios.
76
77 // Do not pass this directly, use __crt_cached_ptd_host&.
78 // When the PTD is queried by a child function, the parent will also not need to re-query.
79 public:
80 enum class locale_status : unsigned char
81 {
82 uninitialized,
83 updated_on_construction,
84 updated_via_ptd
85 };
86
87 explicit __crt_cached_ptd_host(_locale_t const locale = nullptr) throw()
88 : _ptd(nullptr), _current_global_state_index_valid(false), _locale_status(locale_status::uninitialized)
89 {
90 if (locale)
91 {
92 _locale_pointers = *locale;
93 _locale_status = locale_status::updated_on_construction;
94 }
95 else if (!__acrt_locale_changed())
96 {
97 _locale_pointers = __acrt_initial_locale_pointers;
98 _locale_status = locale_status::updated_on_construction;
99 }
100 }
101
102 ~__crt_cached_ptd_host() throw()
103 {
104 if (_locale_status == locale_status::updated_via_ptd)
105 {
106 // We only locked the PTD from locale propagation if we are using
107 // the locale data from the PTD.
108 __acrt_enable_global_locale_sync(_ptd); // The PTD must be valid if locale was updated via the PTD.
109 }
110
111 if (_current_errno.valid())
112 {
113 get_raw_ptd()->_terrno = _current_errno.unsafe_value();
114 }
115
116 if (_current_doserrno.valid())
117 {
118 get_raw_ptd()->_tdoserrno = _current_doserrno.unsafe_value();
119 }
120 }
121
122 __crt_cached_ptd_host(__crt_cached_ptd_host const&) = delete;
123 __crt_cached_ptd_host& operator=(__crt_cached_ptd_host const&) = delete;
124
126 {
127 update_locale();
128 return &_locale_pointers;
129 }
130
131 size_t get_current_global_state_index() throw()
132 {
133 return check_synchronize_global_state_index();
134 }
135
136 __acrt_ptd * get_raw_ptd() throw()
137 {
138 return check_synchronize_per_thread_data();
139 }
140
141 __acrt_ptd * get_raw_ptd_noexit() throw()
142 {
143 return try_synchronize_per_thread_data();
144 }
145
146 template <typename T>
147 struct cached
148 {
149 public:
150 cached() throw()
151 : _valid(false)
152 {}
153
154 bool valid() const throw()
155 {
156 return _valid;
157 }
158
159 T set(T new_value) throw()
160 {
161 _valid = true;
162 _value = new_value;
163 return new_value;
164 }
165
166 T value_or(T const alternative) const throw()
167 {
168 if (_valid)
169 {
170 return _value;
171 }
172 return alternative;
173 }
174
175 bool check(T const value) const throw()
176 {
177 return _valid && _value == value;
178 }
179
180 class guard
181 {
182 public:
183 explicit guard(cached& parent) throw()
184 : _parent(parent), _copy(parent), _enabled(true)
185 {
186 }
187
188 ~guard() throw()
189 {
190 if (_enabled)
191 {
192 _parent = _copy;
193 }
194 }
195
196 guard(guard const&) = delete;
197 guard& operator=(guard const&) = delete;
198
199 void disable() throw()
200 {
201 _enabled = false;
202 }
203
204 void enable() throw()
205 {
206 _enabled = true;
207 }
208
209 private:
210 cached& _parent;
211 cached _copy;
212 bool _enabled;
213 };
214
215 guard create_guard() throw()
216 {
217 return guard(*this);
218 }
219
220 T unsafe_value() throw()
221 {
222 // Must check status beforehand.
223 return _value;
224 }
225
226 private:
227 cached(cached const&) = default;
228 cached(cached&&) = default;
229
230 cached& operator=(cached const&) = default;
231 cached& operator=(cached&&) = default;
232
233 T _value;
234 bool _valid;
235 };
236
237 auto& get_errno() throw()
238 {
239 return _current_errno;
240 }
241
242 auto& get_doserrno() throw()
243 {
244 return _current_doserrno;
245 }
246
247 private:
248 __forceinline void update_locale() throw()
249 { // Avoid costs for function call if locale doesn't need to be updated.
250 if (_locale_status == locale_status::uninitialized)
251 {
252 update_locale_slow();
253 }
254 }
255
256 void update_locale_slow() throw()
257 {
258 __acrt_ptd * const ptd_ptr = get_raw_ptd();
259
260 _locale_pointers.locinfo = ptd_ptr->_locale_info;
261 _locale_pointers.mbcinfo = ptd_ptr->_multibyte_info;
262
263 // _get_raw_ptd() will update _current_global_state_index
265 ptd_ptr, &_locale_pointers.locinfo, _current_global_state_index
266 );
267
269 ptd_ptr, &_locale_pointers.mbcinfo, _current_global_state_index
270 );
271
272 if ((ptd_ptr->_own_locale & _PER_THREAD_LOCALE_BIT) == 0)
273 {
274 // Skip re-synchronization with the global locale to prevent the
275 // locale from changing half-way through the call.
276 __acrt_disable_global_locale_sync(ptd_ptr);
277 _locale_status = locale_status::updated_via_ptd;
278 }
279 }
280
281 __forceinline __acrt_ptd * check_synchronize_per_thread_data() throw()
282 {
283 if (_ptd == nullptr)
284 {
285 if (force_synchronize_per_thread_data() == nullptr)
286 {
287 abort();
288 }
289 }
290 return _ptd;
291 }
292
293 __forceinline __acrt_ptd * try_synchronize_per_thread_data() throw()
294 {
295 if (_ptd == nullptr)
296 {
297 return force_synchronize_per_thread_data();
298 }
299
300 return _ptd;
301 }
302
303 __acrt_ptd * force_synchronize_per_thread_data() throw()
304 { // This function should be called at most once per UCRT function call.
305 // Update all per-thread variables to minimize number of GetLastError() calls.
306 __crt_scoped_get_last_error_reset const last_error_reset;
307
308 return _ptd = __acrt_getptd_noexit_explicit(
309 last_error_reset,
310 check_synchronize_global_state_index(last_error_reset)
311 );
312 }
313
314 size_t check_synchronize_global_state_index() throw()
315 {
316 if (!_current_global_state_index_valid)
317 {
318 __crt_scoped_get_last_error_reset const last_error_reset;
319 return force_synchronize_global_state_index(last_error_reset);
320 }
321
322 return _current_global_state_index;
323 }
324
325 size_t check_synchronize_global_state_index(__crt_scoped_get_last_error_reset const& last_error_reset) throw()
326 {
327 if (!_current_global_state_index_valid)
328 {
329 return force_synchronize_global_state_index(last_error_reset);
330 }
331
332 return _current_global_state_index;
333 }
334
335 size_t force_synchronize_global_state_index(__crt_scoped_get_last_error_reset const& last_error_reset) throw()
336 {
337 _current_global_state_index = __crt_state_management::get_current_state_index(last_error_reset);
338 _current_global_state_index_valid = true;
339
340 return _current_global_state_index;
341 }
342
343 __acrt_ptd * _ptd;
344
345 size_t _current_global_state_index;
346 bool _current_global_state_index_valid;
347
348 __crt_locale_pointers _locale_pointers;
349 locale_status _locale_status;
350
351 cached<errno_t> _current_errno;
352 cached<unsigned long> _current_doserrno;
353 };
354
355
356 namespace __crt_state_management
357 {
358 // If we have already grabbed the PTD, then we also grabbed the current global state index
359 // and can use the global state index cached inside the __crt_cached_ptd_host.
360
361 template <typename T>
362 T& dual_state_global<T>::value(__crt_cached_ptd_host& ptd) throw()
363 {
364 return _value[ptd.get_current_global_state_index()];
365 }
366
367 template <typename T>
368 T const& dual_state_global<T>::value(__crt_cached_ptd_host& ptd) const throw()
369 {
370 return _value[ptd.get_current_global_state_index()];
371 }
372 }
373} // extern "C++"
374#endif // __cplusplus
375
376//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
377//
378// Validation Macros / Errno update functions
379//
380// All the UCRT _VALIDATE_* macros will set errno directly on error.
381// These new validation macros use __crt_cached_ptd_host& instead to prevent overhead.
382//
383// If this header is included, then the old validation/errno macros are #undef-ed to
384// to prevent accidental calling of errno.
385//
386// _ALLOW_OLD_VALIDATE_MACROS is provided as an escape hatch for files that need to include
387// this header, but still have code paths that require the old validate macros.
388//
389//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
390
391#ifndef _ALLOW_OLD_VALIDATE_MACROS
392 // Use this to allow the validate macros without PTD propagation in a source file.
393 #undef _INVALID_PARAMETER
394
395 #undef _VALIDATE_CLEAR_OSSERR_RETURN
396 #undef _VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE
397 #undef _VALIDATE_RETURN
398 #undef _VALIDATE_RETURN_ERRCODE
399 #undef _VALIDATE_RETURN_ERRCODE_NOEXC
400 #undef _VALIDATE_RETURN_NOERRNO
401 #undef _VALIDATE_RETURN_NOEXC
402 #undef _VALIDATE_RETURN_VOID
403
404 #undef _ERRCHECK_SPRINTF
405 #undef _VALIDATE_STREAM_ANSI_RETURN
406 #undef _CHECK_FH_RETURN
407 #undef _CHECK_FH_CLEAR_OSSERR_RETURN
408 #undef _CHECK_FH_CLEAR_OSSERR_RETURN_ERRCODE
409 #undef _VALIDATE_CLEAR_OSSERR_RETURN
410
411 #undef errno
412 #undef _doserrno
413
414#endif // _ALLOW_OLD_VALIDATE_MACROS
415
416// Validate macros whose counterparts are from internal_shared.h
417
418#ifdef _DEBUG
419 #define _UCRT_INVALID_PARAMETER(ptd, expr) _invalid_parameter_internal(expr, __FUNCTIONW__, __FILEW__, __LINE__, 0, ptd)
420#else // _DEBUG
421 #define _UCRT_INVALID_PARAMETER(ptd, expr) _invalid_parameter_internal(nullptr, nullptr, nullptr, 0, 0, ptd)
422#endif // _DEBUG
423
424#define _UCRT_VALIDATE_CLEAR_OSSERR_RETURN(ptd, expr, errorcode, retexpr) \
425 { \
426 int _Expr_val = !!(expr); \
427 _ASSERT_EXPR((_Expr_val), _CRT_WIDE(#expr)); \
428 if (!(_Expr_val)) \
429 { \
430 (ptd).get_doserrno().set(0L); \
431 (ptd).get_errno().set((errorcode)); \
432 _UCRT_INVALID_PARAMETER((ptd), _CRT_WIDE(#expr)); \
433 return (retexpr); \
434 } \
435 }
436
437#define _UCRT_VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE(ptd, expr, errorcode) \
438 { \
439 int _Expr_val = !!(expr); \
440 _ASSERT_EXPR((_Expr_val), _CRT_WIDE(#expr)); \
441 if (!(_Expr_val)) \
442 { \
443 (ptd).get_doserrno().set(0L); \
444 (ptd).get_errno().set((errorcode)); \
445 _UCRT_INVALID_PARAMETER((ptd), _CRT_WIDE(#expr)); \
446 return (errorcode); \
447 } \
448 }
449
450#define _UCRT_VALIDATE_RETURN(ptd, expr, errorcode, retexpr) \
451 { \
452 int _Expr_val = !!(expr); \
453 _ASSERT_EXPR((_Expr_val), _CRT_WIDE(#expr)); \
454 if (!(_Expr_val)) \
455 { \
456 (ptd).get_errno().set((errorcode)); \
457 _UCRT_INVALID_PARAMETER((ptd), _CRT_WIDE(#expr)); \
458 return (retexpr); \
459 } \
460 }
461
462#define _UCRT_VALIDATE_RETURN_ERRCODE(ptd, expr, errorcode) \
463 { \
464 int _Expr_val = !!(expr); \
465 _ASSERT_EXPR((_Expr_val), _CRT_WIDE(#expr)); \
466 if (!(_Expr_val)) \
467 { \
468 (ptd).get_errno().set((errorcode)); \
469 _UCRT_INVALID_PARAMETER((ptd), _CRT_WIDE(#expr)); \
470 return (errorcode); \
471 } \
472 }
473
474#define _UCRT_VALIDATE_RETURN_ERRCODE_NOEXC(ptd, expr, errorcode) \
475 { \
476 if (!(expr)) \
477 { \
478 return (ptd).get_errno().set((errorcode)); \
479 } \
480 }
481
482#define _UCRT_VALIDATE_RETURN_NOERRNO(ptd, expr, retexpr) \
483 { \
484 int _Expr_val = !!(expr); \
485 _ASSERT_EXPR((_Expr_val), _CRT_WIDE(#expr)); \
486 if (!(_Expr_val)) \
487 { \
488 _UCRT_INVALID_PARAMETER((ptd), _CRT_WIDE(#expr)); \
489 return (retexpr); \
490 } \
491 }
492
493#define _UCRT_VALIDATE_RETURN_NOEXC(ptd, expr, errorcode, retexpr) \
494 { \
495 if (!(expr)) \
496 { \
497 (ptd).get_errno().set((errorcode)); \
498 return (retexpr); \
499 } \
500 }
501
502#define _UCRT_VALIDATE_RETURN_VOID(ptd, expr, errorcode) \
503 { \
504 int _Expr_val = !!(expr); \
505 _ASSERT_EXPR((_Expr_val), _CRT_WIDE(#expr)); \
506 if (!(_Expr_val)) \
507 { \
508 (ptd).get_errno().set((errorcode)); \
509 _UCRT_INVALID_PARAMETER((ptd), _CRT_WIDE(#expr)); \
510 return; \
511 } \
512 }
513
514// Validate macros whose counterparts are from corecrt_internal.h
515
516#define _UCRT_VALIDATE_STREAM_ANSI_RETURN(ptd, stream, errorcode, retexpr) \
517 { \
518 __crt_stdio_stream const _Stream((stream)); \
519 int fn; \
520 _UCRT_VALIDATE_RETURN((ptd), ( \
521 (_Stream.is_string_backed()) || \
522 (fn = _fileno(_Stream.public_stream()), \
523 ((_textmode_safe(fn) == __crt_lowio_text_mode::ansi) && \
524 !_tm_unicode_safe(fn)))), \
525 (errorcode), (retexpr)) \
526 }
527
528#define _UCRT_CHECK_FH_RETURN(ptd, handle, errorcode, retexpr) \
529 { \
530 if ((handle) == _NO_CONSOLE_FILENO) \
531 { \
532 (ptd).get_errno().set((errorcode)); \
533 return (retexpr); \
534 } \
535 }
536
537#define _UCRT_CHECK_FH_CLEAR_OSSERR_RETURN(ptd, handle, errorcode, retexpr) \
538 { \
539 if ((handle) == _NO_CONSOLE_FILENO) \
540 { \
541 (ptd).get_doserrno().set(0L); \
542 (ptd).get_errno().set((errorcode)); \
543 return (retexpr); \
544 } \
545 }
546
547#define _UCRT_CHECK_FH_CLEAR_OSSERR_RETURN_ERRCODE(ptd, handle, retexpr) \
548 { \
549 if ((handle) == _NO_CONSOLE_FILENO) \
550 { \
551 (ptd).get_doserrno().set(0L); \
552 return (retexpr); \
553 } \
554 }
555
556#define _UCRT_VALIDATE_CLEAR_OSSERR_RETURN(ptd, expr, errorcode, retexpr) \
557 { \
558 int _Expr_val = !!(expr); \
559 _ASSERT_EXPR((_Expr_val), _CRT_WIDE(#expr)); \
560 if (!(_Expr_val)) \
561 { \
562 (ptd).get_doserrno().set(0L); \
563 (ptd).get_errno().set((errorcode)); \
564 _UCRT_INVALID_PARAMETER((ptd), _CRT_WIDE(#expr)); \
565 return (retexpr); \
566 } \
567 }
568
#define __cdecl
Definition: accygwin.h:79
static WCHAR * get_locale(void)
Definition: builtin.c:3937
Definition: _locale.h:75
Definition: _set.h:50
__crt_locale_pointers __acrt_initial_locale_pointers
Definition: nlsdata.cpp:137
#define _PER_THREAD_LOCALE_BIT
_CRT_BEGIN_C_HEADER __acrt_ptd *__cdecl __acrt_getptd_noexit_explicit(__crt_scoped_get_last_error_reset const &, size_t global_state_index)
result_buffer_count char *const _In_ int const _In_ bool const _In_ unsigned const _In_ STRFLT const _In_ bool const _Inout_ __crt_cached_ptd_host &ptd throw()
Definition: cvt.cpp:119
_In_ size_t const _In_ int _In_ bool const _In_ unsigned const _In_ __acrt_rounding_mode const _Inout_ __crt_cached_ptd_host & ptd
Definition: cvt.cpp:355
#define check(expected, result)
Definition: dplayx.c:32
r parent
Definition: btrfs.c:3010
BOOLEAN valid
GLboolean enable
Definition: glext.h:11120
#define abort()
Definition: i386-dis.c:34
#define T
Definition: mbstring.h:31
void __acrt_update_multibyte_info_explicit(__acrt_ptd *const ptd, __crt_multibyte_data **const multibyte_info, size_t const current_global_state_index)
void __acrt_update_locale_info_explicit(__acrt_ptd *const ptd, __crt_locale_data **const locale_info, size_t const current_global_state_index)
int disable
Definition: msacm.c:1365
#define false
Definition: stdbool.h:37
__crt_multibyte_data * _multibyte_info
__crt_locale_data * _locale_info
Definition: pdh_main.c:96
#define _CRT_END_C_HEADER
Definition: vcruntime.h:42
#define _CRT_BEGIN_C_HEADER
Definition: vcruntime.h:40