-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Improve call_once performance by using InitOnceBeginInitialize / InitOnceComplete #688
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
53b197d
c9c8292
8daed26
1d21bef
c029e70
85b2ba7
eb9cb4c
735b5cd
739cf2c
344027d
709ab31
63cb2e5
8e67703
b7629f8
3af40b3
b28b93f
21a99f3
96f9c29
b856878
84876de
4f501d6
0e37c3c
6f6b244
7e5dd62
aab20bd
c646d9c
1011283
0cefdea
0ac2631
09492d9
9b35e10
4834dbf
f708bb4
da1ab54
c11d81a
0f792a0
e655060
3b504fd
c1da0c8
bbe2f67
69869ad
ce14a58
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| :: Copyright (c) Microsoft Corporation. | ||
| :: SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| :: | ||
| :: Generates aliases for Windows API functions called by headers. | ||
| :: TRANSITION, VSO-1116868 "'aliasobj.exe' should be available with Visual Studio" | ||
|
|
||
| cd %~dp0 | ||
|
|
||
| rmdir /s /q i386 | ||
| rmdir /s /q amd64 | ||
| rmdir /s /q arm | ||
| rmdir /s /q arm64 | ||
| rmdir /s /q chpe | ||
|
|
||
| mkdir i386 | ||
| mkdir amd64 | ||
| mkdir arm | ||
| mkdir arm64 | ||
| mkdir chpe | ||
|
|
||
| :: __std_init_once_begin_initialize | ||
| ..\..\..\..\..\tools\x86\aliasobj.exe ^ | ||
| __imp____std_init_once_begin_initialize@16 ^ | ||
| __imp__InitOnceBeginInitialize@16 ^ | ||
| i386\std_init_once_begin_initialize.obj | ||
| ..\..\..\..\..\tools\amd64\aliasobj.exe ^ | ||
| __imp___std_init_once_begin_initialize ^ | ||
| __imp_InitOnceBeginInitialize ^ | ||
| amd64\std_init_once_begin_initialize.obj | ||
| ..\..\..\..\..\tools\x86\aliasobj.exe ^ | ||
| __imp___std_init_once_begin_initialize ^ | ||
| __imp_InitOnceBeginInitialize ^ | ||
| arm\std_init_once_begin_initialize.obj | ||
| copy amd64\std_init_once_begin_initialize.obj arm64\std_init_once_begin_initialize.obj | ||
| ..\..\..\..\..\tools\x86\aliasobj.exe ^ | ||
| __imp_#__std_init_once_begin_initialize@16 ^ | ||
| __imp_#InitOnceBeginInitialize@16 ^ | ||
| chpe\std_init_once_begin_initialize.obj | ||
|
|
||
| :: __std_init_once_complete | ||
| ..\..\..\..\..\tools\x86\aliasobj.exe ^ | ||
| __imp____std_init_once_complete@12 ^ | ||
| __imp__InitOnceComplete@12 ^ | ||
| i386\std_init_once_complete.obj | ||
| ..\..\..\..\..\tools\amd64\aliasobj.exe ^ | ||
| __imp___std_init_once_complete ^ | ||
| __imp_InitOnceComplete ^ | ||
| amd64\std_init_once_complete.obj | ||
| ..\..\..\..\..\tools\x86\aliasobj.exe ^ | ||
| __imp___std_init_once_complete ^ | ||
| __imp_InitOnceComplete ^ | ||
| arm\std_init_once_complete.obj | ||
| copy amd64\std_init_once_complete.obj arm64\std_init_once_complete.obj | ||
| ..\..\..\..\..\tools\x86\aliasobj.exe ^ | ||
| __imp_#__std_init_once_complete@12 ^ | ||
| __imp_#InitOnceComplete@12 ^ | ||
| chpe\std_init_once_complete.obj |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,9 +14,9 @@ | |
| #endif // _M_CEE_PURE | ||
|
|
||
| #include <chrono> | ||
| #include <stdlib.h> | ||
| #include <system_error> | ||
| #include <thread> | ||
| #include <tuple> | ||
| #include <utility> | ||
| #include <xcall_once.h> | ||
|
|
||
|
|
@@ -508,61 +508,72 @@ public: | |
| }; | ||
| #endif // _HAS_CXX17 | ||
|
|
||
| // FUNCTION TEMPLATE _Invoke_stored_explicit | ||
| template <class... _Types, size_t... _Indices> | ||
| auto _Invoke_stored_explicit(tuple<_Types...>&& _Tuple, index_sequence<_Indices...>) -> decltype( | ||
| _STD invoke(_STD get<_Indices>(_STD move(_Tuple))...)) { // invoke() a tuple with explicit parameter ordering | ||
| return _STD invoke(_STD get<_Indices>(_STD move(_Tuple))...); | ||
| } | ||
|
|
||
| // FUNCTION TEMPLATE _Invoke_stored | ||
| template <class... _Types> | ||
| auto _Invoke_stored(tuple<_Types...>&& _Tuple) | ||
| -> decltype(_Invoke_stored_explicit(_STD move(_Tuple), index_sequence_for<_Types...>{})) { // invoke() a tuple | ||
| return _Invoke_stored_explicit(_STD move(_Tuple), index_sequence_for<_Types...>{}); | ||
| } | ||
|
|
||
| // FUNCTION TEMPLATE call_once | ||
| [[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _XGetLastError(); | ||
|
|
||
| template <class _Tuple, class _Seq, size_t _Idx> | ||
| int __stdcall _Callback_once(void*, void* _Pv, void**) { // adapt call_once() to callback API | ||
| _Tuple* _Ptup = static_cast<_Tuple*>(_Pv); | ||
|
|
||
| _TRY_BEGIN | ||
| // Note explicit _Seq{} selects every element from *_Ptup except the last, | ||
| // which contains call_once's exception_ptr. | ||
| _Invoke_stored_explicit(_STD move(*_Ptup), _Seq{}); | ||
| _CATCH_ALL | ||
| auto& _Ref = _STD get<_Idx>(*_Ptup); | ||
| _Ref = _STD current_exception(); | ||
| return 0; | ||
| _CATCH_END | ||
| #ifdef _M_CEE | ||
| #define _WINDOWS_API __stdcall | ||
| #define _RENAME_WINDOWS_API(_Api) _Api##_clr | ||
| #else // ^^^ _M_CEE // !_M_CEE vvv | ||
| #define _WINDOWS_API __declspec(dllimport) __stdcall | ||
StephanTLavavej marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| #define _RENAME_WINDOWS_API(_Api) _Api | ||
| #endif // _M_CEE | ||
|
|
||
| return 1; | ||
| } | ||
| // WINBASEAPI | ||
| // BOOL | ||
| // WINAPI | ||
| // InitOnceBeginInitialize( | ||
| // _Inout_ LPINIT_ONCE lpInitOnce, | ||
| // _In_ DWORD dwFlags, | ||
| // _Out_ PBOOL fPending, | ||
| // _Outptr_opt_result_maybenull_ LPVOID* lpContext | ||
| // ); | ||
| extern "C" _NODISCARD int _WINDOWS_API _RENAME_WINDOWS_API(__std_init_once_begin_initialize)( | ||
| void** _LpInitOnce, unsigned long _DwFlags, int* _FPending, void** _LpContext) noexcept; | ||
|
|
||
| // WINBASEAPI | ||
AlexGuteniev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // BOOL | ||
| // WINAPI | ||
| // InitOnceComplete( | ||
| // _Inout_ LPINIT_ONCE lpInitOnce, | ||
| // _In_ DWORD dwFlags, | ||
| // _In_opt_ LPVOID lpContext | ||
| // ); | ||
| extern "C" _NODISCARD int _WINDOWS_API _RENAME_WINDOWS_API(__std_init_once_complete)( | ||
| void** _LpInitOnce, unsigned long _DwFlags, void* _LpContext) noexcept; | ||
|
|
||
| // #define RTL_RUN_ONCE_INIT_FAILED 0x00000004UL | ||
AlexGuteniev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // #define INIT_ONCE_INIT_FAILED RTL_RUN_ONCE_INIT_FAILED | ||
| _INLINE_VAR constexpr unsigned long _Init_once_init_failed = 0x4UL; | ||
|
|
||
| struct _Init_once_completer { | ||
AlexGuteniev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| once_flag& _Once; | ||
| unsigned long _DwFlags; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why no
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's Billy's code but I am afraid of mixing default and value initialization since #661
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because I wanted this thing to be an aggregate because optimizers like aggregates.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We discovered with |
||
| ~_Init_once_completer() { | ||
| if (_RENAME_WINDOWS_API(__std_init_once_complete)(&_Once._Opaque, _DwFlags, nullptr) == 0) { | ||
| _CSTD abort(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| template <class _Fn, class... _Args> | ||
| void(call_once)(once_flag& _Flag, _Fn&& _Fx, _Args&&... _Ax) { // call _Fx(_Ax...) once | ||
| using _Tuple = tuple<_Fn&&, _Args&&..., exception_ptr&>; | ||
| using _Seq = make_index_sequence<1 + sizeof...(_Args)>; | ||
|
|
||
| exception_ptr _Exc; | ||
| _Tuple _Tup(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)..., _Exc); | ||
|
|
||
| _Execute_once_fp_t _Fp = &_Callback_once<_Tuple, _Seq, 1 + sizeof...(_Args)>; | ||
|
|
||
| if (_Execute_once(_Flag, _Fp, _STD addressof(_Tup)) != 0) { | ||
| return; | ||
| void(call_once)(once_flag& _Once, _Fn&& _Fx, _Args&&... _Ax) noexcept( | ||
| noexcept(_STD invoke(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...))) /* strengthened */ { | ||
| // call _Fx(_Ax...) once | ||
| // parentheses against common "#define call_once(flag,func) pthread_once(flag,func)" | ||
| int _Pending; | ||
| if (_RENAME_WINDOWS_API(__std_init_once_begin_initialize)(&_Once._Opaque, 0, &_Pending, nullptr) == 0) { | ||
| _CSTD abort(); | ||
| } | ||
|
|
||
| if (_Exc) { | ||
| _STD rethrow_exception(_Exc); | ||
| if (_Pending != 0) { | ||
| _Init_once_completer _Op{_Once, _Init_once_init_failed}; | ||
| _STD invoke(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...); | ||
| _Op._DwFlags = 0; | ||
| } | ||
|
|
||
| _XGetLastError(); | ||
| } | ||
|
|
||
| #undef _WINDOWS_API | ||
| #undef _RENAME_WINDOWS_API | ||
|
|
||
| // condition_variable, timed_mutex, and recursive_timed_mutex are not supported under /clr | ||
| #ifndef _M_CEE | ||
| enum class cv_status { // names for wait returns | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| #include <yvals_core.h> | ||
|
|
||
| #include <synchapi.h> | ||
|
|
||
| // This must be as small as possible, because its contents are | ||
| // injected into the msvcprt.lib and msvcprtd.lib import libraries. | ||
| // Do not include or define anything else here. | ||
| // In particular, basic_string must not be included here. | ||
|
|
||
| // Provides forwarders for InitOnceBeginInitialize and InitOnceComplete for | ||
| // environments that can't use aliasobj, like /clr. | ||
|
|
||
| _EXTERN_C | ||
|
|
||
| int __stdcall __std_init_once_begin_initialize_clr( | ||
| void** _LpInitOnce, unsigned long _DwFlags, int* _FPending, void** _LpContext) noexcept { | ||
| return InitOnceBeginInitialize(reinterpret_cast<LPINIT_ONCE>(_LpInitOnce), _DwFlags, _FPending, _LpContext); | ||
| } | ||
|
|
||
| int __stdcall __std_init_once_complete_clr(void** _LpInitOnce, unsigned long _DwFlags, void* _LpContext) noexcept { | ||
| return InitOnceComplete(reinterpret_cast<LPINIT_ONCE>(_LpInitOnce), _DwFlags, _LpContext); | ||
| } | ||
|
|
||
| _END_EXTERN_C |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cbezault Why two object libraries instead of one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No reason. We could make them a single library and give it a nice logical name.