Skip to content
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

Temporarily restore support for Win7 / Server 2008 R2 #4857

Merged
merged 6 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion stl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ function(add_stl_dlls D_SUFFIX REL_OR_DBG)
generate_satellite_def("atomic_wait" "${D_SUFFIX}")

add_library(msvcp${D_SUFFIX}_atomic_wait SHARED "${CMAKE_BINARY_DIR}/msvcp_atomic_wait${D_SUFFIX}.def")
target_link_libraries(msvcp${D_SUFFIX}_atomic_wait PRIVATE msvcp${D_SUFFIX}_atomic_wait_objects msvcp${D_SUFFIX}_satellite_objects msvcp${D_SUFFIX}_implib_objects "msvcp${D_SUFFIX}" "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.lib" "advapi32.lib" "synchronization.lib")
target_link_libraries(msvcp${D_SUFFIX}_atomic_wait PRIVATE msvcp${D_SUFFIX}_atomic_wait_objects msvcp${D_SUFFIX}_satellite_objects msvcp${D_SUFFIX}_implib_objects "msvcp${D_SUFFIX}" "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.lib" "advapi32.lib")
set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES ARCHIVE_OUTPUT_NAME "msvcp140_atomic_wait${D_SUFFIX}${VCLIBS_SUFFIX}")
set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES OUTPUT_NAME "msvcp140${D_SUFFIX}_atomic_wait${VCLIBS_SUFFIX}")
Expand Down
16 changes: 15 additions & 1 deletion stl/inc/xatomic_wait.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,23 @@ _STL_DISABLE_CLANG_WARNINGS
extern "C" {
inline constexpr unsigned long __std_atomic_wait_no_timeout = 0xFFFF'FFFF; // Pass as partial timeout

enum class __std_atomic_api_level : unsigned long {
__not_set,
__detecting,
__has_srwlock,
__has_wait_on_address,
};

// This function allows testing the atomic wait support while always using the APIs for a platform with fewer
// capabilities; it attempts to lock the APIs used to the level `_Requested_api_level`, and returns the actual API level
// in use. Once the API level has been set by calling this function (or detected by a call to one of the atomic wait
// functions), it can no longer be changed.
__std_atomic_api_level __stdcall __std_atomic_set_api_level(__std_atomic_api_level _Requested_api_level) noexcept;

// Support for atomic waits.
// The "direct" functions are used when the underlying infrastructure can use WaitOnAddress directly; that is, _Size is
// 1, 2, 4, or 8. The contract is the same as the WaitOnAddress function from the Windows SDK.
// 1, 2, 4, or 8. The contract is the same as the WaitOnAddress function from the Windows SDK. If WaitOnAddress is not
// available on the current platform, falls back to a similar solution based on SRWLOCK and CONDITION_VARIABLE.
int __stdcall __std_atomic_wait_direct(
const void* _Storage, void* _Comparand, size_t _Size, unsigned long _Remaining_timeout) noexcept;
void __stdcall __std_atomic_notify_one_direct(const void* _Storage) noexcept;
Expand Down
10 changes: 7 additions & 3 deletions stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -1939,6 +1939,7 @@ compiler option, or define _ALLOW_RTCc_IN_STL to suppress this error.
#endif // defined(MRTDLL) && !defined(_M_CEE_PURE)

#define _STL_WIN32_WINNT_VISTA 0x0600 // _WIN32_WINNT_VISTA from sdkddkver.h
#define _STL_WIN32_WINNT_WIN7 0x0601 // _WIN32_WINNT_WIN7 from sdkddkver.h
#define _STL_WIN32_WINNT_WIN8 0x0602 // _WIN32_WINNT_WIN8 from sdkddkver.h
#define _STL_WIN32_WINNT_WIN10 0x0A00 // _WIN32_WINNT_WIN10 from sdkddkver.h

Expand All @@ -1947,10 +1948,13 @@ compiler option, or define _ALLOW_RTCc_IN_STL to suppress this error.
#if defined(_M_ARM64)
// The first ARM64 Windows was Windows 10
#define _STL_WIN32_WINNT _STL_WIN32_WINNT_WIN10
#else // ^^^ defined(_M_ARM64) / !defined(_M_ARM64) vvv
// The earliest Windows supported by this implementation is Windows 8
#elif defined(_M_ARM) || defined(_ONECORE) || defined(_CRT_APP)
// The first ARM or OneCore or App Windows was Windows 8
#define _STL_WIN32_WINNT _STL_WIN32_WINNT_WIN8
#endif // ^^^ !defined(_M_ARM64) ^^^
#else // ^^^ default to Win8 / default to Win7 vvv
// The earliest Windows supported by this implementation is Windows 7
#define _STL_WIN32_WINNT _STL_WIN32_WINNT_WIN7
#endif // ^^^ !defined(_M_ARM) && !defined(_M_ARM64) && !defined(_ONECORE) && !defined(_CRT_APP) ^^^
#endif // !defined(_STL_WIN32_WINNT)

#ifdef __cpp_noexcept_function_type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
<TargetLib Include="$(CrtLibPath)\vcruntime$(BuildSuffix)$(ClrLibSuffix).lib"/>
<TargetLib Include="$(UniversalCRTLib)"/>
<TargetLib Condition="'$(MsvcpFlavor)' == 'kernel32' or '$(MsvcpFlavor)' == 'netfx'" Include="$(SdkLibPath)\advapi32.lib"/>
<TargetLib Condition="'$(MsvcpFlavor)' == 'kernel32' or '$(MsvcpFlavor)' == 'netfx'" Include="$(SdkLibPath)\synchronization.lib"/>
</ItemGroup>

<!-- Copy the output dll and pdb to various destinations -->
Expand Down
166 changes: 157 additions & 9 deletions stl/src/atomic_wait.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@

#include <Windows.h>

#pragma comment(lib, "synchronization")

namespace {
constexpr unsigned long long _Atomic_wait_no_deadline = 0xFFFF'FFFF'FFFF'FFFF;

Expand Down Expand Up @@ -91,13 +89,134 @@ namespace {
}
#endif // defined(_DEBUG)
}

#ifndef _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE
#if _STL_WIN32_WINNT >= _STL_WIN32_WINNT_WIN8
#define _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE 1
#else // ^^^ _STL_WIN32_WINNT >= _STL_WIN32_WINNT_WIN8 / _STL_WIN32_WINNT < _STL_WIN32_WINNT_WIN8 vvv
#define _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE 0
#endif // ^^^ _STL_WIN32_WINNT < _STL_WIN32_WINNT_WIN8 ^^^
#endif // !defined(_ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE)

#if _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE

#pragma comment(lib, "synchronization")

#define __crtWaitOnAddress WaitOnAddress
#define __crtWakeByAddressSingle WakeByAddressSingle
#define __crtWakeByAddressAll WakeByAddressAll

#else // ^^^ _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE / !_ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE vvv

struct _Wait_functions_table {
_STD atomic<decltype(&::WaitOnAddress)> _Pfn_WaitOnAddress{nullptr};
_STD atomic<decltype(&::WakeByAddressSingle)> _Pfn_WakeByAddressSingle{nullptr};
_STD atomic<decltype(&::WakeByAddressAll)> _Pfn_WakeByAddressAll{nullptr};
_STD atomic<__std_atomic_api_level> _Api_level{__std_atomic_api_level::__not_set};
};

_Wait_functions_table _Wait_functions;

void _Force_wait_functions_srwlock_only() noexcept {
auto _Local = _Wait_functions._Api_level.load(_STD memory_order_acquire);
if (_Local <= __std_atomic_api_level::__detecting) {
while (!_Wait_functions._Api_level.compare_exchange_weak(
_Local, __std_atomic_api_level::__has_srwlock, _STD memory_order_acq_rel)) {
if (_Local > __std_atomic_api_level::__detecting) {
return;
}
}
}
}

[[nodiscard]] __std_atomic_api_level _Init_wait_functions(__std_atomic_api_level _Level) {
while (!_Wait_functions._Api_level.compare_exchange_weak(
_Level, __std_atomic_api_level::__detecting, _STD memory_order_acq_rel)) {
if (_Level > __std_atomic_api_level::__detecting) {
return _Level;
}
}

_Level = __std_atomic_api_level::__has_srwlock;

const HMODULE _Sync_module = GetModuleHandleW(L"api-ms-win-core-synch-l1-2-0.dll");
if (_Sync_module != nullptr) {
const auto _Wait_on_address =
reinterpret_cast<decltype(&::WaitOnAddress)>(GetProcAddress(_Sync_module, "WaitOnAddress"));
const auto _Wake_by_address_single =
reinterpret_cast<decltype(&::WakeByAddressSingle)>(GetProcAddress(_Sync_module, "WakeByAddressSingle"));
const auto _Wake_by_address_all =
reinterpret_cast<decltype(&::WakeByAddressAll)>(GetProcAddress(_Sync_module, "WakeByAddressAll"));

if (_Wait_on_address != nullptr && _Wake_by_address_single != nullptr && _Wake_by_address_all != nullptr) {
_Wait_functions._Pfn_WaitOnAddress.store(_Wait_on_address, _STD memory_order_relaxed);
_Wait_functions._Pfn_WakeByAddressSingle.store(_Wake_by_address_single, _STD memory_order_relaxed);
_Wait_functions._Pfn_WakeByAddressAll.store(_Wake_by_address_all, _STD memory_order_relaxed);
_Level = __std_atomic_api_level::__has_wait_on_address;
}
}

// for __has_srwlock, relaxed would have been enough, not distinguishing for consistency
_Wait_functions._Api_level.store(_Level, _STD memory_order_release);
return _Level;
}

[[nodiscard]] __std_atomic_api_level _Acquire_wait_functions() noexcept {
auto _Level = _Wait_functions._Api_level.load(_STD memory_order_acquire);
if (_Level <= __std_atomic_api_level::__detecting) {
_Level = _Init_wait_functions(_Level);
}

return _Level;
}

[[nodiscard]] BOOL __crtWaitOnAddress(
volatile VOID* Address, PVOID CompareAddress, SIZE_T AddressSize, DWORD dwMilliseconds) {
const auto _Wait_on_address = _Wait_functions._Pfn_WaitOnAddress.load(_STD memory_order_relaxed);
return _Wait_on_address(Address, CompareAddress, AddressSize, dwMilliseconds);
}

VOID __crtWakeByAddressSingle(PVOID Address) {
const auto _Wake_by_address_single = _Wait_functions._Pfn_WakeByAddressSingle.load(_STD memory_order_relaxed);
_Wake_by_address_single(Address);
}

VOID __crtWakeByAddressAll(PVOID Address) {
const auto _Wake_by_address_all = _Wait_functions._Pfn_WakeByAddressAll.load(_STD memory_order_relaxed);
_Wake_by_address_all(Address);
}

bool __stdcall _Atomic_wait_are_equal_direct_fallback(
const void* _Storage, void* _Comparand, size_t _Size, void*) noexcept {
switch (_Size) {
case 1:
return __iso_volatile_load8(static_cast<const char*>(_Storage)) == *static_cast<const char*>(_Comparand);
case 2:
return __iso_volatile_load16(static_cast<const short*>(_Storage)) == *static_cast<const short*>(_Comparand);
case 4:
return __iso_volatile_load32(static_cast<const int*>(_Storage)) == *static_cast<const int*>(_Comparand);
case 8:
return __iso_volatile_load64(static_cast<const long long*>(_Storage))
== *static_cast<const long long*>(_Comparand);
default:
_CSTD abort();
}
}
#endif // _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE
} // unnamed namespace

extern "C" {
int __stdcall __std_atomic_wait_direct(const void* const _Storage, void* const _Comparand, const size_t _Size,
const unsigned long _Remaining_timeout) noexcept {
const auto _Result =
WaitOnAddress(const_cast<volatile void*>(_Storage), const_cast<void*>(_Comparand), _Size, _Remaining_timeout);
#if _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE == 0
if (_Acquire_wait_functions() < __std_atomic_api_level::__has_wait_on_address) {
return __std_atomic_wait_indirect(
_Storage, _Comparand, _Size, nullptr, &_Atomic_wait_are_equal_direct_fallback, _Remaining_timeout);
}
#endif // _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE == 0

const auto _Result = __crtWaitOnAddress(
const_cast<volatile void*>(_Storage), const_cast<void*>(_Comparand), _Size, _Remaining_timeout);

if (!_Result) {
_Assume_timeout();
Expand All @@ -106,11 +225,25 @@ int __stdcall __std_atomic_wait_direct(const void* const _Storage, void* const _
}

void __stdcall __std_atomic_notify_one_direct(const void* const _Storage) noexcept {
WakeByAddressSingle(const_cast<void*>(_Storage));
#if _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE == 0
if (_Acquire_wait_functions() < __std_atomic_api_level::__has_wait_on_address) {
__std_atomic_notify_one_indirect(_Storage);
return;
}
#endif // _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE = 0

__crtWakeByAddressSingle(const_cast<void*>(_Storage));
}

void __stdcall __std_atomic_notify_all_direct(const void* const _Storage) noexcept {
WakeByAddressAll(const_cast<void*>(_Storage));
#if _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE == 0
if (_Acquire_wait_functions() < __std_atomic_api_level::__has_wait_on_address) {
__std_atomic_notify_all_indirect(_Storage);
return;
}
#endif // _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE == 0

__crtWakeByAddressAll(const_cast<void*>(_Storage));
}

void __stdcall __std_atomic_notify_one_indirect(const void* const _Storage) noexcept {
Expand Down Expand Up @@ -206,10 +339,25 @@ unsigned long __stdcall __std_atomic_wait_get_remaining_timeout(unsigned long lo
return static_cast<unsigned long>(_Remaining);
}

// TRANSITION, ABI: preserved for binary compatibility
enum class __std_atomic_api_level : unsigned long { __not_set, __detecting, __has_srwlock, __has_wait_on_address };
__std_atomic_api_level __stdcall __std_atomic_set_api_level(__std_atomic_api_level) noexcept {
__std_atomic_api_level __stdcall __std_atomic_set_api_level(__std_atomic_api_level _Requested_api_level) noexcept {
#if _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE
(void) _Requested_api_level;
return __std_atomic_api_level::__has_wait_on_address;
#else // ^^^ _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE / !_ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE vvv
switch (_Requested_api_level) {
case __std_atomic_api_level::__not_set:
case __std_atomic_api_level::__detecting:
_CSTD abort();
case __std_atomic_api_level::__has_srwlock:
_Force_wait_functions_srwlock_only();
break;
case __std_atomic_api_level::__has_wait_on_address:
default: // future compat: new header using an old DLL will get the highest requested level supported
break;
}

return _Acquire_wait_functions();
#endif // !_ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE
}

#pragma warning(push)
Expand Down
11 changes: 11 additions & 0 deletions stl/src/awint.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@

_CRT_BEGIN_C_HEADER

#if _STL_WIN32_WINNT >= _WIN32_WINNT_WIN8

#define __crtGetSystemTimePreciseAsFileTime(lpSystemTimeAsFileTime) \
GetSystemTimePreciseAsFileTime(lpSystemTimeAsFileTime)

#else // ^^^ _STL_WIN32_WINNT >= _WIN32_WINNT_WIN8 / _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 vvv

_CRTIMP2 void __cdecl __crtGetSystemTimePreciseAsFileTime(_Out_ LPFILETIME lpSystemTimeAsFileTime) noexcept;

#endif // ^^^ _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 ^^^

_CRTIMP2 int __cdecl __crtCompareStringA(_In_z_ LPCWSTR _LocaleName, _In_ DWORD _DwCmpFlags,
_In_reads_(_CchCount1) LPCSTR _LpString1, _In_ int _CchCount1, _In_reads_(_CchCount2) LPCSTR _LpString2,
_In_ int _CchCount2, _In_ int _CodePage) noexcept;
Expand Down
6 changes: 6 additions & 0 deletions stl/src/ppltasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ namespace Concurrency {

namespace details {
[[noreturn]] _CRTIMP2 void __cdecl _ReportUnobservedException() {
#if (defined(_M_IX86) || defined(_M_X64)) && !defined(_CRT_APP) && _STL_WIN32_WINNT < _WIN32_WINNT_WIN8
if (!IsProcessorFeaturePresent(PF_FASTFAIL_AVAILABLE)) {
std::abort();
}
#endif // ^^^ __fastfail conditionally available ^^^

__fastfail(FAST_FAIL_INVALID_ARG);
}

Expand Down
18 changes: 16 additions & 2 deletions stl/src/winapisupp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ namespace {
// Use this macro for defining the following function pointers
#define DEFINEFUNCTIONPOINTER(fn_name) decltype(&fn_name) __KERNEL32Function_##fn_name = nullptr

#if _STL_WIN32_WINNT < _WIN32_WINNT_WIN8
DEFINEFUNCTIONPOINTER(GetSystemTimePreciseAsFileTime);
#endif // _STL_WIN32_WINNT < _WIN32_WINNT_WIN8

DEFINEFUNCTIONPOINTER(GetTempPath2W);

// Use this macro for caching a function pointer from a DLL
Expand Down Expand Up @@ -154,9 +158,15 @@ extern "C" _CRTIMP2 BOOL __cdecl __crtSetFileInformationByHandle(_In_ HANDLE con

#if _STL_WIN32_WINNT < _WIN32_WINNT_WIN8

// TRANSITION, ABI: preserved for binary compatibility
extern "C" _CRTIMP2 void __cdecl __crtGetSystemTimePreciseAsFileTime(_Out_ LPFILETIME lpSystemTimeAsFileTime) noexcept {
GetSystemTimePreciseAsFileTime(lpSystemTimeAsFileTime);
// use GetSystemTimePreciseAsFileTime if it is available (only on Windows 8+)...
IFDYNAMICGETCACHEDFUNCTION(GetSystemTimePreciseAsFileTime) {
pfGetSystemTimePreciseAsFileTime(lpSystemTimeAsFileTime);
return;
}

// ...otherwise use GetSystemTimeAsFileTime.
GetSystemTimeAsFileTime(lpSystemTimeAsFileTime);
}

#endif // _STL_WIN32_WINNT < _WIN32_WINNT_WIN8
Expand Down Expand Up @@ -186,6 +196,10 @@ static int __cdecl initialize_pointers() noexcept {
HINSTANCE hKernel32 = GetModuleHandleW(L"kernel32.dll");
_Analysis_assume_(hKernel32);

#if _STL_WIN32_WINNT < _WIN32_WINNT_WIN8
STOREFUNCTIONPOINTER(hKernel32, GetSystemTimePreciseAsFileTime);
#endif // _STL_WIN32_WINNT < _WIN32_WINNT_WIN8

// Note that GetTempPath2W is defined as of Windows 10 Build 20348 (a server release) or Windows 11,
// but there is no "_WIN32_WINNT_WIN11" constant, so we will always dynamically load it
STOREFUNCTIONPOINTER(hKernel32, GetTempPath2W);
Expand Down
2 changes: 1 addition & 1 deletion stl/src/xtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ _CRTIMP2_PURE long long __cdecl _Xtime_get_ticks() noexcept {
constexpr long long _Epoch = 0x19DB1DED53E8000LL;

FILETIME ft;
GetSystemTimePreciseAsFileTime(&ft);
__crtGetSystemTimePreciseAsFileTime(&ft);
return ((static_cast<long long>(ft.dwHighDateTime)) << 32) + static_cast<long long>(ft.dwLowDateTime) - _Epoch;
}

Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ tests\P1032R1_miscellaneous_constexpr
tests\P1132R7_out_ptr
tests\P1135R6_atomic_flag_test
tests\P1135R6_atomic_wait
tests\P1135R6_atomic_wait_win7
tests\P1135R6_barrier
tests\P1135R6_latch
tests\P1135R6_semaphore
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P1135R6_atomic_wait_win7/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_20_matrix.lst
Loading