Skip to content

Commit

Permalink
Temporarily restore support for Win7 / Server 2008 R2 (microsoft#4857)
Browse files Browse the repository at this point in the history
  • Loading branch information
StephanTLavavej authored Jul 24, 2024
1 parent 6c32079 commit d2fa3a3
Show file tree
Hide file tree
Showing 12 changed files with 590 additions and 18 deletions.
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

0 comments on commit d2fa3a3

Please sign in to comment.