From f49eee878998101e83bdd24cb96acc7690a5caf6 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Sat, 14 Nov 2020 18:00:17 -0800 Subject: [PATCH 01/23] [time.clocks] C++20 clocks --- stl/CMakeLists.txt | 4 +- stl/inc/chrono | 681 +++++++++++++++++- stl/inc/filesystem | 19 +- stl/inc/header-units.json | 1 + stl/inc/xfilesystem_abi.h | 5 +- stl/inc/xtzdb.h | 40 + stl/inc/xutility | 1 + .../stl_atomic_wait.files.settings.targets | 1 + .../stl_base/stl.files.settings.targets | 1 + stl/src/msvcp_atomic_wait.src | 1 + stl/src/tzdb.cpp | 63 ++ tests/std/test.lst | 1 + tests/std/tests/P0355R7_clocks/env.lst | 4 + tests/std/tests/P0355R7_clocks/test.cpp | 320 ++++++++ 14 files changed, 1121 insertions(+), 21 deletions(-) create mode 100644 stl/inc/xtzdb.h create mode 100644 stl/src/tzdb.cpp create mode 100644 tests/std/tests/P0355R7_clocks/env.lst create mode 100644 tests/std/tests/P0355R7_clocks/test.cpp diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 298f1439d0..b98e0da8f0 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -232,6 +232,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/xtimec.h ${CMAKE_CURRENT_LIST_DIR}/inc/xtr1common ${CMAKE_CURRENT_LIST_DIR}/inc/xtree + ${CMAKE_CURRENT_LIST_DIR}/inc/xtzdb.h ${CMAKE_CURRENT_LIST_DIR}/inc/xutility ${CMAKE_CURRENT_LIST_DIR}/inc/ymath.h ${CMAKE_CURRENT_LIST_DIR}/inc/yvals.h @@ -398,6 +399,7 @@ set(SOURCES_SATELLITE_ATOMIC_WAIT ${CMAKE_CURRENT_LIST_DIR}/src/atomic_wait.cpp ${CMAKE_CURRENT_LIST_DIR}/src/parallel_algorithms.cpp ${CMAKE_CURRENT_LIST_DIR}/src/syncstream.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/tzdb.cpp ) set(SOURCES_SATELLITE_CODECVT_IDS @@ -497,7 +499,7 @@ function(add_stl_dlls D_SUFFIX THIS_CONFIG_DEFINITIONS THIS_CONFIG_COMPILE_OPTIO file(WRITE "${_ATOMIC_WAIT_DEF_NAME}" "${_ATOMIC_WAIT_DEF_CONTENTS}") add_library(msvcp${D_SUFFIX}_atomic_wait SHARED "${_ATOMIC_WAIT_DEF_NAME}") - 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") + 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 "${_ATOMIC_WAIT_OUTPUT_NAME}") diff --git a/stl/inc/chrono b/stl/inc/chrono index c664419e17..3a616cbd2b 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -16,10 +16,11 @@ #include #if _HAS_CXX20 +#include #include -#ifdef __cpp_lib_concepts -#include -#endif // defined(__cpp_lib_concepts) +#include +#include +#include #endif // _HAS_CXX20 #pragma pack(push, _CRT_PACKING) @@ -2086,6 +2087,680 @@ namespace chrono { return hours{_Ret}; } + + // [time.zone.leap] + + class leap_second { + public: + leap_second(const leap_second&) = default; + leap_second& operator=(const leap_second&) = default; + + constexpr leap_second(const sys_seconds& _Date_, bool _Is_positive_) noexcept + : _Date{_Date_}, _Is_positive{_Is_positive_} {} + + _NODISCARD constexpr sys_seconds date() const noexcept { + return _Date; + } + + _NODISCARD constexpr seconds value() const noexcept { + return _Is_positive ? seconds{1} : seconds{-1}; + } + + _NODISCARD constexpr bool _Positive() const noexcept { + return _Is_positive; + } + + private: + sys_seconds _Date; + bool _Is_positive; + }; + + _NODISCARD inline bool operator==(const leap_second& _Left, const leap_second& _Right) noexcept { + return _Left.date() == _Right.date(); + } + template + _NODISCARD bool operator==(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + return _Left.date() == _Right; + } + + template + _NODISCARD bool operator<(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + return _Left.date() < _Right; + } + template + _NODISCARD bool operator<(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { + return _Left < _Right.date(); + } + + template + _NODISCARD bool operator>(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + return _Right < _Left.date(); + } + template + _NODISCARD bool operator>(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { + return _Right.date() < _Left; + } + + template + _NODISCARD bool operator<=(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + return !(_Right < _Left.date()); + } + template + _NODISCARD bool operator<=(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { + return !(_Right.date() < _Left); + } + + template + _NODISCARD bool operator>=(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + return !(_Left.date() < _Right); + } + template + _NODISCARD bool operator>=(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { + return !(_Left < _Right.date()); + } + + template + requires three_way_comparable_with> constexpr auto operator<=>( + const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + return _Left.date() <=> _Right; + } + constexpr inline strong_ordering operator<=>(const leap_second& _Left, const leap_second& _Right) noexcept { + return _Left.date() <=> _Right.date(); + } + + inline pair, bool> _Xtzdb_generate_leap_seconds(const size_t _Current_size) { + // Returns empty vector if no new leap seconds are found. + constexpr static leap_second _Pre_2018_leap_seconds[]{ + {sys_seconds{seconds{78796800}}, true}, + {sys_seconds{seconds{94694400}}, true}, + {sys_seconds{seconds{126230400}}, true}, + {sys_seconds{seconds{157766400}}, true}, + {sys_seconds{seconds{189302400}}, true}, + {sys_seconds{seconds{220924800}}, true}, + {sys_seconds{seconds{252460800}}, true}, + {sys_seconds{seconds{283996800}}, true}, + {sys_seconds{seconds{315532800}}, true}, + {sys_seconds{seconds{362793600}}, true}, + {sys_seconds{seconds{394329600}}, true}, + {sys_seconds{seconds{425865600}}, true}, + {sys_seconds{seconds{489024000}}, true}, + {sys_seconds{seconds{567993600}}, true}, + {sys_seconds{seconds{631152000}}, true}, + {sys_seconds{seconds{662688000}}, true}, + {sys_seconds{seconds{709948800}}, true}, + {sys_seconds{seconds{741484800}}, true}, + {sys_seconds{seconds{773020800}}, true}, + {sys_seconds{seconds{820454400}}, true}, + {sys_seconds{seconds{867715200}}, true}, + {sys_seconds{seconds{915148800}}, true}, + {sys_seconds{seconds{1136073600}}, true}, + {sys_seconds{seconds{1230768000}}, true}, + {sys_seconds{seconds{1341100800}}, true}, + {sys_seconds{seconds{1435708800}}, true}, + {sys_seconds{seconds{1483228800}}, true}, + }; + constexpr size_t _Pre_2018_count = _STD size(_Pre_2018_leap_seconds); + + size_t _New_reg_ls_size; + auto _Reg_ls_data = unique_ptr<_RegistryLeapSecondInfo[]>{__std_tzdb_get_reg_leap_seconds( + (_STD max)(_Current_size, _Pre_2018_count) - _Pre_2018_count, &_New_reg_ls_size)}; + + const size_t _New_size = _Pre_2018_count + _New_reg_ls_size; + if (_New_reg_ls_size > _Current_size && !_Reg_ls_data) { + _Xbad_alloc(); + } else if (_New_reg_ls_size == 0 && _Reg_ls_data) { + _XGetLastError(); + } + + vector _Leap_sec_info; + bool _All_ls_positive = true; + + if (_New_size > _Current_size) { + _Leap_sec_info.reserve(_New_size); + _Leap_sec_info.insert( + _Leap_sec_info.begin(), _STD cbegin(_Pre_2018_leap_seconds), _STD cend(_Pre_2018_leap_seconds)); + + for (size_t _Idx = 0; _Idx < _New_reg_ls_size; ++_Idx) { + // Leap second happens at ls._Hour:59:59. We store the next second after, so add a whole hour. + const auto& _Ls = _Reg_ls_data[_Idx]; + const auto _Date = + static_cast(year_month_day{year{_Ls._Year}, month{_Ls._Month}, day{_Ls._Day}}) + + hours{_Ls._Hour + 1}; + _Leap_sec_info.emplace_back(_Date, !_Ls._Negative); + _All_ls_positive = _All_ls_positive && !_Ls._Negative; + } + } + + return {_Leap_sec_info, _All_ls_positive}; + } + + // [time.zone.db] + + // TRANSITION: work in progress + struct tzdb { + vector leap_seconds; + bool _All_ls_positive; + }; + + // TRANSITION: work in progress + class tzdb_list { + private: + using _ListType = forward_list; + + public: + using const_iterator = _ListType::const_iterator; + + tzdb_list(const tzdb_list&) = delete; + tzdb_list& operator=(const tzdb_list&) = delete; + tzdb_list() = default; + + const tzdb& front() const noexcept { + _Shared_lock _Lk(_Tzdb_mutex); + return _Tzdb_list.front(); + } + + template + void _Emplace_front(_ArgsTy&&... _Args) { + _Unique_lock _Lk(_Tzdb_mutex); + _Tzdb_list.emplace_front(_STD forward<_ArgsTy>(_Args)...); + } + + const tzdb& _Reload() { + _Unique_lock _Lk(_Tzdb_mutex); + auto [_Leap_sec, _All_ls_positive] = _Xtzdb_generate_leap_seconds(_Tzdb_list.front().leap_seconds.size()); + if (!_Leap_sec.empty()) { + _Emplace_front(tzdb{_STD move(_Leap_sec), _All_ls_positive}); + } + return front(); + } + + void _Initialize() { + _Shared_lock _Shr_lk(_Tzdb_mutex); + if (_Tzdb_list.empty()) { + _Shr_lk._Unlock(); + _Unique_lock _Lk(_Tzdb_mutex); + if (_Tzdb_list.empty()) { + auto _Leap_sec = _Xtzdb_generate_leap_seconds(0); + _Tzdb_list.emplace_front(tzdb{_STD move(_Leap_sec.first), _Leap_sec.second}); + } + } + } + + private: + _ListType _Tzdb_list; + mutable _Smtx_t _Tzdb_mutex = {}; + + struct _NODISCARD _Shared_lock { + explicit _Shared_lock(_Smtx_t& _Mtx_) : _Mtx{&_Mtx_} { + _Smtx_lock_shared(_Mtx); + } + + void _Unlock() { + if (owns) { + _Smtx_unlock_shared(_Mtx); + } + owns = false; + } + + ~_Shared_lock() { + if (owns) { + _Smtx_unlock_shared(_Mtx); + } + } + + _Smtx_t* _Mtx; + bool owns = true; + }; + + struct _NODISCARD _Unique_lock { + explicit _Unique_lock(_Smtx_t& _Mtx_) : _Mtx{&_Mtx_} { + _Smtx_lock_exclusive(_Mtx); + } + + ~_Unique_lock() { + _Smtx_unlock_exclusive(_Mtx); + } + + _Smtx_t* _Mtx; + }; + }; + + _INLINE_VAR tzdb_list _Global_tzdb_list; + + inline tzdb_list& get_tzdb_list() { + _Global_tzdb_list._Initialize(); + return _Global_tzdb_list; + } + + inline const tzdb& get_tzdb() { + return _CHRONO get_tzdb_list().front(); + } + + inline const tzdb& reload_tzdb() { + return _CHRONO get_tzdb_list()._Reload(); + } + + // [time.clock.utc] + + class utc_clock; + template + using utc_time = time_point; + using utc_seconds = utc_time; + + struct leap_second_info { + bool is_leap_second; + seconds elapsed; + }; + + template + _NODISCARD leap_second_info get_leap_second_info(const utc_time<_Duration>& _Time) { + const utc_seconds _Time_floor = _CHRONO floor(_Time); + const auto& _Tzdb = _CHRONO get_tzdb(); + const auto& _Ls_vector = _Tzdb.leap_seconds; + + // Find first leap second after _Time and the accumulated offset of all *prior* leap seconds. + seconds _Offset; + vector::const_iterator _It; + if (_Tzdb._All_ls_positive) { + // _It here is actually the *2nd* leap second after _Time for the _Offset-1 seconds before the next leap + // second insertion. This doesn't overlap with the actual leap second insertion period, so prev(_It) can + // always be used to detect if _Time is during an insertion. _Offset, however, needs to be corrected later. + // For example, if leap seconds are {100, 200, 300, 400}, we have + // + // UTC sys *_It _Offset + // 99 99 100 0 + // 100 X 200 1 + // 101 100 200 1 + // 102 101 200 1 + // ... + // 199 198 200 1 + // 200 199 300 2^ + // 201 X 300 2 + // 202 200 300 2 + // ... + // 299 297 300 2 + // 300 298 400 3^ + // 301 299 400 3^ + // 302 X 400 3 + // 303 300 400 3 + // + // ^_It points to 2nd leap second and _Offset value is wrong. + + _It = _STD upper_bound(_Ls_vector.begin(), _Ls_vector.end(), sys_seconds{_Time_floor.time_since_epoch()}); + _Offset = seconds{_It - _Ls_vector.begin()}; + } else { + _Offset = seconds{0}; + for (_It = _Ls_vector.begin(); _It != _Ls_vector.end(); ++_It) { + const auto _New_offset = _Offset + _It->value(); + // Positive leap seconds are counted one second earlier, when insertion begins. + if (utc_seconds{_It->date().time_since_epoch()} + (_It->_Positive() ? _Offset : _New_offset) + > _Time_floor) { + break; + } + _Offset = _New_offset; + } + } + + if (_It != _Ls_vector.begin()) { + // Convert to the last leap second before or equal to _Time (except for the special case described above). + const auto _Last_leap = &*(--_It); + const auto _Utc_leap_second = --utc_seconds{_Last_leap->date().time_since_epoch() + _Offset}; + const auto _Leap_cmp = _Utc_leap_second <=> _Time_floor; + if (_Tzdb._All_ls_positive && _STD is_gt(_Leap_cmp)) { + --_Offset; + } + return {_Last_leap->_Positive() && _STD is_eq(_Leap_cmp), _Offset}; + } else { + return {false, _Offset}; + } + } + + class utc_clock { + public: + using rep = system_clock::rep; + using period = system_clock::period; + using duration = duration; + using time_point = time_point; + static constexpr bool is_steady = system_clock::is_steady; + + _NODISCARD static time_point now() { + return from_sys(system_clock::now()); + } + + template + _NODISCARD static sys_time> to_sys(const utc_time<_Duration>& _Utc_time) { + const auto _Lsi{get_leap_second_info(_Utc_time)}; + sys_time> _Sys_time{_Utc_time.time_since_epoch() - _Lsi.elapsed}; + if (_Lsi.is_leap_second) { + constexpr auto _Delta{seconds{1} - common_type_t<_Duration, seconds>{1}}; + _Sys_time = _CHRONO floor(_Sys_time) + _Delta; + } + return _Sys_time; + } + + template + _NODISCARD static utc_time> from_sys(const sys_time<_Duration>& _Sys_time) { + const auto& _Tzdb = _CHRONO get_tzdb(); + const auto& _Ls_vector = _Tzdb.leap_seconds; + const auto _It = _STD upper_bound(_Ls_vector.begin(), _Ls_vector.end(), _CHRONO floor(_Sys_time)); + + seconds _Offset; + if (_Tzdb._All_ls_positive) { + _Offset = seconds{_It - _Ls_vector.begin()}; + } else { + _Offset = seconds{0}; + for (auto _Ls = _Tzdb.leap_seconds.begin(); _Ls != _It; ++_Ls) { + _Offset += _Ls->value(); + } + } + + return utc_time>{_Sys_time.time_since_epoch() + _Offset}; + } + }; + + // [time.clock.tai] + + class tai_clock; + + template + using tai_time = time_point; + using tai_seconds = tai_time; + + class tai_clock { + public: + using rep = system_clock::rep; + using period = system_clock::period; + using duration = duration; + using time_point = time_point; + static constexpr bool is_steady = system_clock::is_steady; + + static constexpr seconds _Tai_epoch_adjust{378691210}; + + _NODISCARD static time_point now() noexcept(noexcept(utc_clock::now())) { + return from_utc(utc_clock::now()); + } + + template + _NODISCARD static utc_time> to_utc( + const tai_time<_Duration>& _Time) noexcept { + return utc_time>{_Time.time_since_epoch()} - _Tai_epoch_adjust; + } + + template + _NODISCARD static tai_time> from_utc( + const utc_time<_Duration>& _Time) noexcept { + return tai_time>{_Time.time_since_epoch()} + _Tai_epoch_adjust; + } + }; + + // [time.clock.gps] + + class gps_clock; + + template + using gps_time = time_point; + using gps_seconds = gps_time; + + class gps_clock { + public: + using rep = system_clock::rep; + using period = system_clock::period; + using duration = chrono::duration; + using time_point = chrono::time_point; + static constexpr bool is_steady = system_clock::is_steady; + + static constexpr seconds _Gps_epoch_adjust{-315964809}; + + _NODISCARD static time_point now() noexcept(noexcept(utc_clock::now())) { + return from_utc(utc_clock::now()); + } + + template + _NODISCARD static utc_time> to_utc( + const gps_time<_Duration>& _Time) noexcept { + return utc_time>{_Time.time_since_epoch()} - _Gps_epoch_adjust; + } + + template + _NODISCARD static gps_time> from_utc( + const utc_time<_Duration>& _Time) noexcept { + return gps_time>{_Time.time_since_epoch()} + _Gps_epoch_adjust; + } + }; + + // [time.clock.file] + + struct _File_time_clock; + using file_clock = _File_time_clock; + + template + using file_time = time_point; +#endif // ^^^ _HAS_CXX20 + +#if _HAS_CXX17 + inline constexpr long long __std_fs_file_time_epoch_adjustment = 0x19DB1DED53E8000LL; // TRANSITION, ABI + + struct _File_time_clock { // Implementation of trivial-clock + using rep = long long; + using period = chrono::system_clock::period; + using duration = chrono::duration; + using time_point = chrono::time_point<_File_time_clock>; + static constexpr bool is_steady = false; + + _NODISCARD static time_point now() noexcept { // get current time; undo epoch adjustment + return time_point(duration(_Xtime_get_ticks() + __std_fs_file_time_epoch_adjustment)); // TRANSITION, ABI + } + +#if _HAS_CXX20 + // Assumes that FILETIME counts leaps seconds only after the first 27 (i.e., after 1 January 2017), even though + // systems can opt out of this behavior. + static constexpr seconds _Skipped_filetime_leap_seconds{27}; + static constexpr sys_days _Cutoff{year_month_day{year{2017}, January, day{1}}}; + + template + _NODISCARD static utc_time> to_utc(const file_time<_Duration>& _File_time) { + using _CT = common_type_t<_Duration, seconds>; + const auto _Ticks = + _File_time.time_since_epoch() - duration_cast(duration{__std_fs_file_time_epoch_adjustment}); + + if (_Ticks < _Cutoff.time_since_epoch()) { + return utc_clock::from_sys(sys_time<_CT>{_Ticks}); + } else { + return utc_time<_CT>{_Ticks + _Skipped_filetime_leap_seconds}; + } + } + + template + _NODISCARD static file_time> from_utc(const utc_time<_Duration>& _Utc_time) { + using _CT = common_type_t<_Duration, seconds>; + file_time<_CT> _File_time{duration_cast(duration{__std_fs_file_time_epoch_adjustment})}; + + if (_Utc_time < utc_seconds{_Cutoff.time_since_epoch()} + _Skipped_filetime_leap_seconds) { + _File_time += utc_clock::to_sys(_Utc_time).time_since_epoch(); + } else { + _File_time += _Utc_time.time_since_epoch() - _Skipped_filetime_leap_seconds; + } + + return _File_time; + } +#endif // _HAS_CXX20 + }; +#endif // _HAS_CXX17 + +#if _HAS_CXX20 + // [time.clock.conv] + + template + struct clock_time_conversion; + + // [time.clock.cast.id] + + template + struct clock_time_conversion<_Clock, _Clock> { + template + _NODISCARD auto operator()(const time_point<_Clock, _Duration>& _Time) const + noexcept(is_arithmetic_v) /* strengthened */ { + return _Time; + } + }; + + template <> + struct clock_time_conversion { + template + _NODISCARD auto operator()(const sys_time<_Duration>& _Time) const + noexcept(is_arithmetic_v) /* strengthened */ { + return _Time; + } + }; + + template <> + struct clock_time_conversion { + template + _NODISCARD auto operator()(const utc_time<_Duration>& _Time) const + noexcept(is_arithmetic_v) /* strengthened */ { + return _Time; + } + }; + + // [time.clock.cast.sys.utc] + + template <> + struct clock_time_conversion { + template + _NODISCARD auto operator()(const sys_time<_Duration>& _Sys_time) const { + return utc_clock::from_sys(_Sys_time); + } + }; + + template <> + struct clock_time_conversion { + template + _NODISCARD auto operator()(const utc_time<_Duration>& _Utc_time) const { + return utc_clock::to_sys(_Utc_time); + } + }; + + // [time.clock.cast.sys] + + template + concept _Is_time_point = requires { + typename _Ty::duration; + requires same_as, _Ty>; + }; + + template + concept _Convertable_to_sys_time = + same_as<_Clock, system_clock> || requires(const time_point<_Clock, _Duration>& _Time) { + { _Clock::to_sys(_Time) } + ->_Is_time_point; + }; + + template + concept _Convertable_from_sys_time = same_as<_Clock, system_clock> || requires(const sys_time<_Duration>& _Time) { + { _Clock::from_sys(_Time) } + ->_Is_time_point<_Clock>; + }; + + template + struct clock_time_conversion { + template + _NODISCARD auto operator()(const time_point<_Source_Clock, _Duration>& _Time) const + noexcept(noexcept(_Source_Clock::to_sys(_Time))) /* strengthened */ + requires _Convertable_to_sys_time<_Source_Clock, _Duration> { + return _Source_Clock::to_sys(_Time); + } + }; + + template + struct clock_time_conversion<_Dest_Clock, system_clock> { + template + _NODISCARD auto operator()(const sys_time<_Duration>& _Time) const + noexcept(noexcept(_Dest_Clock::from_sys(_Time))) /* strengthened */ + requires _Convertable_from_sys_time<_Dest_Clock, _Duration> { + return _Dest_Clock::from_sys(_Time); + } + }; + + // [time.clock.cast.utc] + + template + concept _Convertable_to_utc_time = + same_as<_Clock, utc_clock> || requires(const time_point<_Clock, _Duration>& _Time) { + { _Clock::to_utc(_Time) } + ->_Is_time_point; + }; + + template + concept _Convertable_from_utc_time = same_as<_Clock, utc_clock> || requires(const utc_time<_Duration>& _Time) { + { _Clock::from_utc(_Time) } + ->_Is_time_point<_Clock>; + }; + + template + struct clock_time_conversion { + template + _NODISCARD auto operator()(const time_point<_Source_Clock, _Duration>& _Time) const + noexcept(noexcept(_Source_Clock::to_utc(_Time))) /* strengthened */ + requires _Convertable_to_utc_time<_Source_Clock, _Duration> { + return _Source_Clock::to_utc(_Time); + } + }; + + template + struct clock_time_conversion<_Dest_Clock, utc_clock> { + template + _NODISCARD auto operator()(const utc_time<_Duration>& _Time) const + noexcept(noexcept(_Dest_Clock::from_utc(_Time))) /* strengthened */ + requires _Convertable_from_utc_time<_Dest_Clock, _Duration> { + return _Dest_Clock::from_utc(_Time); + } + }; + + // [time.clock.cast.fn] + + template + _NODISCARD auto clock_cast(const time_point<_Source_Clock, _Duration>& _Time) { + constexpr bool _Has_direct_conversion = + is_invocable_v, decltype(_Time)>; + + constexpr bool _Utc_from_src = _Convertable_to_utc_time<_Source_Clock, _Duration>; + constexpr bool _Sys_from_src = _Convertable_to_sys_time<_Source_Clock, _Duration>; + constexpr bool _Dest_from_utc = _Convertable_from_utc_time<_Dest_Clock, _Duration>; + constexpr bool _Dest_from_sys = _Convertable_from_sys_time<_Dest_Clock, _Duration>; + + constexpr bool _Has_utc_conversion = _Dest_from_utc && _Utc_from_src; + constexpr bool _Has_sys_conversion = _Dest_from_sys && _Sys_from_src; + static_assert(_Has_direct_conversion || !(_Has_utc_conversion && _Has_sys_conversion), + "A two-step clock time conversion is required to be unique, either through utc_clock or system_clock, but " + "not both."); + + constexpr bool _Has_sys_utc_conversion = _Dest_from_sys && _Utc_from_src; + constexpr bool _Has_utc_sys_conversion = _Dest_from_utc && _Sys_from_src; + static_assert(_Has_direct_conversion || _Has_utc_conversion || _Has_sys_conversion + || !(_Has_utc_sys_conversion && _Has_sys_utc_conversion), + "A three-step clock time conversion is required to be unique, either utc-to-system or system-to-utc, but " + "not both."); + + if constexpr (_Has_direct_conversion) { + return clock_time_conversion<_Dest_Clock, _Source_Clock>{}(_Time); + } else if constexpr (_Has_utc_conversion) { + return clock_time_conversion<_Dest_Clock, utc_clock>{}( + clock_time_conversion{}(_Time)); + } else if constexpr (_Has_sys_conversion) { + return clock_time_conversion<_Dest_Clock, system_clock>{}( + clock_time_conversion{}(_Time)); + } else if constexpr (_Has_sys_utc_conversion) { + return clock_time_conversion<_Dest_Clock, system_clock>{}(clock_time_conversion{}( + clock_time_conversion{}(_Time))); + } else if constexpr (_Has_utc_sys_conversion) { + return clock_time_conversion<_Dest_Clock, utc_clock>{}(clock_time_conversion{}( + clock_time_conversion{}(_Time))); + } else { + static_assert(!_Has_direct_conversion && !_Has_utc_conversion && !_Has_sys_conversion + && !_Has_sys_utc_conversion && !_Has_utc_sys_conversion, + "No clock time conversion exists from source clock type to destination clock type."); + } + } #endif // _HAS_CXX20 } // namespace chrono diff --git a/stl/inc/filesystem b/stl/inc/filesystem index c0444a29bd..937aa70a40 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -2104,20 +2104,11 @@ namespace filesystem { __std_fs_stats_flags::_Attributes | __std_fs_stats_flags::_Reparse_tag; // ALIAS file_time_type - struct _File_time_clock { // Implementation of trivial-clock - using rep = long long; - using period = chrono::system_clock::period; - using duration = chrono::duration; - using time_point = chrono::time_point<_File_time_clock>; - - static constexpr bool is_steady = false; - - _NODISCARD static time_point now() noexcept { // get current time; undo epoch adjustment - return time_point(duration(_Xtime_get_ticks() + __std_fs_file_time_epoch_adjustment)); // TRANSITION, ABI - } - }; - - using file_time_type = chrono::time_point<_File_time_clock>; +#if _HAS_CXX20 + using file_time_type = chrono::time_point; +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + using file_time_type = chrono::time_point; +#endif // ^^^ !_HAS_CXX20 // CLASS directory_entry class directory_entry { diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index 66cb299669..38b26fcc61 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -142,6 +142,7 @@ "xtimec.h", "xtr1common", "xtree", + "xtzdb.h", "xutility", "ymath.h", "yvals.h", diff --git a/stl/inc/xfilesystem_abi.h b/stl/inc/xfilesystem_abi.h index 52f8c8f740..052af6cc93 100644 --- a/stl/inc/xfilesystem_abi.h +++ b/stl/inc/xfilesystem_abi.h @@ -19,9 +19,8 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new -inline constexpr size_t __std_fs_max_path = 260; // #define MAX_PATH 260 -inline constexpr size_t __std_fs_temp_path_max = __std_fs_max_path + 1; -inline constexpr long long __std_fs_file_time_epoch_adjustment = 0x19DB1DED53E8000LL; // TRANSITION, ABI +inline constexpr size_t __std_fs_max_path = 260; // #define MAX_PATH 260 +inline constexpr size_t __std_fs_temp_path_max = __std_fs_max_path + 1; enum class __std_win_error : unsigned long { _Success = 0, // #define ERROR_SUCCESS 0L diff --git a/stl/inc/xtzdb.h b/stl/inc/xtzdb.h new file mode 100644 index 0000000000..8ab59b0a85 --- /dev/null +++ b/stl/inc/xtzdb.h @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _XTZDB_H +#define _XTZDB_H +#include +#if _STL_COMPILER_PREPROCESSOR +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +struct _RegistryLeapSecondInfo { + uint16_t _Year; + uint16_t _Month; + uint16_t _Day; + uint16_t _Hour; + uint16_t _Negative; + uint16_t _Reserved; +}; + +_EXTERN_C + +_RegistryLeapSecondInfo* __stdcall __std_tzdb_get_reg_leap_seconds( + size_t _Prev_reg_ls_size, size_t* _Current_reg_ls_size); + +_END_EXTERN_C + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _XTZDB_H diff --git a/stl/inc/xutility b/stl/inc/xutility index 66e6cbd227..8505b58edb 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5446,6 +5446,7 @@ _INLINE_VAR constexpr allocator_arg_t allocator_arg{}; [[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Xout_of_range(_In_z_ const char*); [[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Xoverflow_error(_In_z_ const char*); [[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Xruntime_error(_In_z_ const char*); +[[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _XGetLastError(); // STRUCT TEMPLATE uses_allocator template diff --git a/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets b/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets index 05ebeab886..6146998be6 100644 --- a/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets +++ b/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets @@ -9,6 +9,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception $(CrtRoot)\github\stl\src\atomic_wait.cpp; $(CrtRoot)\github\stl\src\parallel_algorithms.cpp; $(CrtRoot)\github\stl\src\syncstream.cpp; + $(CrtRoot)\github\stl\src\tzdb.cpp; "> nativecpp diff --git a/stl/msbuild/stl_base/stl.files.settings.targets b/stl/msbuild/stl_base/stl.files.settings.targets index b83c74b1e4..53efb17224 100644 --- a/stl/msbuild/stl_base/stl.files.settings.targets +++ b/stl/msbuild/stl_base/stl.files.settings.targets @@ -13,6 +13,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception $(CrtRoot)\github\stl\src\parallel_algorithms.cpp; $(CrtRoot)\github\stl\src\special_math.cpp; $(CrtRoot)\github\stl\src\syncstream.cpp; + $(CrtRoot)\github\stl\src\tzdb.cpp; $(CrtRoot)\github\stl\src\ulocale.cpp; "> nativecpp diff --git a/stl/src/msvcp_atomic_wait.src b/stl/src/msvcp_atomic_wait.src index 240916023e..515ae81e15 100644 --- a/stl/src/msvcp_atomic_wait.src +++ b/stl/src/msvcp_atomic_wait.src @@ -27,4 +27,5 @@ EXPORTS __std_parallel_algorithms_hw_threads __std_release_shared_mutex_for_instance __std_submit_threadpool_work + __std_tzdb_get_reg_leap_seconds __std_wait_for_threadpool_work_callbacks diff --git a/stl/src/tzdb.cpp b/stl/src/tzdb.cpp new file mode 100644 index 0000000000..9ec85fb525 --- /dev/null +++ b/stl/src/tzdb.cpp @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// 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. + +#include + +#include + +#pragma comment(lib, "Advapi32") + +_EXTERN_C + +_RegistryLeapSecondInfo* __stdcall __std_tzdb_get_reg_leap_seconds( + const size_t prev_reg_ls_size, size_t* current_reg_ls_size) { + // On exit--- + // *current_reg_ls_size <= prev_reg_ls_size, *reg_ls_data == nullptr --> no new data + // *current_reg_ls_size > prev_reg_ls_size, *reg_ls_data != nullptr --> new data, successfully read + // *current_reg_ls_size == 0, *reg_ls_data != nullptr --> new data, failed reading + // *current_reg_ls_size > prev_reg_ls_size, *reg_ls_data == nullptr --> new data, failed allocation + + constexpr auto reg_key_name = TEXT("SYSTEM\\CurrentControlSet\\Control\\LeapSecondInformation"); + constexpr auto reg_subkey_name = TEXT("LeapSeconds"); + *current_reg_ls_size = 0; + HKEY leap_sec_key = 0; + + LSTATUS status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_key_name, 0, KEY_READ, &leap_sec_key); + if (status != ERROR_SUCCESS) { + // May not exist on older systems. Treat this as equivalent to the key existing but with no data. + return nullptr; + } + + DWORD byte_size = 0; + status = RegQueryValueEx(leap_sec_key, reg_subkey_name, nullptr, nullptr, nullptr, &byte_size); + static_assert(sizeof(_RegistryLeapSecondInfo) == 12); + const auto ls_size = byte_size / 12; + *current_reg_ls_size = ls_size; + + _RegistryLeapSecondInfo* reg_ls_data = nullptr; + if ((status == ERROR_SUCCESS || status == ERROR_MORE_DATA) && ls_size > prev_reg_ls_size) { + try { + reg_ls_data = new _RegistryLeapSecondInfo[ls_size]; + status = RegQueryValueEx( + leap_sec_key, reg_subkey_name, nullptr, nullptr, reinterpret_cast(reg_ls_data), &byte_size); + if (status != ERROR_SUCCESS) { + *current_reg_ls_size = 0; + } + } catch (...) { + } + } + + RegCloseKey(leap_sec_key); + if (status != ERROR_SUCCESS) { + SetLastError(status); + } + + return reg_ls_data; +} + +_END_EXTERN_C diff --git a/tests/std/test.lst b/tests/std/test.lst index ca60f536b2..ad0eaf9d16 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -235,6 +235,7 @@ tests\P0355R7_calendars_and_time_zones_dates_literals tests\P0355R7_calendars_and_time_zones_hms tests\P0355R7_calendars_and_time_zones_io tests\P0355R7_calendars_and_time_zones_time_point_and_durations +tests\P0355R7_clocks tests\P0356R5_bind_front tests\P0357R3_supporting_incomplete_types_in_reference_wrapper tests\P0408R7_efficient_access_to_stringbuf_buffer diff --git a/tests/std/tests/P0355R7_clocks/env.lst b/tests/std/tests/P0355R7_clocks/env.lst new file mode 100644 index 0000000000..642f530ffa --- /dev/null +++ b/tests/std/tests/P0355R7_clocks/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0355R7_clocks/test.cpp b/tests/std/tests/P0355R7_clocks/test.cpp new file mode 100644 index 0000000000..45c0c94dc6 --- /dev/null +++ b/tests/std/tests/P0355R7_clocks/test.cpp @@ -0,0 +1,320 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +using namespace std; +using namespace std::chrono; +using namespace std::chrono_literals; + +void test_is_leap_second(const year_month_day& ymd) { + const auto ls = sys_days{ymd}; + const auto& leap_seconds = get_tzdb().leap_seconds; + assert(find(leap_seconds.begin(), leap_seconds.end(), ls + days{1}) != leap_seconds.end()); + assert(get_leap_second_info(utc_clock::from_sys(ls) + days{1}).is_leap_second); +} + +void test_leap_second() { + constexpr int jun_leap_second_years[] = {1972, 1981, 1982, 1983, 1985, 1992, 1993, 1994, 1997, 2012, 2015}; + constexpr int dec_leap_second_years[] = { + 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1987, 1989, 1990, 1995, 1998, 2005, 2008, 2016}; + static_assert(size(jun_leap_second_years) + size(dec_leap_second_years) == 27); + + for (const auto& ls_year : jun_leap_second_years) { + test_is_leap_second(30d / June / year{ls_year}); + } + for (const auto& ls_year : dec_leap_second_years) { + test_is_leap_second(31d / December / year{ls_year}); + } + + constexpr leap_second leap{sys_seconds{42s}, true}; + constexpr sys_seconds smaller{41s}; + constexpr sys_seconds equal{42s}; + constexpr sys_seconds larger{43s}; + + assert(equal == leap); + assert(leap == equal); + static_assert(noexcept(equal == leap)); + static_assert(noexcept(leap == equal)); + + assert(leap < larger); + assert(smaller < leap); + static_assert(noexcept(leap < larger)); + static_assert(noexcept(smaller < leap)); + + assert(larger > leap); + assert(leap > smaller); + static_assert(noexcept(larger > leap)); + static_assert(noexcept(leap > smaller)); + + assert(equal <= leap); + assert(smaller <= leap); + assert(leap <= equal); + assert(leap <= larger); + static_assert(noexcept(equal <= leap)); + static_assert(noexcept(leap <= equal)); + + assert(equal >= leap); + assert(larger >= leap); + assert(leap >= equal); + assert(leap >= smaller); + static_assert(noexcept(equal >= leap)); + static_assert(noexcept(leap >= equal)); + + static_assert(is_eq(leap <=> equal)); + static_assert(is_lt(leap <=> larger)); + static_assert(is_gt(leap <=> smaller)); + static_assert(is_lteq(leap <=> larger)); + static_assert(is_gteq(leap <=> smaller)); + static_assert(is_lteq(leap <=> equal)); + static_assert(is_gteq(leap <=> equal)); + static_assert(noexcept(leap <=> equal)); + + static_assert(noexcept(leap.date())); + static_assert(noexcept(leap.value())); + static_assert(leap_second{sys_seconds{42s}, true}.date() == sys_seconds{42s}); + static_assert(leap_second{sys_seconds{42s}, true}.value() == 1s); + static_assert(leap_second{sys_seconds{42s}, false}.value() == -1s); +} + +constexpr bool operator==(const leap_second_info& lhs, const leap_second_info& rhs) { + return lhs.is_leap_second == rhs.is_leap_second && lhs.elapsed == rhs.elapsed; +} + +void test_leap_second_info(const leap_second& leap, seconds accum) { + const bool is_positive = (leap.value() == 1s); + // First UTC time when leap is counted, before insertion of a positive leap, after insertion of a negative one. + const utc_seconds utc_leap{leap.date().time_since_epoch() + accum + (is_positive ? 0s : -1s)}; + + auto lsi = get_leap_second_info(utc_leap - 1s); + assert(lsi == (leap_second_info{false, accum})); + + lsi = get_leap_second_info(utc_leap - 500ms); + assert(lsi == (leap_second_info{false, accum})); + + accum += leap.value(); + lsi = get_leap_second_info(utc_leap); + assert(lsi == (leap_second_info{is_positive, accum})); + + lsi = get_leap_second_info(utc_leap + 500ms); + assert(lsi == (leap_second_info{is_positive, accum})); + + lsi = get_leap_second_info(utc_leap + 1s); + assert(lsi == (leap_second_info{false, accum})); +} + +template +void test_utc_clock_to_sys(const leap_second& leap) { + auto u = utc_clock::from_sys(leap.date() - Duration{1}); // just before leap second + assert(utc_clock::from_sys(utc_clock::to_sys(u)) == u); + if (leap.value() == 1s) { + u += Duration{1}; + assert(utc_clock::to_sys(u) == leap.date() - Duration{1}); // during + } else { + assert(utc_clock::from_sys(utc_clock::to_sys(u)) == u); + } + + u += 1s; + assert(utc_clock::from_sys(utc_clock::to_sys(u)) == u); // just after +} + +template +void test_file_clock_from_utc(const leap_second& leap) { + const auto file_leap = clock_cast(leap.date()); + + auto u = utc_clock::from_sys(leap.date() - Duration{1}); // just before leap second + assert(file_clock::to_utc(file_clock::from_utc(u)) == u); + + if (leap.value() == 1s && leap.date() <= sys_days{1d / January / 2017y}) { + u += Duration{1}; + assert(file_clock::from_utc(u) == file_leap - Duration{1}); // during + } else { + assert(file_clock::to_utc(file_clock::from_utc(u)) == u); + } + + u += 1s; + assert(file_clock::to_utc(file_clock::from_utc(u)) == u); // just after +} + +void test_clock_cast() { + sys_days st(2020y / January / 1); + const auto ut = utc_clock::from_sys(st); + const auto tt = tai_clock::from_utc(ut); + const auto gt = gps_clock::from_utc(ut); + const auto ft = file_clock::from_utc(ut); + + assert(clock_cast(ut) == ut); + assert(clock_cast(st) == ut); + assert(clock_cast(tt) == ut); + assert(clock_cast(gt) == ut); + assert(clock_cast(ft) == ut); + + assert(clock_cast(ut) == st); + assert(clock_cast(st) == st); + assert(clock_cast(tt) == st); + assert(clock_cast(gt) == st); + assert(clock_cast(ft) == st); + + assert(clock_cast(ut) == tt); + assert(clock_cast(st) == tt); + assert(clock_cast(tt) == tt); + assert(clock_cast(gt) == tt); + assert(clock_cast(ft) == tt); + + assert(clock_cast(ut) == gt); + assert(clock_cast(st) == gt); + assert(clock_cast(tt) == gt); + assert(clock_cast(gt) == gt); + assert(clock_cast(ft) == gt); + + assert(clock_cast(ut) == ft); + assert(clock_cast(st) == ft); + assert(clock_cast(tt) == ft); + assert(clock_cast(gt) == ft); + assert(clock_cast(ft) == ft); + + // [time.clock.utc.overview]/1 Example 1 + assert(clock_cast(sys_seconds{sys_days{1970y / January / 1}}).time_since_epoch() == 0s); + assert(clock_cast(sys_seconds{sys_days{2000y / January / 1}}).time_since_epoch() == 946'684'822s); +} + +static_assert(is_clock_v); +static_assert(is_clock_v); +static_assert(is_clock_v); +static_assert(is_clock_v); + +void test_utc_clock_from_sys(const leap_second& leap, seconds offset) { + // Generalized from [time.clock.utc.members]/3 Example 1. + auto t = leap.date() - 2ns; + auto u = utc_clock::from_sys(t); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); + + t += 1ns; + u = utc_clock::from_sys(t); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); + + t += 1ns; + u = utc_clock::from_sys(t); + offset += leap.value(); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); + + t += 1ns; + u = utc_clock::from_sys(t); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); +} + +void test_file_clock_utc() { + const auto file_epoch{utc_clock::from_sys(sys_days{January / 1 / 1601})}; + assert(file_clock::to_utc(file_time{0s}) == file_epoch); + assert(file_clock::from_utc(file_epoch) == file_time{0s}); +} + +void test_file_clock_to_utc(const leap_second& leap, seconds offset) { + // FILETIME counts leap seconds after 1 January 2017, so offset is constant thereafter. + constexpr sys_days file_time_cutoff{1d / January / 2017y}; + auto t = clock_cast(leap.date()) - 2us; + if (leap > file_time_cutoff) { + offset = 27s; + } + offset -= duration_cast(file_clock::duration{__std_fs_file_time_epoch_adjustment}); + + auto u = file_clock::to_utc(t); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); + + t += 1us; + u = file_clock::to_utc(t); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); + + t += 1us; + u = file_clock::to_utc(t); + if (leap.date() <= file_time_cutoff) + offset += leap.value(); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); + + t += 1us; + u = file_clock::to_utc(t); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); +} + +void test_gps_tai_clocks_utc() { + const auto tai_epoch{utc_clock::from_sys(sys_days{January / 1 / 1958} - seconds{10})}; + const auto gps_epoch{utc_clock::from_sys(sys_days{January / Sunday[1] / 1980})}; + + assert(tai_clock::to_utc(tai_seconds{0s}) == tai_epoch); + assert(tai_clock::from_utc(tai_epoch) == tai_seconds{0s}); + + assert(gps_clock::to_utc(gps_seconds{0s}) == gps_epoch); + assert(gps_clock::from_utc(gps_epoch) == gps_seconds{0s}); +} + +int main() { + test_leap_second(); + + seconds offset{0}; + for (const auto& leap : get_tzdb().leap_seconds) { + test_leap_second_info(leap, offset); + test_utc_clock_to_sys(leap); + test_utc_clock_to_sys(leap); + test_file_clock_from_utc(leap); + test_file_clock_from_utc(leap); + test_utc_clock_from_sys(leap, offset); + test_file_clock_to_utc(leap, offset); + offset += leap.value(); + } + test_gps_tai_clocks_utc(); + test_file_clock_utc(); + test_clock_cast(); + + // a negative leap second when the accumulated offset is positive + { + auto my_tzdb = get_tzdb_list().front(); + auto& leap_vec = my_tzdb.leap_seconds; + leap_vec.erase(leap_vec.begin() + 27, leap_vec.end()); + leap_vec.emplace_back(sys_days{1d / January / 2020y}, false); + leap_vec.emplace_back(sys_days{1d / January / 2021y}, true); + my_tzdb._All_ls_positive = false; + get_tzdb_list()._Emplace_front(move(my_tzdb)); + } + + offset = 0s; + for (const auto& leap : get_tzdb().leap_seconds) { + test_leap_second_info(leap, offset); + test_utc_clock_to_sys(leap); + test_utc_clock_to_sys(leap); + test_file_clock_from_utc(leap); + test_file_clock_from_utc(leap); + test_utc_clock_from_sys(leap, offset); + test_file_clock_to_utc(leap, offset); + offset += leap.value(); + } + + // positive and negative leap seconds when the accumulated offset is negative + { + auto my_tzdb = get_tzdb_list().front(); + auto& leap_vec = my_tzdb.leap_seconds; + leap_vec.erase(leap_vec.begin() + 27, leap_vec.end()); + for (int i = 0; i < 30; ++i) { + leap_vec.emplace_back(sys_days{1d / January / year{i + 2020}}, false); + } + leap_vec.emplace_back(sys_days{1d / January / 2060y}, true); + get_tzdb_list()._Emplace_front(move(my_tzdb)); + } + + offset = 0s; + for (const auto& leap : get_tzdb().leap_seconds) { + test_leap_second_info(leap, offset); + test_utc_clock_to_sys(leap); + test_utc_clock_to_sys(leap); + test_file_clock_from_utc(leap); + test_file_clock_from_utc(leap); + test_utc_clock_from_sys(leap, offset); + test_file_clock_to_utc(leap, offset); + offset += leap.value(); + } + + return 0; +} From 2baaffbac625eb08010aeb4efabdfe322fb063a5 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Sat, 20 Feb 2021 22:36:54 -0800 Subject: [PATCH 02/23] concepts workarounds for /BE --- stl/inc/chrono | 21 +++++++++++++++------ tests/std/tests/P0355R7_clocks/test.cpp | 13 ++++++++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 3a616cbd2b..e9fddc564e 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2159,6 +2159,7 @@ namespace chrono { return !(_Left < _Right.date()); } +#ifdef __cpp_lib_concepts template requires three_way_comparable_with> constexpr auto operator<=>( const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { @@ -2167,6 +2168,7 @@ namespace chrono { constexpr inline strong_ordering operator<=>(const leap_second& _Left, const leap_second& _Right) noexcept { return _Left.date() <=> _Right.date(); } +#endif // __cpp_lib_concepts inline pair, bool> _Xtzdb_generate_leap_seconds(const size_t _Current_size) { // Returns empty vector if no new leap seconds are found. @@ -2405,7 +2407,13 @@ namespace chrono { // Convert to the last leap second before or equal to _Time (except for the special case described above). const auto _Last_leap = &*(--_It); const auto _Utc_leap_second = --utc_seconds{_Last_leap->date().time_since_epoch() + _Offset}; - const auto _Leap_cmp = _Utc_leap_second <=> _Time_floor; +#ifdef __cpp_lib_concepts + const auto _Leap_cmp = _Utc_leap_second <=> _Time_floor; +#else // ^^^ __cpp_lib_concepts / TRANSITION, GH-395 workaround vvv + const auto _Leap_cmp = _Utc_leap_second > _Time_floor ? strong_ordering::greater + : _Utc_leap_second == _Time_floor ? strong_ordering::equal + : strong_ordering::less; +#endif // ^^^ workaround if (_Tzdb._All_ls_positive && _STD is_gt(_Leap_cmp)) { --_Offset; } @@ -2642,21 +2650,22 @@ namespace chrono { // [time.clock.cast.sys] + // TRANSITION, GH-395 workaround, is_same_v -> same_as template concept _Is_time_point = requires { typename _Ty::duration; - requires same_as, _Ty>; + requires is_same_v, _Ty>; }; template concept _Convertable_to_sys_time = - same_as<_Clock, system_clock> || requires(const time_point<_Clock, _Duration>& _Time) { + is_same_v<_Clock, system_clock> || requires(const time_point<_Clock, _Duration>& _Time) { { _Clock::to_sys(_Time) } ->_Is_time_point; }; template - concept _Convertable_from_sys_time = same_as<_Clock, system_clock> || requires(const sys_time<_Duration>& _Time) { + concept _Convertable_from_sys_time = is_same_v<_Clock, system_clock> || requires(const sys_time<_Duration>& _Time) { { _Clock::from_sys(_Time) } ->_Is_time_point<_Clock>; }; @@ -2685,13 +2694,13 @@ namespace chrono { template concept _Convertable_to_utc_time = - same_as<_Clock, utc_clock> || requires(const time_point<_Clock, _Duration>& _Time) { + is_same_v<_Clock, utc_clock> || requires(const time_point<_Clock, _Duration>& _Time) { { _Clock::to_utc(_Time) } ->_Is_time_point; }; template - concept _Convertable_from_utc_time = same_as<_Clock, utc_clock> || requires(const utc_time<_Duration>& _Time) { + concept _Convertable_from_utc_time = is_same_v<_Clock, utc_clock> || requires(const utc_time<_Duration>& _Time) { { _Clock::from_utc(_Time) } ->_Is_time_point<_Clock>; }; diff --git a/tests/std/tests/P0355R7_clocks/test.cpp b/tests/std/tests/P0355R7_clocks/test.cpp index 45c0c94dc6..6dfb794a26 100644 --- a/tests/std/tests/P0355R7_clocks/test.cpp +++ b/tests/std/tests/P0355R7_clocks/test.cpp @@ -65,6 +65,7 @@ void test_leap_second() { static_assert(noexcept(equal >= leap)); static_assert(noexcept(leap >= equal)); +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 static_assert(is_eq(leap <=> equal)); static_assert(is_lt(leap <=> larger)); static_assert(is_gt(leap <=> smaller)); @@ -74,6 +75,16 @@ void test_leap_second() { static_assert(is_gteq(leap <=> equal)); static_assert(noexcept(leap <=> equal)); + static_assert(is_eq(leap <=> leap_second{equal, true})); + static_assert(is_lt(leap <=> leap_second{larger, true})); + static_assert(is_gt(leap <=> leap_second{smaller, true})); + static_assert(is_lteq(leap <=> leap_second{larger, true})); + static_assert(is_gteq(leap <=> leap_second{smaller, true})); + static_assert(is_lteq(leap <=> leap_second{equal, true})); + static_assert(is_gteq(leap <=> leap_second{equal, true})); + static_assert(noexcept(leap <=> leap_second{equal, true})); +#endif // __cpp_lib_concepts + static_assert(noexcept(leap.date())); static_assert(noexcept(leap.value())); static_assert(leap_second{sys_seconds{42s}, true}.date() == sys_seconds{42s}); @@ -90,7 +101,7 @@ void test_leap_second_info(const leap_second& leap, seconds accum) { // First UTC time when leap is counted, before insertion of a positive leap, after insertion of a negative one. const utc_seconds utc_leap{leap.date().time_since_epoch() + accum + (is_positive ? 0s : -1s)}; - auto lsi = get_leap_second_info(utc_leap - 1s); + auto lsi = get_leap_second_info(utc_leap - 1s); assert(lsi == (leap_second_info{false, accum})); lsi = get_leap_second_info(utc_leap - 500ms); From 3318a36caa64effa3a42d42442945f31e98007b0 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Wed, 24 Feb 2021 00:24:02 -0800 Subject: [PATCH 03/23] test failures and review feedback --- stl/inc/chrono | 351 +++++++++++++----------- stl/inc/xtzdb.h | 37 +++ stl/src/msvcp_atomic_wait.src | 3 + stl/src/syncstream.cpp | 31 +-- stl/src/tzdb.cpp | 18 +- tests/std/tests/P0355R7_clocks/test.cpp | 38 +-- 6 files changed, 267 insertions(+), 211 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index e9fddc564e..5b088a979f 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -17,9 +17,12 @@ #if _HAS_CXX20 #include +#include #include #include +#include #include +#include #include #endif // _HAS_CXX20 @@ -415,8 +418,8 @@ namespace chrono { #ifdef __cpp_lib_concepts // clang-format off template - requires three_way_comparable, duration<_Rep2, _Period2>>::rep> - _NODISCARD constexpr auto + requires three_way_comparable, duration<_Rep2, _Period2>>::rep> + _NODISCARD constexpr auto operator<=>(const duration<_Rep1, _Period1>& _Left, const duration<_Rep2, _Period2>& _Right) noexcept( is_arithmetic_v<_Rep1>&& is_arithmetic_v<_Rep2>) /* strengthened */ { // clang-format on @@ -2095,8 +2098,11 @@ namespace chrono { leap_second(const leap_second&) = default; leap_second& operator=(const leap_second&) = default; - constexpr leap_second(const sys_seconds& _Date_, bool _Is_positive_) noexcept - : _Date{_Date_}, _Is_positive{_Is_positive_} {} + constexpr leap_second( + const sys_seconds& _Date_, const bool _Is_positive_, const seconds& _Prev_elapsed) noexcept + : _Date{_Date_}, _Is_positive{_Is_positive_} { + _Elapsed_offset = _Prev_elapsed + value(); + } _NODISCARD constexpr sys_seconds date() const noexcept { return _Date; @@ -2110,9 +2116,14 @@ namespace chrono { return _Is_positive; } + _NODISCARD constexpr seconds _Elapsed() const noexcept { + return _Elapsed_offset; + } + private: sys_seconds _Date; bool _Is_positive; + seconds _Elapsed_offset; }; _NODISCARD inline bool operator==(const leap_second& _Left, const leap_second& _Right) noexcept { @@ -2161,51 +2172,62 @@ namespace chrono { #ifdef __cpp_lib_concepts template - requires three_way_comparable_with> constexpr auto operator<=>( + requires three_way_comparable_with> _NODISCARD constexpr auto operator<=>( const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { return _Left.date() <=> _Right; } - constexpr inline strong_ordering operator<=>(const leap_second& _Left, const leap_second& _Right) noexcept { + _NODISCARD constexpr strong_ordering operator<=>(const leap_second& _Left, const leap_second& _Right) noexcept { return _Left.date() <=> _Right.date(); } #endif // __cpp_lib_concepts - inline pair, bool> _Xtzdb_generate_leap_seconds(const size_t _Current_size) { + // [time.zone.db] + + // TRANSITION: work in progress + struct tzdb { + vector> leap_seconds; + bool _All_ls_positive; + }; + + _NODISCARD inline pair _Xtzdb_generate_leap_seconds( + const size_t _Current_size) { // Returns empty vector if no new leap seconds are found. - constexpr static leap_second _Pre_2018_leap_seconds[]{ - {sys_seconds{seconds{78796800}}, true}, - {sys_seconds{seconds{94694400}}, true}, - {sys_seconds{seconds{126230400}}, true}, - {sys_seconds{seconds{157766400}}, true}, - {sys_seconds{seconds{189302400}}, true}, - {sys_seconds{seconds{220924800}}, true}, - {sys_seconds{seconds{252460800}}, true}, - {sys_seconds{seconds{283996800}}, true}, - {sys_seconds{seconds{315532800}}, true}, - {sys_seconds{seconds{362793600}}, true}, - {sys_seconds{seconds{394329600}}, true}, - {sys_seconds{seconds{425865600}}, true}, - {sys_seconds{seconds{489024000}}, true}, - {sys_seconds{seconds{567993600}}, true}, - {sys_seconds{seconds{631152000}}, true}, - {sys_seconds{seconds{662688000}}, true}, - {sys_seconds{seconds{709948800}}, true}, - {sys_seconds{seconds{741484800}}, true}, - {sys_seconds{seconds{773020800}}, true}, - {sys_seconds{seconds{820454400}}, true}, - {sys_seconds{seconds{867715200}}, true}, - {sys_seconds{seconds{915148800}}, true}, - {sys_seconds{seconds{1136073600}}, true}, - {sys_seconds{seconds{1230768000}}, true}, - {sys_seconds{seconds{1341100800}}, true}, - {sys_seconds{seconds{1435708800}}, true}, - {sys_seconds{seconds{1483228800}}, true}, + static constexpr leap_second _Known_leap_seconds[]{ + {sys_seconds{seconds{78796800}}, true, seconds{0}}, + {sys_seconds{seconds{94694400}}, true, seconds{1}}, + {sys_seconds{seconds{126230400}}, true, seconds{2}}, + {sys_seconds{seconds{157766400}}, true, seconds{3}}, + {sys_seconds{seconds{189302400}}, true, seconds{4}}, + {sys_seconds{seconds{220924800}}, true, seconds{5}}, + {sys_seconds{seconds{252460800}}, true, seconds{6}}, + {sys_seconds{seconds{283996800}}, true, seconds{7}}, + {sys_seconds{seconds{315532800}}, true, seconds{8}}, + {sys_seconds{seconds{362793600}}, true, seconds{9}}, + {sys_seconds{seconds{394329600}}, true, seconds{10}}, + {sys_seconds{seconds{425865600}}, true, seconds{11}}, + {sys_seconds{seconds{489024000}}, true, seconds{12}}, + {sys_seconds{seconds{567993600}}, true, seconds{13}}, + {sys_seconds{seconds{631152000}}, true, seconds{14}}, + {sys_seconds{seconds{662688000}}, true, seconds{15}}, + {sys_seconds{seconds{709948800}}, true, seconds{16}}, + {sys_seconds{seconds{741484800}}, true, seconds{17}}, + {sys_seconds{seconds{773020800}}, true, seconds{18}}, + {sys_seconds{seconds{820454400}}, true, seconds{19}}, + {sys_seconds{seconds{867715200}}, true, seconds{20}}, + {sys_seconds{seconds{915148800}}, true, seconds{21}}, + {sys_seconds{seconds{1136073600}}, true, seconds{22}}, + {sys_seconds{seconds{1230768000}}, true, seconds{23}}, + {sys_seconds{seconds{1341100800}}, true, seconds{24}}, + {sys_seconds{seconds{1435708800}}, true, seconds{25}}, + {sys_seconds{seconds{1483228800}}, true, seconds{26}}, }; - constexpr size_t _Pre_2018_count = _STD size(_Pre_2018_leap_seconds); + constexpr size_t _Pre_2018_count = 27; size_t _New_reg_ls_size; - auto _Reg_ls_data = unique_ptr<_RegistryLeapSecondInfo[]>{__std_tzdb_get_reg_leap_seconds( - (_STD max)(_Current_size, _Pre_2018_count) - _Pre_2018_count, &_New_reg_ls_size)}; + unique_ptr<_RegistryLeapSecondInfo[], decltype(&__std_decalloc_reg_leap_seconds)> _Reg_ls_data( + __std_tzdb_get_reg_leap_seconds( + (_STD max)(_Current_size, _STD size(_Known_leap_seconds)) - _Pre_2018_count, &_New_reg_ls_size), + &__std_decalloc_reg_leap_seconds); const size_t _New_size = _Pre_2018_count + _New_reg_ls_size; if (_New_reg_ls_size > _Current_size && !_Reg_ls_data) { @@ -2214,13 +2236,13 @@ namespace chrono { _XGetLastError(); } - vector _Leap_sec_info; + decltype(tzdb::leap_seconds) _Leap_sec_info; bool _All_ls_positive = true; if (_New_size > _Current_size) { _Leap_sec_info.reserve(_New_size); _Leap_sec_info.insert( - _Leap_sec_info.begin(), _STD cbegin(_Pre_2018_leap_seconds), _STD cend(_Pre_2018_leap_seconds)); + _Leap_sec_info.begin(), _STD cbegin(_Known_leap_seconds), _STD cend(_Known_leap_seconds)); for (size_t _Idx = 0; _Idx < _New_reg_ls_size; ++_Idx) { // Leap second happens at ls._Hour:59:59. We store the next second after, so add a whole hour. @@ -2228,7 +2250,7 @@ namespace chrono { const auto _Date = static_cast(year_month_day{year{_Ls._Year}, month{_Ls._Month}, day{_Ls._Day}}) + hours{_Ls._Hour + 1}; - _Leap_sec_info.emplace_back(_Date, !_Ls._Negative); + _Leap_sec_info.emplace_back(_Date, !_Ls._Negative, _Leap_sec_info.back()._Elapsed()); _All_ls_positive = _All_ls_positive && !_Ls._Negative; } } @@ -2236,27 +2258,23 @@ namespace chrono { return {_Leap_sec_info, _All_ls_positive}; } - // [time.zone.db] - - // TRANSITION: work in progress - struct tzdb { - vector leap_seconds; - bool _All_ls_positive; - }; - // TRANSITION: work in progress class tzdb_list { private: - using _ListType = forward_list; + using _ListType = forward_list>; public: using const_iterator = _ListType::const_iterator; tzdb_list(const tzdb_list&) = delete; tzdb_list& operator=(const tzdb_list&) = delete; - tzdb_list() = default; - const tzdb& front() const noexcept { + tzdb_list() { + auto [_Leap_sec, _All_ls_positive] = _Xtzdb_generate_leap_seconds(0); + _Tzdb_list.emplace_front(tzdb{_STD move(_Leap_sec), _All_ls_positive}); + } + + _NODISCARD const tzdb& front() const noexcept { _Shared_lock _Lk(_Tzdb_mutex); return _Tzdb_list.front(); } @@ -2273,19 +2291,7 @@ namespace chrono { if (!_Leap_sec.empty()) { _Emplace_front(tzdb{_STD move(_Leap_sec), _All_ls_positive}); } - return front(); - } - - void _Initialize() { - _Shared_lock _Shr_lk(_Tzdb_mutex); - if (_Tzdb_list.empty()) { - _Shr_lk._Unlock(); - _Unique_lock _Lk(_Tzdb_mutex); - if (_Tzdb_list.empty()) { - auto _Leap_sec = _Xtzdb_generate_leap_seconds(0); - _Tzdb_list.emplace_front(tzdb{_STD move(_Leap_sec.first), _Leap_sec.second}); - } - } + return _Tzdb_list.front(); } private: @@ -2297,21 +2303,24 @@ namespace chrono { _Smtx_lock_shared(_Mtx); } + _Shared_lock(const _Shared_lock&) = delete; + _Shared_lock& operator=(const _Shared_lock&) = delete; + void _Unlock() { - if (owns) { + if (_Owns) { _Smtx_unlock_shared(_Mtx); } - owns = false; + _Owns = false; } ~_Shared_lock() { - if (owns) { + if (_Owns) { _Smtx_unlock_shared(_Mtx); } } _Smtx_t* _Mtx; - bool owns = true; + bool _Owns = true; }; struct _NODISCARD _Unique_lock { @@ -2319,6 +2328,9 @@ namespace chrono { _Smtx_lock_exclusive(_Mtx); } + _Unique_lock(const _Unique_lock&) = delete; + _Unique_lock& operator=(const _Unique_lock&) = delete; + ~_Unique_lock() { _Smtx_unlock_exclusive(_Mtx); } @@ -2327,14 +2339,25 @@ namespace chrono { }; }; - _INLINE_VAR tzdb_list _Global_tzdb_list; + inline atomic _Global_tzdb_list; - inline tzdb_list& get_tzdb_list() { - _Global_tzdb_list._Initialize(); - return _Global_tzdb_list; + _NODISCARD inline tzdb_list& get_tzdb_list() { + auto* _Tzdb_ptr = _Global_tzdb_list.load(); + if (_Tzdb_ptr == nullptr) { + auto _My_tzdb = static_cast(__std_calloc_crt(1, sizeof(tzdb_list))); + _STD construct_at(_My_tzdb); + if (_Global_tzdb_list.compare_exchange_strong(_Tzdb_ptr, _My_tzdb)) { + _Tzdb_ptr = _My_tzdb; + } else { + _STD destroy_at(_My_tzdb); + __std_free_crt(_My_tzdb); + } + } + + return *_Tzdb_ptr; } - inline const tzdb& get_tzdb() { + _NODISCARD inline const tzdb& get_tzdb() { return _CHRONO get_tzdb_list().front(); } @@ -2360,53 +2383,50 @@ namespace chrono { const auto& _Tzdb = _CHRONO get_tzdb(); const auto& _Ls_vector = _Tzdb.leap_seconds; - // Find first leap second after _Time and the accumulated offset of all *prior* leap seconds. - seconds _Offset; + // Find first leap second after _Time. vector::const_iterator _It; if (_Tzdb._All_ls_positive) { - // _It here is actually the *2nd* leap second after _Time for the _Offset-1 seconds before the next leap + // _It here is actually the *2nd* leap second after _Time for the _Elapsed()-1 seconds before the next leap // second insertion. This doesn't overlap with the actual leap second insertion period, so prev(_It) can - // always be used to detect if _Time is during an insertion. _Offset, however, needs to be corrected later. - // For example, if leap seconds are {100, 200, 300, 400}, we have + // always be used to detect if _Time is during an insertion. But an additional decrement is needed to get + // the correct eleapsed offset. For example, if leap seconds are {100, 200, 300, 400}, we have // - // UTC sys *_It _Offset - // 99 99 100 0 - // 100 X 200 1 - // 101 100 200 1 - // 102 101 200 1 + // UTC sys *_It + // 99 99 100 + // 100 X 200 + // 101 100 200 + // 102 101 200 // ... - // 199 198 200 1 - // 200 199 300 2^ - // 201 X 300 2 - // 202 200 300 2 + // 199 198 200 + // 200 199 300^ + // 201 X 300 + // 202 200 300 // ... - // 299 297 300 2 - // 300 298 400 3^ - // 301 299 400 3^ - // 302 X 400 3 - // 303 300 400 3 + // 299 297 300 + // 300 298 400^ + // 301 299 400^ + // 302 X 400 + // 303 300 400 // - // ^_It points to 2nd leap second and _Offset value is wrong. + // ^_It points to 2nd leap second _It = _STD upper_bound(_Ls_vector.begin(), _Ls_vector.end(), sys_seconds{_Time_floor.time_since_epoch()}); - _Offset = seconds{_It - _Ls_vector.begin()}; } else { - _Offset = seconds{0}; + seconds _Prev_elapsed{0}; for (_It = _Ls_vector.begin(); _It != _Ls_vector.end(); ++_It) { - const auto _New_offset = _Offset + _It->value(); // Positive leap seconds are counted one second earlier, when insertion begins. - if (utc_seconds{_It->date().time_since_epoch()} + (_It->_Positive() ? _Offset : _New_offset) + if (utc_seconds{_It->date().time_since_epoch()} + (_It->_Positive() ? _Prev_elapsed : _It->_Elapsed()) > _Time_floor) { break; } - _Offset = _New_offset; + _Prev_elapsed = _It->_Elapsed(); } } if (_It != _Ls_vector.begin()) { - // Convert to the last leap second before or equal to _Time (except for the special case described above). - const auto _Last_leap = &*(--_It); - const auto _Utc_leap_second = --utc_seconds{_Last_leap->date().time_since_epoch() + _Offset}; + // Convert to the last leap second before or equal to _Time. + const auto& _Last_leap = *--_It; + const utc_seconds _Utc_leap_second{_Last_leap.date().time_since_epoch() + _It->_Elapsed() - seconds{1}}; #ifdef __cpp_lib_concepts const auto _Leap_cmp = _Utc_leap_second <=> _Time_floor; #else // ^^^ __cpp_lib_concepts / TRANSITION, GH-395 workaround vvv @@ -2415,11 +2435,11 @@ namespace chrono { : strong_ordering::less; #endif // ^^^ workaround if (_Tzdb._All_ls_positive && _STD is_gt(_Leap_cmp)) { - --_Offset; + --_It; } - return {_Last_leap->_Positive() && _STD is_eq(_Leap_cmp), _Offset}; + return {_Last_leap._Positive() && _STD is_eq(_Leap_cmp), _It->_Elapsed()}; } else { - return {false, _Offset}; + return {false, seconds{0}}; } } @@ -2535,17 +2555,24 @@ namespace chrono { return gps_time>{_Time.time_since_epoch()} + _Gps_epoch_adjust; } }; +} // namespace chrono - // [time.clock.file] +// [time.clock.file] +namespace filesystem { struct _File_time_clock; - using file_clock = _File_time_clock; +} // namespace filesystem + +namespace chrono { + using file_clock = filesystem::_File_time_clock; template using file_time = time_point; +} // namespace chrono #endif // ^^^ _HAS_CXX20 #if _HAS_CXX17 +namespace filesystem { inline constexpr long long __std_fs_file_time_epoch_adjustment = 0x19DB1DED53E8000LL; // TRANSITION, ABI struct _File_time_clock { // Implementation of trivial-clock @@ -2560,13 +2587,16 @@ namespace chrono { } #if _HAS_CXX20 - // Assumes that FILETIME counts leaps seconds only after the first 27 (i.e., after 1 January 2017), even though + // Assumes that FILETIME counts leap seconds only after the first 27 (i.e., after 1 January 2017), even though // systems can opt out of this behavior. - static constexpr seconds _Skipped_filetime_leap_seconds{27}; - static constexpr sys_days _Cutoff{year_month_day{year{2017}, January, day{1}}}; + static constexpr chrono::seconds _Skipped_filetime_leap_seconds{27}; + static constexpr chrono::sys_days _Cutoff{ + chrono::year_month_day{chrono::year{2017}, chrono::January, chrono::day{1}}}; template - _NODISCARD static utc_time> to_utc(const file_time<_Duration>& _File_time) { + _NODISCARD static chrono::utc_time> to_utc( + const chrono::file_time<_Duration>& _File_time) { + using namespace chrono; using _CT = common_type_t<_Duration, seconds>; const auto _Ticks = _File_time.time_since_epoch() - duration_cast(duration{__std_fs_file_time_epoch_adjustment}); @@ -2579,7 +2609,9 @@ namespace chrono { } template - _NODISCARD static file_time> from_utc(const utc_time<_Duration>& _Utc_time) { + _NODISCARD static chrono::file_time> from_utc( + const chrono::utc_time<_Duration>& _Utc_time) { + using namespace chrono; using _CT = common_type_t<_Duration, seconds>; file_time<_CT> _File_time{duration_cast(duration{__std_fs_file_time_epoch_adjustment})}; @@ -2593,12 +2625,14 @@ namespace chrono { } #endif // _HAS_CXX20 }; +} // namespace filesystem #endif // _HAS_CXX17 +namespace chrono { #if _HAS_CXX20 // [time.clock.conv] - template + template struct clock_time_conversion; // [time.clock.cast.id] @@ -2658,115 +2692,114 @@ namespace chrono { }; template - concept _Convertable_to_sys_time = + concept _Convertible_to_sys_time = is_same_v<_Clock, system_clock> || requires(const time_point<_Clock, _Duration>& _Time) { { _Clock::to_sys(_Time) } ->_Is_time_point; }; template - concept _Convertable_from_sys_time = is_same_v<_Clock, system_clock> || requires(const sys_time<_Duration>& _Time) { + concept _Convertible_from_sys_time = is_same_v<_Clock, system_clock> || requires(const sys_time<_Duration>& _Time) { { _Clock::from_sys(_Time) } ->_Is_time_point<_Clock>; }; - template - struct clock_time_conversion { + template + struct clock_time_conversion { template - _NODISCARD auto operator()(const time_point<_Source_Clock, _Duration>& _Time) const - noexcept(noexcept(_Source_Clock::to_sys(_Time))) /* strengthened */ - requires _Convertable_to_sys_time<_Source_Clock, _Duration> { - return _Source_Clock::to_sys(_Time); + _NODISCARD auto operator()(const time_point<_SourceClock, _Duration>& _Time) const + noexcept(noexcept(_SourceClock::to_sys(_Time))) /* strengthened */ + requires _Convertible_to_sys_time<_SourceClock, _Duration> { + return _SourceClock::to_sys(_Time); } }; - template - struct clock_time_conversion<_Dest_Clock, system_clock> { + template + struct clock_time_conversion<_DestClock, system_clock> { template _NODISCARD auto operator()(const sys_time<_Duration>& _Time) const - noexcept(noexcept(_Dest_Clock::from_sys(_Time))) /* strengthened */ - requires _Convertable_from_sys_time<_Dest_Clock, _Duration> { - return _Dest_Clock::from_sys(_Time); + noexcept(noexcept(_DestClock::from_sys(_Time))) /* strengthened */ + requires _Convertible_from_sys_time<_DestClock, _Duration> { + return _DestClock::from_sys(_Time); } }; // [time.clock.cast.utc] template - concept _Convertable_to_utc_time = + concept _Convertible_to_utc_time = is_same_v<_Clock, utc_clock> || requires(const time_point<_Clock, _Duration>& _Time) { { _Clock::to_utc(_Time) } ->_Is_time_point; }; template - concept _Convertable_from_utc_time = is_same_v<_Clock, utc_clock> || requires(const utc_time<_Duration>& _Time) { + concept _Convertible_from_utc_time = is_same_v<_Clock, utc_clock> || requires(const utc_time<_Duration>& _Time) { { _Clock::from_utc(_Time) } ->_Is_time_point<_Clock>; }; - template - struct clock_time_conversion { + template + struct clock_time_conversion { template - _NODISCARD auto operator()(const time_point<_Source_Clock, _Duration>& _Time) const - noexcept(noexcept(_Source_Clock::to_utc(_Time))) /* strengthened */ - requires _Convertable_to_utc_time<_Source_Clock, _Duration> { - return _Source_Clock::to_utc(_Time); + _NODISCARD auto operator()(const time_point<_SourceClock, _Duration>& _Time) const + noexcept(noexcept(_SourceClock::to_utc(_Time))) /* strengthened */ + requires _Convertible_to_utc_time<_SourceClock, _Duration> { + return _SourceClock::to_utc(_Time); } }; - template - struct clock_time_conversion<_Dest_Clock, utc_clock> { + template + struct clock_time_conversion<_DestClock, utc_clock> { template _NODISCARD auto operator()(const utc_time<_Duration>& _Time) const - noexcept(noexcept(_Dest_Clock::from_utc(_Time))) /* strengthened */ - requires _Convertable_from_utc_time<_Dest_Clock, _Duration> { - return _Dest_Clock::from_utc(_Time); + noexcept(noexcept(_DestClock::from_utc(_Time))) /* strengthened */ + requires _Convertible_from_utc_time<_DestClock, _Duration> { + return _DestClock::from_utc(_Time); } }; // [time.clock.cast.fn] - template - _NODISCARD auto clock_cast(const time_point<_Source_Clock, _Duration>& _Time) { + template + _NODISCARD auto clock_cast(const time_point<_SourceClock, _Duration>& _Time) { constexpr bool _Has_direct_conversion = - is_invocable_v, decltype(_Time)>; + is_invocable_v, decltype(_Time)>; - constexpr bool _Utc_from_src = _Convertable_to_utc_time<_Source_Clock, _Duration>; - constexpr bool _Sys_from_src = _Convertable_to_sys_time<_Source_Clock, _Duration>; - constexpr bool _Dest_from_utc = _Convertable_from_utc_time<_Dest_Clock, _Duration>; - constexpr bool _Dest_from_sys = _Convertable_from_sys_time<_Dest_Clock, _Duration>; + constexpr bool _Utc_from_src = _Convertible_to_utc_time<_SourceClock, _Duration>; + constexpr bool _Sys_from_src = _Convertible_to_sys_time<_SourceClock, _Duration>; + constexpr bool _Dest_from_utc = _Convertible_from_utc_time<_DestClock, _Duration>; + constexpr bool _Dest_from_sys = _Convertible_from_sys_time<_DestClock, _Duration>; constexpr bool _Has_utc_conversion = _Dest_from_utc && _Utc_from_src; constexpr bool _Has_sys_conversion = _Dest_from_sys && _Sys_from_src; static_assert(_Has_direct_conversion || !(_Has_utc_conversion && _Has_sys_conversion), "A two-step clock time conversion is required to be unique, either through utc_clock or system_clock, but " - "not both."); + "not both [time.clock.cast.fn]/2."); constexpr bool _Has_sys_utc_conversion = _Dest_from_sys && _Utc_from_src; constexpr bool _Has_utc_sys_conversion = _Dest_from_utc && _Sys_from_src; static_assert(_Has_direct_conversion || _Has_utc_conversion || _Has_sys_conversion || !(_Has_utc_sys_conversion && _Has_sys_utc_conversion), "A three-step clock time conversion is required to be unique, either utc-to-system or system-to-utc, but " - "not both."); + "not both [time.clock.cast.fn]/2."); if constexpr (_Has_direct_conversion) { - return clock_time_conversion<_Dest_Clock, _Source_Clock>{}(_Time); + return clock_time_conversion<_DestClock, _SourceClock>{}(_Time); } else if constexpr (_Has_utc_conversion) { - return clock_time_conversion<_Dest_Clock, utc_clock>{}( - clock_time_conversion{}(_Time)); + return clock_time_conversion<_DestClock, utc_clock>{}( + clock_time_conversion{}(_Time)); } else if constexpr (_Has_sys_conversion) { - return clock_time_conversion<_Dest_Clock, system_clock>{}( - clock_time_conversion{}(_Time)); + return clock_time_conversion<_DestClock, system_clock>{}( + clock_time_conversion{}(_Time)); } else if constexpr (_Has_sys_utc_conversion) { - return clock_time_conversion<_Dest_Clock, system_clock>{}(clock_time_conversion{}( - clock_time_conversion{}(_Time))); + return clock_time_conversion<_DestClock, system_clock>{}(clock_time_conversion{}( + clock_time_conversion{}(_Time))); } else if constexpr (_Has_utc_sys_conversion) { - return clock_time_conversion<_Dest_Clock, utc_clock>{}(clock_time_conversion{}( - clock_time_conversion{}(_Time))); + return clock_time_conversion<_DestClock, utc_clock>{}(clock_time_conversion{}( + clock_time_conversion{}(_Time))); } else { - static_assert(!_Has_direct_conversion && !_Has_utc_conversion && !_Has_sys_conversion - && !_Has_sys_utc_conversion && !_Has_utc_sys_conversion, + static_assert(_Always_false<_Duration>, "No clock time conversion exists from source clock type to destination clock type."); } } diff --git a/stl/inc/xtzdb.h b/stl/inc/xtzdb.h index 8ab59b0a85..678c421447 100644 --- a/stl/inc/xtzdb.h +++ b/stl/inc/xtzdb.h @@ -8,6 +8,8 @@ #if _STL_COMPILER_PREPROCESSOR #include #include +#include +#include #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -30,8 +32,43 @@ _EXTERN_C _RegistryLeapSecondInfo* __stdcall __std_tzdb_get_reg_leap_seconds( size_t _Prev_reg_ls_size, size_t* _Current_reg_ls_size); +void __stdcall __std_decalloc_reg_leap_seconds(_RegistryLeapSecondInfo* _Rlsi); + +_NODISCARD void* __stdcall __std_calloc_crt(size_t _Count, size_t _Size); +void __stdcall __std_free_crt(void* _Ptr); + _END_EXTERN_C +_STD_BEGIN + +template +class _Crt_allocator { +public: + using value_type = _Ty; + using propagate_on_container_move_assignment = _STD true_type; + using is_always_equal = _STD true_type; + + constexpr _Crt_allocator() noexcept = default; + + constexpr _Crt_allocator(const _Crt_allocator&) noexcept = default; + template + constexpr _Crt_allocator(const _Crt_allocator<_Other>&) noexcept {} + + _NODISCARD __declspec(allocator) _Ty* allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + const auto _Ptr = __std_calloc_crt(_Count, sizeof(_Ty)); + if (!_Ptr) { + _Xbad_alloc(); + } + return static_cast<_Ty*>(_Ptr); + } + + void deallocate(_Ty* const _Ptr, size_t) noexcept { + __std_free_crt(_Ptr); + } +}; + +_STD_END + #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) diff --git a/stl/src/msvcp_atomic_wait.src b/stl/src/msvcp_atomic_wait.src index 515ae81e15..62c5e88092 100644 --- a/stl/src/msvcp_atomic_wait.src +++ b/stl/src/msvcp_atomic_wait.src @@ -20,10 +20,13 @@ EXPORTS __std_atomic_wait_get_remaining_timeout __std_atomic_wait_indirect __std_bulk_submit_threadpool_work + __std_calloc_crt __std_close_threadpool_work __std_create_threadpool_work + __std_decalloc_reg_leap_seconds __std_execution_wait_on_uchar __std_execution_wake_by_address_all + __std_free_crt __std_parallel_algorithms_hw_threads __std_release_shared_mutex_for_instance __std_submit_threadpool_work diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 77e7df373e..6a3454a56b 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -4,13 +4,12 @@ // initialize syncstream mutex map #include -#include #include #include #include #include -#include #include +#include #pragma warning(disable : 4074) #pragma init_seg(compiler) @@ -23,33 +22,7 @@ namespace { size_t _Ref_count = 0; }; - template - class _Crt_allocator { - public: - using value_type = _Ty; - using propagate_on_container_move_assignment = _STD true_type; - using is_always_equal = _STD true_type; - - constexpr _Crt_allocator() noexcept = default; - - constexpr _Crt_allocator(const _Crt_allocator&) noexcept = default; - template - constexpr _Crt_allocator(const _Crt_allocator<_Other>&) noexcept {} - - _NODISCARD __declspec(allocator) _Ty* allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - const auto _Ptr = _calloc_crt(_Count, sizeof(_Ty)); - if (!_Ptr) { - throw _STD bad_alloc{}; - } - return static_cast<_Ty*>(_Ptr); - } - - void deallocate(_Ty* const _Ptr, size_t) noexcept { - _free_crt(_Ptr); - } - }; - - using _Map_alloc = _Crt_allocator<_STD pair>; + using _Map_alloc = _STD _Crt_allocator<_STD pair>; using _Map_type = _STD map, _Map_alloc>; _Map_type _Lookup_map; diff --git a/stl/src/tzdb.cpp b/stl/src/tzdb.cpp index 9ec85fb525..54be0537ba 100644 --- a/stl/src/tzdb.cpp +++ b/stl/src/tzdb.cpp @@ -1,11 +1,7 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// 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. - +#include #include #include @@ -60,4 +56,16 @@ _RegistryLeapSecondInfo* __stdcall __std_tzdb_get_reg_leap_seconds( return reg_ls_data; } +void __stdcall __std_decalloc_reg_leap_seconds(_RegistryLeapSecondInfo* _Rlsi) { + delete[] _Rlsi; +} + +_NODISCARD void* __std_calloc_crt(const size_t count, const size_t size) { + return _calloc_crt(count, size); +} + +void __std_free_crt(void* p) { + _free_crt(p); +} + _END_EXTERN_C diff --git a/tests/std/tests/P0355R7_clocks/test.cpp b/tests/std/tests/P0355R7_clocks/test.cpp index 6dfb794a26..9df066457c 100644 --- a/tests/std/tests/P0355R7_clocks/test.cpp +++ b/tests/std/tests/P0355R7_clocks/test.cpp @@ -9,7 +9,6 @@ using namespace std; using namespace std::chrono; -using namespace std::chrono_literals; void test_is_leap_second(const year_month_day& ymd) { const auto ls = sys_days{ymd}; @@ -31,7 +30,7 @@ void test_leap_second() { test_is_leap_second(31d / December / year{ls_year}); } - constexpr leap_second leap{sys_seconds{42s}, true}; + constexpr leap_second leap{sys_seconds{42s}, true, 0s}; constexpr sys_seconds smaller{41s}; constexpr sys_seconds equal{42s}; constexpr sys_seconds larger{43s}; @@ -75,21 +74,21 @@ void test_leap_second() { static_assert(is_gteq(leap <=> equal)); static_assert(noexcept(leap <=> equal)); - static_assert(is_eq(leap <=> leap_second{equal, true})); - static_assert(is_lt(leap <=> leap_second{larger, true})); - static_assert(is_gt(leap <=> leap_second{smaller, true})); - static_assert(is_lteq(leap <=> leap_second{larger, true})); - static_assert(is_gteq(leap <=> leap_second{smaller, true})); - static_assert(is_lteq(leap <=> leap_second{equal, true})); - static_assert(is_gteq(leap <=> leap_second{equal, true})); - static_assert(noexcept(leap <=> leap_second{equal, true})); + static_assert(is_eq(leap <=> leap_second{equal, true, 0s})); + static_assert(is_lt(leap <=> leap_second{larger, true, 0s})); + static_assert(is_gt(leap <=> leap_second{smaller, true, 0s})); + static_assert(is_lteq(leap <=> leap_second{larger, true, 0s})); + static_assert(is_gteq(leap <=> leap_second{smaller, true, 0s})); + static_assert(is_lteq(leap <=> leap_second{equal, true, 0s})); + static_assert(is_gteq(leap <=> leap_second{equal, true, 0s})); + static_assert(noexcept(leap <=> leap_second{equal, true, 0s})); #endif // __cpp_lib_concepts static_assert(noexcept(leap.date())); static_assert(noexcept(leap.value())); - static_assert(leap_second{sys_seconds{42s}, true}.date() == sys_seconds{42s}); - static_assert(leap_second{sys_seconds{42s}, true}.value() == 1s); - static_assert(leap_second{sys_seconds{42s}, false}.value() == -1s); + static_assert(leap_second{sys_seconds{42s}, true, 0s}.date() == sys_seconds{42s}); + static_assert(leap_second{sys_seconds{42s}, true, 0s}.value() == 1s); + static_assert(leap_second{sys_seconds{42s}, false, 0s}.value() == -1s); } constexpr bool operator==(const leap_second_info& lhs, const leap_second_info& rhs) { @@ -231,7 +230,7 @@ void test_file_clock_to_utc(const leap_second& leap, seconds offset) { if (leap > file_time_cutoff) { offset = 27s; } - offset -= duration_cast(file_clock::duration{__std_fs_file_time_epoch_adjustment}); + offset -= duration_cast(file_clock::duration{filesystem::__std_fs_file_time_epoch_adjustment}); auto u = file_clock::to_utc(t); assert(u.time_since_epoch() - t.time_since_epoch() == offset); @@ -275,6 +274,7 @@ int main() { test_utc_clock_from_sys(leap, offset); test_file_clock_to_utc(leap, offset); offset += leap.value(); + assert(leap._Elapsed() == offset); } test_gps_tai_clocks_utc(); test_file_clock_utc(); @@ -285,8 +285,8 @@ int main() { auto my_tzdb = get_tzdb_list().front(); auto& leap_vec = my_tzdb.leap_seconds; leap_vec.erase(leap_vec.begin() + 27, leap_vec.end()); - leap_vec.emplace_back(sys_days{1d / January / 2020y}, false); - leap_vec.emplace_back(sys_days{1d / January / 2021y}, true); + leap_vec.emplace_back(sys_days{1d / January / 2020y}, false, leap_vec.back()._Elapsed()); + leap_vec.emplace_back(sys_days{1d / January / 2021y}, true, leap_vec.back()._Elapsed()); my_tzdb._All_ls_positive = false; get_tzdb_list()._Emplace_front(move(my_tzdb)); } @@ -301,6 +301,7 @@ int main() { test_utc_clock_from_sys(leap, offset); test_file_clock_to_utc(leap, offset); offset += leap.value(); + assert(leap._Elapsed() == offset); } // positive and negative leap seconds when the accumulated offset is negative @@ -309,9 +310,9 @@ int main() { auto& leap_vec = my_tzdb.leap_seconds; leap_vec.erase(leap_vec.begin() + 27, leap_vec.end()); for (int i = 0; i < 30; ++i) { - leap_vec.emplace_back(sys_days{1d / January / year{i + 2020}}, false); + leap_vec.emplace_back(sys_days{1d / January / year{i + 2020}}, false, leap_vec.back()._Elapsed()); } - leap_vec.emplace_back(sys_days{1d / January / 2060y}, true); + leap_vec.emplace_back(sys_days{1d / January / 2060y}, true, leap_vec.back()._Elapsed()); get_tzdb_list()._Emplace_front(move(my_tzdb)); } @@ -325,6 +326,7 @@ int main() { test_utc_clock_from_sys(leap, offset); test_file_clock_to_utc(leap, offset); offset += leap.value(); + assert(leap._Elapsed() == offset); } return 0; From f8fc72548da4feb205273c420f2c6a35f3722297 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Wed, 24 Feb 2021 00:42:38 -0800 Subject: [PATCH 04/23] buildfix --- stl/src/tzdb.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/src/tzdb.cpp b/stl/src/tzdb.cpp index 54be0537ba..21c0863506 100644 --- a/stl/src/tzdb.cpp +++ b/stl/src/tzdb.cpp @@ -60,11 +60,11 @@ void __stdcall __std_decalloc_reg_leap_seconds(_RegistryLeapSecondInfo* _Rlsi) { delete[] _Rlsi; } -_NODISCARD void* __std_calloc_crt(const size_t count, const size_t size) { +_NODISCARD void* __stdcall __std_calloc_crt(const size_t count, const size_t size) { return _calloc_crt(count, size); } -void __std_free_crt(void* p) { +void __stdcall __std_free_crt(void* p) { _free_crt(p); } From 462def963a356b62dae27b7ff1f610aa7ed9e39f Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Wed, 24 Feb 2021 01:07:33 -0800 Subject: [PATCH 05/23] fix _HAS_CXX20/namespace nesting --- stl/inc/chrono | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 5b088a979f..dcbf3c5c0a 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2555,10 +2555,12 @@ namespace chrono { return gps_time>{_Time.time_since_epoch()} + _Gps_epoch_adjust; } }; +#endif // ^^^ _HAS_CXX20 } // namespace chrono // [time.clock.file] +#if _HAS_CXX20 namespace filesystem { struct _File_time_clock; } // namespace filesystem @@ -2623,10 +2625,10 @@ namespace filesystem { return _File_time; } -#endif // _HAS_CXX20 +#endif // ^^^ _HAS_CXX20 }; } // namespace filesystem -#endif // _HAS_CXX17 +#endif // ^^^ _HAS_CXX17 namespace chrono { #if _HAS_CXX20 From b0c81df62b6346a6228525b048ef966158d7b6f3 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Wed, 24 Feb 2021 01:15:34 -0800 Subject: [PATCH 06/23] _File_time_clock to filesystem namespace --- stl/inc/filesystem | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/filesystem b/stl/inc/filesystem index 937aa70a40..90ea82c2bf 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -2107,7 +2107,7 @@ namespace filesystem { #if _HAS_CXX20 using file_time_type = chrono::time_point; #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv - using file_time_type = chrono::time_point; + using file_time_type = chrono::time_point; #endif // ^^^ !_HAS_CXX20 // CLASS directory_entry From aaa64c06838dbd4d19a9095567ccca5580e552fd Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Wed, 24 Feb 2021 10:10:51 -0800 Subject: [PATCH 07/23] [time.clock.file] tests now pass, consolidate testing code --- tests/libcxx/expected_results.txt | 4 - tests/libcxx/skipped_tests.txt | 4 - tests/std/test.lst | 1 - .../test.compile.pass.cpp | 86 ------------------- .../test.cpp | 78 +++++++++++++++++ tests/std/tests/P0355R7_clocks/env.lst | 4 - 6 files changed, 78 insertions(+), 99 deletions(-) delete mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.compile.pass.cpp rename tests/std/tests/{P0355R7_clocks => P0355R7_calendars_and_time_zones_clocks}/test.cpp (83%) delete mode 100644 tests/std/tests/P0355R7_clocks/env.lst diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index a2496d683f..a200af184f 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -291,10 +291,6 @@ std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.nonmembers/streaming.pass. std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/streaming.pass.cpp FAIL std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/streaming.pass.cpp FAIL std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.clock/time.clock.file/consistency.pass.cpp FAIL -std/utilities/time/time.clock/time.clock.file/file_time.pass.cpp FAIL -std/utilities/time/time.clock/time.clock.file/now.pass.cpp FAIL -std/utilities/time/time.clock/time.clock.file/rep_signed.pass.cpp FAIL # C++20 P0466R5 "Layout-Compatibility And Pointer-Interconvertibility Traits" std/language.support/support.limits/support.limits.general/type_traits.version.pass.cpp:1 FAIL diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 2760e3e0b3..859fa0c485 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -291,10 +291,6 @@ utilities\time\time.cal\time.cal.ymd\time.cal.ymd.nonmembers\streaming.pass.cpp utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.nonmembers\streaming.pass.cpp utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.nonmembers\streaming.pass.cpp utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.nonmembers\streaming.pass.cpp -utilities\time\time.clock\time.clock.file\consistency.pass.cpp -utilities\time\time.clock\time.clock.file\file_time.pass.cpp -utilities\time\time.clock\time.clock.file\now.pass.cpp -utilities\time\time.clock\time.clock.file\rep_signed.pass.cpp # C++20 P0466R5 "Layout-Compatibility And Pointer-Interconvertibility Traits" language.support\support.limits\support.limits.general\type_traits.version.pass.cpp diff --git a/tests/std/test.lst b/tests/std/test.lst index ad0eaf9d16..ca60f536b2 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -235,7 +235,6 @@ tests\P0355R7_calendars_and_time_zones_dates_literals tests\P0355R7_calendars_and_time_zones_hms tests\P0355R7_calendars_and_time_zones_io tests\P0355R7_calendars_and_time_zones_time_point_and_durations -tests\P0355R7_clocks tests\P0356R5_bind_front tests\P0357R3_supporting_incomplete_types_in_reference_wrapper tests\P0408R7_efficient_access_to_stringbuf_buffer diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.compile.pass.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.compile.pass.cpp deleted file mode 100644 index 26ac91006b..0000000000 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.compile.pass.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include - -using namespace std::chrono; - -struct not_a_clock { - bool rep(); - static char period; - int duration(); - static float time_point; - using is_steady = long; - static int now; -}; - -struct real_fake_clock { - using rep = bool; - using period = char; - using duration = float; - using time_point = int; - static long is_steady; - static short now(); -}; - -struct no_rep { - using period = char; - using duration = float; - using time_point = int; - static long is_steady; - static short now(); -}; - -struct no_period { - using rep = bool; - using duration = float; - using time_point = int; - static long is_steady; - static short now(); -}; - -struct no_duration { - using rep = bool; - using period = char; - using time_point = int; - static long is_steady; - static short now(); -}; - -struct no_time_point { - using rep = bool; - using period = char; - using duration = float; - static long is_steady; - static short now(); -}; - -struct no_steady { - using rep = bool; - using period = char; - using duration = float; - using time_point = int; - static short now(); -}; - -struct no_now { - using rep = bool; - using period = char; - using duration = float; - using time_point = int; - static long is_steady; -}; - -static_assert(is_clock::value, "steady_clock is not a clock"); -static_assert(is_clock_v, "steady_clock is not a clock"); -static_assert(is_clock_v, "real_fake_clock is not a clock"); -static_assert(!is_clock_v, "not_a_clock is a clock"); - -static_assert(!is_clock_v, "no_rep is a clock"); -static_assert(!is_clock_v, "no_period is a clock"); -static_assert(!is_clock_v, "no_duration is a clock"); -static_assert(!is_clock_v, "no_time_point is a clock"); -static_assert(!is_clock_v, "no_steady is a clock"); -static_assert(!is_clock_v, "no_now is a clock"); - -int main() {} // COMPILE-ONLY diff --git a/tests/std/tests/P0355R7_clocks/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp similarity index 83% rename from tests/std/tests/P0355R7_clocks/test.cpp rename to tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp index 9df066457c..d2d1eff279 100644 --- a/tests/std/tests/P0355R7_clocks/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp @@ -10,6 +10,84 @@ using namespace std; using namespace std::chrono; +struct not_a_clock { + bool rep(); + static char period; + int duration(); + static float time_point; + using is_steady = long; + static int now; +}; + +struct real_fake_clock { + using rep = bool; + using period = char; + using duration = float; + using time_point = int; + static long is_steady; + static short now(); +}; + +struct no_rep { + using period = char; + using duration = float; + using time_point = int; + static long is_steady; + static short now(); +}; + +struct no_period { + using rep = bool; + using duration = float; + using time_point = int; + static long is_steady; + static short now(); +}; + +struct no_duration { + using rep = bool; + using period = char; + using time_point = int; + static long is_steady; + static short now(); +}; + +struct no_time_point { + using rep = bool; + using period = char; + using duration = float; + static long is_steady; + static short now(); +}; + +struct no_steady { + using rep = bool; + using period = char; + using duration = float; + using time_point = int; + static short now(); +}; + +struct no_now { + using rep = bool; + using period = char; + using duration = float; + using time_point = int; + static long is_steady; +}; + +static_assert(is_clock::value, "steady_clock is not a clock"); +static_assert(is_clock_v, "steady_clock is not a clock"); +static_assert(is_clock_v, "real_fake_clock is not a clock"); +static_assert(!is_clock_v, "not_a_clock is a clock"); + +static_assert(!is_clock_v, "no_rep is a clock"); +static_assert(!is_clock_v, "no_period is a clock"); +static_assert(!is_clock_v, "no_duration is a clock"); +static_assert(!is_clock_v, "no_time_point is a clock"); +static_assert(!is_clock_v, "no_steady is a clock"); +static_assert(!is_clock_v, "no_now is a clock"); + void test_is_leap_second(const year_month_day& ymd) { const auto ls = sys_days{ymd}; const auto& leap_seconds = get_tzdb().leap_seconds; diff --git a/tests/std/tests/P0355R7_clocks/env.lst b/tests/std/tests/P0355R7_clocks/env.lst deleted file mode 100644 index 642f530ffa..0000000000 --- a/tests/std/tests/P0355R7_clocks/env.lst +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -RUNALL_INCLUDE ..\usual_latest_matrix.lst From 8a91f5a8e6144bc13f5bc102e63625b6aacf6c15 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Thu, 25 Feb 2021 08:10:51 -0800 Subject: [PATCH 08/23] revert whitespace change; more review feedback --- stl/inc/chrono | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index dcbf3c5c0a..565b831dd5 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -418,8 +418,8 @@ namespace chrono { #ifdef __cpp_lib_concepts // clang-format off template - requires three_way_comparable, duration<_Rep2, _Period2>>::rep> - _NODISCARD constexpr auto + requires three_way_comparable, duration<_Rep2, _Period2>>::rep> + _NODISCARD constexpr auto operator<=>(const duration<_Rep1, _Period1>& _Left, const duration<_Rep2, _Period2>& _Right) noexcept( is_arithmetic_v<_Rep1>&& is_arithmetic_v<_Rep2>) /* strengthened */ { // clang-format on @@ -2289,7 +2289,7 @@ namespace chrono { _Unique_lock _Lk(_Tzdb_mutex); auto [_Leap_sec, _All_ls_positive] = _Xtzdb_generate_leap_seconds(_Tzdb_list.front().leap_seconds.size()); if (!_Leap_sec.empty()) { - _Emplace_front(tzdb{_STD move(_Leap_sec), _All_ls_positive}); + _Tzdb_list.emplace_front(tzdb{_STD move(_Leap_sec), _All_ls_positive}); } return _Tzdb_list.front(); } @@ -2470,18 +2470,8 @@ namespace chrono { _NODISCARD static utc_time> from_sys(const sys_time<_Duration>& _Sys_time) { const auto& _Tzdb = _CHRONO get_tzdb(); const auto& _Ls_vector = _Tzdb.leap_seconds; - const auto _It = _STD upper_bound(_Ls_vector.begin(), _Ls_vector.end(), _CHRONO floor(_Sys_time)); - - seconds _Offset; - if (_Tzdb._All_ls_positive) { - _Offset = seconds{_It - _Ls_vector.begin()}; - } else { - _Offset = seconds{0}; - for (auto _Ls = _Tzdb.leap_seconds.begin(); _Ls != _It; ++_Ls) { - _Offset += _Ls->value(); - } - } - + auto _It = _STD upper_bound(_Ls_vector.begin(), _Ls_vector.end(), _CHRONO floor(_Sys_time)); + const auto _Offset = _It == _Ls_vector.begin() ? seconds{0} : (--_It)->_Elapsed(); return utc_time>{_Sys_time.time_since_epoch() + _Offset}; } }; From 40399b56b27efd3978cf390750d53ce751e0c537 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Sun, 28 Feb 2021 20:48:52 -0800 Subject: [PATCH 09/23] utc_clock::to_sys with floating point durations --- stl/inc/chrono | 18 ++++++++--- .../test.cpp | 32 +++++++++++++++---- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 565b831dd5..48e829f56a 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -18,6 +18,7 @@ #if _HAS_CXX20 #include #include +#include #include #include #include @@ -2457,13 +2458,22 @@ namespace chrono { template _NODISCARD static sys_time> to_sys(const utc_time<_Duration>& _Utc_time) { + using _CT = common_type_t<_Duration, seconds>; const auto _Lsi{get_leap_second_info(_Utc_time)}; - sys_time> _Sys_time{_Utc_time.time_since_epoch() - _Lsi.elapsed}; + _CT _Ticks; if (_Lsi.is_leap_second) { - constexpr auto _Delta{seconds{1} - common_type_t<_Duration, seconds>{1}}; - _Sys_time = _CHRONO floor(_Sys_time) + _Delta; + const auto _Leap_sec_minus_one = _CHRONO floor(_Utc_time.time_since_epoch()) - _Lsi.elapsed; + if constexpr (is_integral_v) { + constexpr auto _Delta{seconds{1} - _CT{1}}; + _Ticks = _Leap_sec_minus_one + _Delta; + } else { + const auto _Leap_sec_begin = _CHRONO ceil<_CT>(_Leap_sec_minus_one + seconds{1}); + _Ticks = _CT{_STD nextafter(_Leap_sec_begin.count(), typename _CT::rep{0})}; + } + } else { + _Ticks = _Utc_time.time_since_epoch() - _Lsi.elapsed; } - return _Sys_time; + return sys_time<_CT>{_Ticks}; } template diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp index d2d1eff279..75417402b0 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -173,10 +174,11 @@ constexpr bool operator==(const leap_second_info& lhs, const leap_second_info& r return lhs.is_leap_second == rhs.is_leap_second && lhs.elapsed == rhs.elapsed; } +template void test_leap_second_info(const leap_second& leap, seconds accum) { const bool is_positive = (leap.value() == 1s); // First UTC time when leap is counted, before insertion of a positive leap, after insertion of a negative one. - const utc_seconds utc_leap{leap.date().time_since_epoch() + accum + (is_positive ? 0s : -1s)}; + const utc_time> utc_leap{leap.date().time_since_epoch() + accum + (is_positive ? 0s : -1s)}; auto lsi = get_leap_second_info(utc_leap - 1s); assert(lsi == (leap_second_info{false, accum})); @@ -197,11 +199,19 @@ void test_leap_second_info(const leap_second& leap, seconds accum) { template void test_utc_clock_to_sys(const leap_second& leap) { - auto u = utc_clock::from_sys(leap.date() - Duration{1}); // just before leap second + sys_time before_leap; + if constexpr (is_integral_v) { + before_leap = leap.date() - Duration{1}; + } else { + before_leap = + sys_time{Duration{nextafter(leap.date().time_since_epoch().count(), typename Duration::rep{0})}}; + } + + auto u = utc_clock::from_sys(before_leap); // just before leap second assert(utc_clock::from_sys(utc_clock::to_sys(u)) == u); if (leap.value() == 1s) { u += Duration{1}; - assert(utc_clock::to_sys(u) == leap.date() - Duration{1}); // during + assert(utc_clock::to_sys(u) == before_leap); // during } else { assert(utc_clock::from_sys(utc_clock::to_sys(u)) == u); } @@ -342,11 +352,17 @@ void test_gps_tai_clocks_utc() { int main() { test_leap_second(); + // This is the only time duration a leap second insertion that can be represented by a duration. + assert(utc_clock::to_sys(utc_time>{duration{78796800.0f}}).time_since_epoch().count() + == nextafter(78796800.0f, 0.0f)); + seconds offset{0}; for (const auto& leap : get_tzdb().leap_seconds) { - test_leap_second_info(leap, offset); + test_leap_second_info(leap, offset); + test_leap_second_info(leap, offset); test_utc_clock_to_sys(leap); test_utc_clock_to_sys(leap); + test_utc_clock_to_sys>(leap); test_file_clock_from_utc(leap); test_file_clock_from_utc(leap); test_utc_clock_from_sys(leap, offset); @@ -371,9 +387,11 @@ int main() { offset = 0s; for (const auto& leap : get_tzdb().leap_seconds) { - test_leap_second_info(leap, offset); + test_leap_second_info(leap, offset); + test_leap_second_info(leap, offset); test_utc_clock_to_sys(leap); test_utc_clock_to_sys(leap); + test_utc_clock_to_sys>(leap); test_file_clock_from_utc(leap); test_file_clock_from_utc(leap); test_utc_clock_from_sys(leap, offset); @@ -396,9 +414,11 @@ int main() { offset = 0s; for (const auto& leap : get_tzdb().leap_seconds) { - test_leap_second_info(leap, offset); + test_leap_second_info(leap, offset); + test_leap_second_info(leap, offset); test_utc_clock_to_sys(leap); test_utc_clock_to_sys(leap); + test_utc_clock_to_sys>(leap); test_file_clock_from_utc(leap); test_file_clock_from_utc(leap); test_utc_clock_from_sys(leap, offset); From 696191603707d39c26ca7b5641b0577e925228d8 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Wed, 3 Mar 2021 22:21:27 -0800 Subject: [PATCH 10/23] Unicode Windows API functions --- stl/src/tzdb.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stl/src/tzdb.cpp b/stl/src/tzdb.cpp index 21c0863506..39d02d65df 100644 --- a/stl/src/tzdb.cpp +++ b/stl/src/tzdb.cpp @@ -18,19 +18,19 @@ _RegistryLeapSecondInfo* __stdcall __std_tzdb_get_reg_leap_seconds( // *current_reg_ls_size == 0, *reg_ls_data != nullptr --> new data, failed reading // *current_reg_ls_size > prev_reg_ls_size, *reg_ls_data == nullptr --> new data, failed allocation - constexpr auto reg_key_name = TEXT("SYSTEM\\CurrentControlSet\\Control\\LeapSecondInformation"); - constexpr auto reg_subkey_name = TEXT("LeapSeconds"); + constexpr auto reg_key_name = LR"(SYSTEM\CurrentControlSet\Control\LeapSecondInformation)"; + constexpr auto reg_subkey_name = L"LeapSeconds"; *current_reg_ls_size = 0; HKEY leap_sec_key = 0; - LSTATUS status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_key_name, 0, KEY_READ, &leap_sec_key); + LSTATUS status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_key_name, 0, KEY_READ, &leap_sec_key); if (status != ERROR_SUCCESS) { // May not exist on older systems. Treat this as equivalent to the key existing but with no data. return nullptr; } DWORD byte_size = 0; - status = RegQueryValueEx(leap_sec_key, reg_subkey_name, nullptr, nullptr, nullptr, &byte_size); + status = RegQueryValueExW(leap_sec_key, reg_subkey_name, nullptr, nullptr, nullptr, &byte_size); static_assert(sizeof(_RegistryLeapSecondInfo) == 12); const auto ls_size = byte_size / 12; *current_reg_ls_size = ls_size; @@ -39,7 +39,7 @@ _RegistryLeapSecondInfo* __stdcall __std_tzdb_get_reg_leap_seconds( if ((status == ERROR_SUCCESS || status == ERROR_MORE_DATA) && ls_size > prev_reg_ls_size) { try { reg_ls_data = new _RegistryLeapSecondInfo[ls_size]; - status = RegQueryValueEx( + status = RegQueryValueExW( leap_sec_key, reg_subkey_name, nullptr, nullptr, reinterpret_cast(reg_ls_data), &byte_size); if (status != ERROR_SUCCESS) { *current_reg_ls_size = 0; From 1d63949d705f891c0001005e33bd64b7eeb3150b Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Wed, 3 Mar 2021 22:24:12 -0800 Subject: [PATCH 11/23] YOU get constexpr, YOU get constexpr, EVERYBODY gets constexpr --- stl/inc/chrono | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 48e829f56a..f550436b71 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2127,47 +2127,47 @@ namespace chrono { seconds _Elapsed_offset; }; - _NODISCARD inline bool operator==(const leap_second& _Left, const leap_second& _Right) noexcept { + _NODISCARD constexpr bool operator==(const leap_second& _Left, const leap_second& _Right) noexcept { return _Left.date() == _Right.date(); } template - _NODISCARD bool operator==(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + _NODISCARD constexpr bool operator==(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { return _Left.date() == _Right; } template - _NODISCARD bool operator<(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + _NODISCARD constexpr bool operator<(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { return _Left.date() < _Right; } template - _NODISCARD bool operator<(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { + _NODISCARD constexpr bool operator<(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { return _Left < _Right.date(); } template - _NODISCARD bool operator>(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + _NODISCARD constexpr bool operator>(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { return _Right < _Left.date(); } template - _NODISCARD bool operator>(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { + _NODISCARD constexpr bool operator>(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { return _Right.date() < _Left; } template - _NODISCARD bool operator<=(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + _NODISCARD constexpr bool operator<=(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { return !(_Right < _Left.date()); } template - _NODISCARD bool operator<=(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { + _NODISCARD constexpr bool operator<=(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { return !(_Right.date() < _Left); } template - _NODISCARD bool operator>=(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + _NODISCARD constexpr bool operator>=(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { return !(_Left.date() < _Right); } template - _NODISCARD bool operator>=(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { + _NODISCARD constexpr bool operator>=(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { return !(_Left < _Right.date()); } From e41fb5576393d4ab3b8b2c4267062514f711ad00 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Thu, 4 Mar 2021 19:10:43 -0800 Subject: [PATCH 12/23] Comment cleanup --- stl/inc/chrono | 50 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index f550436b71..f934fd1fce 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2222,21 +2222,27 @@ namespace chrono { {sys_seconds{seconds{1435708800}}, true, seconds{25}}, {sys_seconds{seconds{1483228800}}, true, seconds{26}}, }; + + // __std_tzdb_get_reg_leap_seconds gets leap second (LS) data from the registry, but only if it contains more + // LSs than we already know about. The registry only contains LSs after 2018, so we need to tell it how many of + // *those* we already know about. The *total* number of LSs known at this point is a combination of what the + // caller knows (_Current_size, 0 on first call) and the _Known_leap_seconds entries. constexpr size_t _Pre_2018_count = 27; + const size_t _Known_post_2018_ls_size = + (_STD max)(_Current_size, _STD size(_Known_leap_seconds)) - _Pre_2018_count; - size_t _New_reg_ls_size; + size_t _Reg_post_2018_ls_size; // number of post-2018 LSs found in the registry unique_ptr<_RegistryLeapSecondInfo[], decltype(&__std_decalloc_reg_leap_seconds)> _Reg_ls_data( - __std_tzdb_get_reg_leap_seconds( - (_STD max)(_Current_size, _STD size(_Known_leap_seconds)) - _Pre_2018_count, &_New_reg_ls_size), + __std_tzdb_get_reg_leap_seconds(_Known_post_2018_ls_size, &_Reg_post_2018_ls_size), &__std_decalloc_reg_leap_seconds); - const size_t _New_size = _Pre_2018_count + _New_reg_ls_size; - if (_New_reg_ls_size > _Current_size && !_Reg_ls_data) { - _Xbad_alloc(); - } else if (_New_reg_ls_size == 0 && _Reg_ls_data) { - _XGetLastError(); + if (_Reg_post_2018_ls_size > _Known_post_2018_ls_size && !_Reg_ls_data) { + _Xbad_alloc(); // registry has new data, but failed to allocate storage + } else if (_Reg_post_2018_ls_size == 0 && _Reg_ls_data) { + _XGetLastError(); // allocated storage for registry data, but failed to read } + const size_t _New_size = _Pre_2018_count + _Reg_post_2018_ls_size; // total size with registry data decltype(tzdb::leap_seconds) _Leap_sec_info; bool _All_ls_positive = true; @@ -2245,8 +2251,9 @@ namespace chrono { _Leap_sec_info.insert( _Leap_sec_info.begin(), _STD cbegin(_Known_leap_seconds), _STD cend(_Known_leap_seconds)); - for (size_t _Idx = 0; _Idx < _New_reg_ls_size; ++_Idx) { - // Leap second happens at ls._Hour:59:59. We store the next second after, so add a whole hour. + for (size_t _Idx = 0; _Idx < _Reg_post_2018_ls_size; ++_Idx) { + // Leap seconds occur at _Ls._Hour:59:59. We store the next second after, so we need to add an entire + // hour. const auto& _Ls = _Reg_ls_data[_Idx]; const auto _Date = static_cast(year_month_day{year{_Ls._Year}, month{_Ls._Month}, day{_Ls._Day}}) @@ -2387,10 +2394,14 @@ namespace chrono { // Find first leap second after _Time. vector::const_iterator _It; if (_Tzdb._All_ls_positive) { - // _It here is actually the *2nd* leap second after _Time for the _Elapsed()-1 seconds before the next leap - // second insertion. This doesn't overlap with the actual leap second insertion period, so prev(_It) can - // always be used to detect if _Time is during an insertion. But an additional decrement is needed to get - // the correct eleapsed offset. For example, if leap seconds are {100, 200, 300, 400}, we have + // Where "target_ls" is the next leap second at or after _Time, _It either points to: + // (1) The 2nd leap second after _Time if _Time_floor is in the range [target_ls - _Elapsed() - 1, + // target_ls), or + // (2) The leap second just after _Time otherwise. + // Note that we can always use prev(_It) to determine whether _Time is *during* a leap second insertion, + // since that falls under case (2) above. However, when we fall under case (1), we need to execute an + // additional decrement to get the correct elapsed offset. For example, if leap seconds are inserted at + // seconds {100, 200, 300, 400}, we have: // // UTC sys *_It // 99 99 100 @@ -2415,9 +2426,12 @@ namespace chrono { } else { seconds _Prev_elapsed{0}; for (_It = _Ls_vector.begin(); _It != _Ls_vector.end(); ++_It) { - // Positive leap seconds are counted one second earlier, when insertion begins. - if (utc_seconds{_It->date().time_since_epoch()} + (_It->_Positive() ? _Prev_elapsed : _It->_Elapsed()) - > _Time_floor) { + // UTC time when leap second insertion begins. In all cases, _It->date() + _It->_Elapsed() is the *end* + // of the insertion. For a negative leap that's also the beginning, but for a positive one, insertion + // begins 1 second earlier. + const utc_seconds _This_ls_begin{ + _It->date().time_since_epoch() + (_It->_Positive() ? _Prev_elapsed : _It->_Elapsed())}; + if (_This_ls_begin > _Time_floor) { break; } _Prev_elapsed = _It->_Elapsed(); @@ -2435,7 +2449,7 @@ namespace chrono { : _Utc_leap_second == _Time_floor ? strong_ordering::equal : strong_ordering::less; #endif // ^^^ workaround - if (_Tzdb._All_ls_positive && _STD is_gt(_Leap_cmp)) { + if (_Tzdb._All_ls_positive && _STD is_gt(_Leap_cmp)) { // Case (1) --_It; } return {_Last_leap._Positive() && _STD is_eq(_Leap_cmp), _It->_Elapsed()}; From c2ef0a1f3bcbfaa21e53175e9c23d215e8648bb3 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Thu, 4 Mar 2021 23:41:54 -0800 Subject: [PATCH 13/23] SHOUTY comments: in for a penny, in for a pound --- stl/inc/chrono | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/stl/inc/chrono b/stl/inc/chrono index f934fd1fce..efe11fd0db 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2094,6 +2094,7 @@ namespace chrono { // [time.zone.leap] + // CLASS leap_second class leap_second { public: leap_second(const leap_second&) = default; @@ -2185,6 +2186,7 @@ namespace chrono { // [time.zone.db] // TRANSITION: work in progress + // STRUCT tzdb struct tzdb { vector> leap_seconds; bool _All_ls_positive; @@ -2267,6 +2269,7 @@ namespace chrono { } // TRANSITION: work in progress + // CLASS tzdb_list class tzdb_list { private: using _ListType = forward_list>; @@ -2349,6 +2352,7 @@ namespace chrono { inline atomic _Global_tzdb_list; + // FUNCTION get_tzdb_list _NODISCARD inline tzdb_list& get_tzdb_list() { auto* _Tzdb_ptr = _Global_tzdb_list.load(); if (_Tzdb_ptr == nullptr) { @@ -2365,10 +2369,12 @@ namespace chrono { return *_Tzdb_ptr; } + // FUNCTION get_tzdb _NODISCARD inline const tzdb& get_tzdb() { return _CHRONO get_tzdb_list().front(); } + // FUNCTION reload_tzdb inline const tzdb& reload_tzdb() { return _CHRONO get_tzdb_list()._Reload(); } @@ -2380,11 +2386,13 @@ namespace chrono { using utc_time = time_point; using utc_seconds = utc_time; + // STRUCT leap_second_info struct leap_second_info { bool is_leap_second; seconds elapsed; }; + // FUNCTION TEMPLATE get_leap_second_info template _NODISCARD leap_second_info get_leap_second_info(const utc_time<_Duration>& _Time) { const utc_seconds _Time_floor = _CHRONO floor(_Time); @@ -2458,6 +2466,7 @@ namespace chrono { } } + // CLASS utc_clock class utc_clock { public: using rep = system_clock::rep; @@ -2508,6 +2517,7 @@ namespace chrono { using tai_time = time_point; using tai_seconds = tai_time; + // CLASS tai_clock class tai_clock { public: using rep = system_clock::rep; @@ -2543,6 +2553,7 @@ namespace chrono { using gps_time = time_point; using gps_seconds = gps_time; + // CLASS gps_clock class gps_clock { public: using rep = system_clock::rep; @@ -2580,6 +2591,7 @@ namespace filesystem { } // namespace filesystem namespace chrono { + // ALIAS file_clock using file_clock = filesystem::_File_time_clock; template @@ -2648,6 +2660,7 @@ namespace chrono { #if _HAS_CXX20 // [time.clock.conv] + // STRUCT TEMPLATE clock_time_conversion template struct clock_time_conversion; @@ -2777,6 +2790,7 @@ namespace chrono { // [time.clock.cast.fn] + // FUNCTION TEMPLATE clock_cast template _NODISCARD auto clock_cast(const time_point<_SourceClock, _Duration>& _Time) { constexpr bool _Has_direct_conversion = From 4a94f3fd2179adb15c15ef42c97105d684ff8cfd Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Thu, 4 Mar 2021 23:45:25 -0800 Subject: [PATCH 14/23] formatting --- stl/inc/chrono | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index efe11fd0db..3a12708241 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2173,9 +2173,12 @@ namespace chrono { } #ifdef __cpp_lib_concepts + // clang-format off template - requires three_way_comparable_with> _NODISCARD constexpr auto operator<=>( + requires three_way_comparable_with> + _NODISCARD constexpr auto operator<=>( const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + // clang-format on return _Left.date() <=> _Right; } _NODISCARD constexpr strong_ordering operator<=>(const leap_second& _Left, const leap_second& _Right) noexcept { @@ -2814,24 +2817,28 @@ namespace chrono { "A three-step clock time conversion is required to be unique, either utc-to-system or system-to-utc, but " "not both [time.clock.cast.fn]/2."); + // clang-format off if constexpr (_Has_direct_conversion) { return clock_time_conversion<_DestClock, _SourceClock>{}(_Time); } else if constexpr (_Has_utc_conversion) { return clock_time_conversion<_DestClock, utc_clock>{}( - clock_time_conversion{}(_Time)); + clock_time_conversion{}(_Time)); } else if constexpr (_Has_sys_conversion) { return clock_time_conversion<_DestClock, system_clock>{}( - clock_time_conversion{}(_Time)); + clock_time_conversion{}(_Time)); } else if constexpr (_Has_sys_utc_conversion) { - return clock_time_conversion<_DestClock, system_clock>{}(clock_time_conversion{}( - clock_time_conversion{}(_Time))); + return clock_time_conversion<_DestClock, system_clock>{}( + clock_time_conversion{}( + clock_time_conversion{}(_Time))); } else if constexpr (_Has_utc_sys_conversion) { - return clock_time_conversion<_DestClock, utc_clock>{}(clock_time_conversion{}( - clock_time_conversion{}(_Time))); + return clock_time_conversion<_DestClock, utc_clock>{}( + clock_time_conversion{}( + clock_time_conversion{}(_Time))); } else { static_assert(_Always_false<_Duration>, "No clock time conversion exists from source clock type to destination clock type."); } + // clang-format on } #endif // _HAS_CXX20 } // namespace chrono From bcab233a46f0c81a2d89fd254b0d4d047a6f6284 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Thu, 4 Mar 2021 23:46:41 -0800 Subject: [PATCH 15/23] static_assert wording --- stl/inc/chrono | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 3a12708241..a9b60c6e83 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2808,14 +2808,14 @@ namespace chrono { constexpr bool _Has_sys_conversion = _Dest_from_sys && _Sys_from_src; static_assert(_Has_direct_conversion || !(_Has_utc_conversion && _Has_sys_conversion), "A two-step clock time conversion is required to be unique, either through utc_clock or system_clock, but " - "not both [time.clock.cast.fn]/2."); + "not both (N4878 [time.clock.cast.fn]/2.)"); constexpr bool _Has_sys_utc_conversion = _Dest_from_sys && _Utc_from_src; constexpr bool _Has_utc_sys_conversion = _Dest_from_utc && _Sys_from_src; static_assert(_Has_direct_conversion || _Has_utc_conversion || _Has_sys_conversion || !(_Has_utc_sys_conversion && _Has_sys_utc_conversion), "A three-step clock time conversion is required to be unique, either utc-to-system or system-to-utc, but " - "not both [time.clock.cast.fn]/2."); + "not both (N4878 [time.clock.cast.fn]/2)."); // clang-format off if constexpr (_Has_direct_conversion) { @@ -2835,8 +2835,8 @@ namespace chrono { clock_time_conversion{}( clock_time_conversion{}(_Time))); } else { - static_assert(_Always_false<_Duration>, - "No clock time conversion exists from source clock type to destination clock type."); + static_assert(_Always_false<_Duration>, "No clock time conversion exists from source clock type to " + "destination clock type (N4878 [time.clock.cast.fn]/1)."); } // clang-format on } From fb5de9b6fa6596819234651f31a05c00a48a655c Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Thu, 4 Mar 2021 23:47:15 -0800 Subject: [PATCH 16/23] improved exception handling --- stl/inc/chrono | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index a9b60c6e83..2df4402b13 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2360,7 +2360,16 @@ namespace chrono { auto* _Tzdb_ptr = _Global_tzdb_list.load(); if (_Tzdb_ptr == nullptr) { auto _My_tzdb = static_cast(__std_calloc_crt(1, sizeof(tzdb_list))); - _STD construct_at(_My_tzdb); + if (_My_tzdb == nullptr) { + _Xruntime_error("bad allocation"); + } + + try { + _STD construct_at(_My_tzdb); + } catch (const exception& _Except) { + _Xruntime_error(_Except.what()); + } + if (_Global_tzdb_list.compare_exchange_strong(_Tzdb_ptr, _My_tzdb)) { _Tzdb_ptr = _My_tzdb; } else { @@ -2379,7 +2388,11 @@ namespace chrono { // FUNCTION reload_tzdb inline const tzdb& reload_tzdb() { - return _CHRONO get_tzdb_list()._Reload(); + try { + return _CHRONO get_tzdb_list()._Reload(); + } catch (const exception& _Except) { + _Xruntime_error(_Except.what()); + } } // [time.clock.utc] From 93a2ba59cb72937c2f92145d5a5a26b826565e49 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Thu, 4 Mar 2021 23:48:01 -0800 Subject: [PATCH 17/23] switch negated test --- stl/inc/chrono | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 2df4402b13..382e626b0a 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2462,7 +2462,9 @@ namespace chrono { } } - if (_It != _Ls_vector.begin()) { + if (_It == _Ls_vector.begin()) { + return {false, seconds{0}}; + } else { // Convert to the last leap second before or equal to _Time. const auto& _Last_leap = *--_It; const utc_seconds _Utc_leap_second{_Last_leap.date().time_since_epoch() + _It->_Elapsed() - seconds{1}}; @@ -2477,8 +2479,6 @@ namespace chrono { --_It; } return {_Last_leap._Positive() && _STD is_eq(_Leap_cmp), _It->_Elapsed()}; - } else { - return {false, seconds{0}}; } } From 6ed6103cda9a02a4cf8f374e89fab8735364f1c3 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Thu, 4 Mar 2021 23:49:37 -0800 Subject: [PATCH 18/23] less macro-y alias --- stl/inc/chrono | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 382e626b0a..6fbf8536b1 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2497,22 +2497,22 @@ namespace chrono { template _NODISCARD static sys_time> to_sys(const utc_time<_Duration>& _Utc_time) { - using _CT = common_type_t<_Duration, seconds>; + using _CommonType = common_type_t<_Duration, seconds>; const auto _Lsi{get_leap_second_info(_Utc_time)}; - _CT _Ticks; + _CommonType _Ticks; if (_Lsi.is_leap_second) { const auto _Leap_sec_minus_one = _CHRONO floor(_Utc_time.time_since_epoch()) - _Lsi.elapsed; if constexpr (is_integral_v) { - constexpr auto _Delta{seconds{1} - _CT{1}}; + constexpr auto _Delta{seconds{1} - _CommonType{1}}; _Ticks = _Leap_sec_minus_one + _Delta; } else { - const auto _Leap_sec_begin = _CHRONO ceil<_CT>(_Leap_sec_minus_one + seconds{1}); - _Ticks = _CT{_STD nextafter(_Leap_sec_begin.count(), typename _CT::rep{0})}; + const auto _Leap_sec_begin = _CHRONO ceil<_CommonType>(_Leap_sec_minus_one + seconds{1}); + _Ticks = _CommonType{_STD nextafter(_Leap_sec_begin.count(), typename _CommonType::rep{0})}; } } else { _Ticks = _Utc_time.time_since_epoch() - _Lsi.elapsed; } - return sys_time<_CT>{_Ticks}; + return sys_time<_CommonType>{_Ticks}; } template @@ -2641,14 +2641,14 @@ namespace filesystem { _NODISCARD static chrono::utc_time> to_utc( const chrono::file_time<_Duration>& _File_time) { using namespace chrono; - using _CT = common_type_t<_Duration, seconds>; + using _CommonType = common_type_t<_Duration, seconds>; const auto _Ticks = _File_time.time_since_epoch() - duration_cast(duration{__std_fs_file_time_epoch_adjustment}); if (_Ticks < _Cutoff.time_since_epoch()) { - return utc_clock::from_sys(sys_time<_CT>{_Ticks}); + return utc_clock::from_sys(sys_time<_CommonType>{_Ticks}); } else { - return utc_time<_CT>{_Ticks + _Skipped_filetime_leap_seconds}; + return utc_time<_CommonType>{_Ticks + _Skipped_filetime_leap_seconds}; } } @@ -2656,8 +2656,8 @@ namespace filesystem { _NODISCARD static chrono::file_time> from_utc( const chrono::utc_time<_Duration>& _Utc_time) { using namespace chrono; - using _CT = common_type_t<_Duration, seconds>; - file_time<_CT> _File_time{duration_cast(duration{__std_fs_file_time_epoch_adjustment})}; + file_time> _File_time{ + duration_cast(duration{__std_fs_file_time_epoch_adjustment})}; if (_Utc_time < utc_seconds{_Cutoff.time_since_epoch()} + _Skipped_filetime_leap_seconds) { _File_time += utc_clock::to_sys(_Utc_time).time_since_epoch(); From 07fc4a8cc7a4b3add2369a1793c58e71c3609628 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Thu, 4 Mar 2021 23:50:44 -0800 Subject: [PATCH 19/23] explicit return types to match Standard --- stl/inc/chrono | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 6fbf8536b1..3b96cfec3f 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2685,7 +2685,7 @@ namespace chrono { template struct clock_time_conversion<_Clock, _Clock> { template - _NODISCARD auto operator()(const time_point<_Clock, _Duration>& _Time) const + _NODISCARD time_point<_Clock, _Duration> operator()(const time_point<_Clock, _Duration>& _Time) const noexcept(is_arithmetic_v) /* strengthened */ { return _Time; } @@ -2694,7 +2694,7 @@ namespace chrono { template <> struct clock_time_conversion { template - _NODISCARD auto operator()(const sys_time<_Duration>& _Time) const + _NODISCARD sys_time<_Duration> operator()(const sys_time<_Duration>& _Time) const noexcept(is_arithmetic_v) /* strengthened */ { return _Time; } @@ -2703,7 +2703,7 @@ namespace chrono { template <> struct clock_time_conversion { template - _NODISCARD auto operator()(const utc_time<_Duration>& _Time) const + _NODISCARD utc_time<_Duration> operator()(const utc_time<_Duration>& _Time) const noexcept(is_arithmetic_v) /* strengthened */ { return _Time; } @@ -2714,7 +2714,7 @@ namespace chrono { template <> struct clock_time_conversion { template - _NODISCARD auto operator()(const sys_time<_Duration>& _Sys_time) const { + _NODISCARD utc_time> operator()(const sys_time<_Duration>& _Sys_time) const { return utc_clock::from_sys(_Sys_time); } }; @@ -2722,7 +2722,7 @@ namespace chrono { template <> struct clock_time_conversion { template - _NODISCARD auto operator()(const utc_time<_Duration>& _Utc_time) const { + _NODISCARD sys_time> operator()(const utc_time<_Duration>& _Utc_time) const { return utc_clock::to_sys(_Utc_time); } }; From de13954af61c4105221a30adc0eb4ef9797accfe Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Thu, 4 Mar 2021 23:53:01 -0800 Subject: [PATCH 20/23] renames and noexcept --- stl/inc/chrono | 4 ++-- stl/inc/xtzdb.h | 12 ++++++------ stl/src/msvcp_atomic_wait.src | 2 +- stl/src/tzdb.cpp | 16 ++++++++-------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 3b96cfec3f..3966f0cd8b 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2237,9 +2237,9 @@ namespace chrono { (_STD max)(_Current_size, _STD size(_Known_leap_seconds)) - _Pre_2018_count; size_t _Reg_post_2018_ls_size; // number of post-2018 LSs found in the registry - unique_ptr<_RegistryLeapSecondInfo[], decltype(&__std_decalloc_reg_leap_seconds)> _Reg_ls_data( + unique_ptr<__std_tzdb_registry_leap_info[], decltype(&__std_tzdb_delete_reg_leap_seconds)> _Reg_ls_data{ __std_tzdb_get_reg_leap_seconds(_Known_post_2018_ls_size, &_Reg_post_2018_ls_size), - &__std_decalloc_reg_leap_seconds); + &__std_tzdb_delete_reg_leap_seconds}; if (_Reg_post_2018_ls_size > _Known_post_2018_ls_size && !_Reg_ls_data) { _Xbad_alloc(); // registry has new data, but failed to allocate storage diff --git a/stl/inc/xtzdb.h b/stl/inc/xtzdb.h index 678c421447..3e8a95be48 100644 --- a/stl/inc/xtzdb.h +++ b/stl/inc/xtzdb.h @@ -18,7 +18,7 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new -struct _RegistryLeapSecondInfo { +struct __std_tzdb_registry_leap_info { uint16_t _Year; uint16_t _Month; uint16_t _Day; @@ -29,13 +29,13 @@ struct _RegistryLeapSecondInfo { _EXTERN_C -_RegistryLeapSecondInfo* __stdcall __std_tzdb_get_reg_leap_seconds( - size_t _Prev_reg_ls_size, size_t* _Current_reg_ls_size); +__std_tzdb_registry_leap_info* __stdcall __std_tzdb_get_reg_leap_seconds( + size_t _Prev_reg_ls_size, size_t* _Current_reg_ls_size) noexcept; -void __stdcall __std_decalloc_reg_leap_seconds(_RegistryLeapSecondInfo* _Rlsi); +void __stdcall __std_tzdb_delete_reg_leap_seconds(__std_tzdb_registry_leap_info* _Rlsi) noexcept; -_NODISCARD void* __stdcall __std_calloc_crt(size_t _Count, size_t _Size); -void __stdcall __std_free_crt(void* _Ptr); +_NODISCARD void* __stdcall __std_calloc_crt(size_t _Count, size_t _Size) noexcept; +void __stdcall __std_free_crt(void* _Ptr) noexcept; _END_EXTERN_C diff --git a/stl/src/msvcp_atomic_wait.src b/stl/src/msvcp_atomic_wait.src index 62c5e88092..2c7b1b76b3 100644 --- a/stl/src/msvcp_atomic_wait.src +++ b/stl/src/msvcp_atomic_wait.src @@ -23,7 +23,7 @@ EXPORTS __std_calloc_crt __std_close_threadpool_work __std_create_threadpool_work - __std_decalloc_reg_leap_seconds + __std_tzdb_delete_reg_leap_seconds __std_execution_wait_on_uchar __std_execution_wake_by_address_all __std_free_crt diff --git a/stl/src/tzdb.cpp b/stl/src/tzdb.cpp index 39d02d65df..a43325c820 100644 --- a/stl/src/tzdb.cpp +++ b/stl/src/tzdb.cpp @@ -10,8 +10,8 @@ _EXTERN_C -_RegistryLeapSecondInfo* __stdcall __std_tzdb_get_reg_leap_seconds( - const size_t prev_reg_ls_size, size_t* current_reg_ls_size) { +__std_tzdb_registry_leap_info* __stdcall __std_tzdb_get_reg_leap_seconds( + const size_t prev_reg_ls_size, size_t* const current_reg_ls_size) noexcept { // On exit--- // *current_reg_ls_size <= prev_reg_ls_size, *reg_ls_data == nullptr --> no new data // *current_reg_ls_size > prev_reg_ls_size, *reg_ls_data != nullptr --> new data, successfully read @@ -31,14 +31,14 @@ _RegistryLeapSecondInfo* __stdcall __std_tzdb_get_reg_leap_seconds( DWORD byte_size = 0; status = RegQueryValueExW(leap_sec_key, reg_subkey_name, nullptr, nullptr, nullptr, &byte_size); - static_assert(sizeof(_RegistryLeapSecondInfo) == 12); + static_assert(sizeof(__std_tzdb_registry_leap_info) == 12); const auto ls_size = byte_size / 12; *current_reg_ls_size = ls_size; - _RegistryLeapSecondInfo* reg_ls_data = nullptr; + __std_tzdb_registry_leap_info* reg_ls_data = nullptr; if ((status == ERROR_SUCCESS || status == ERROR_MORE_DATA) && ls_size > prev_reg_ls_size) { try { - reg_ls_data = new _RegistryLeapSecondInfo[ls_size]; + reg_ls_data = new __std_tzdb_registry_leap_info[ls_size]; status = RegQueryValueExW( leap_sec_key, reg_subkey_name, nullptr, nullptr, reinterpret_cast(reg_ls_data), &byte_size); if (status != ERROR_SUCCESS) { @@ -56,15 +56,15 @@ _RegistryLeapSecondInfo* __stdcall __std_tzdb_get_reg_leap_seconds( return reg_ls_data; } -void __stdcall __std_decalloc_reg_leap_seconds(_RegistryLeapSecondInfo* _Rlsi) { +void __stdcall __std_tzdb_delete_reg_leap_seconds(__std_tzdb_registry_leap_info* _Rlsi) noexcept { delete[] _Rlsi; } -_NODISCARD void* __stdcall __std_calloc_crt(const size_t count, const size_t size) { +_NODISCARD void* __stdcall __std_calloc_crt(const size_t count, const size_t size) noexcept { return _calloc_crt(count, size); } -void __stdcall __std_free_crt(void* p) { +void __stdcall __std_free_crt(void* p) noexcept { _free_crt(p); } From 3b013dedf37e67de31bcbac644a6b33fe1335aff Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Thu, 4 Mar 2021 23:53:24 -0800 Subject: [PATCH 21/23] misc review feedback --- stl/inc/chrono | 2 +- stl/inc/filesystem | 4 ++-- stl/inc/xtzdb.h | 6 ++++-- stl/src/tzdb.cpp | 10 +++++----- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 3966f0cd8b..1ccb6b93ca 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2678,7 +2678,7 @@ namespace chrono { // STRUCT TEMPLATE clock_time_conversion template - struct clock_time_conversion; + struct clock_time_conversion {}; // [time.clock.cast.id] diff --git a/stl/inc/filesystem b/stl/inc/filesystem index 90ea82c2bf..a830963f15 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -2105,9 +2105,9 @@ namespace filesystem { // ALIAS file_time_type #if _HAS_CXX20 - using file_time_type = chrono::time_point; + using file_time_type = _CHRONO time_point<_CHRONO file_clock>; #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv - using file_time_type = chrono::time_point; + using file_time_type = _CHRONO time_point; #endif // ^^^ !_HAS_CXX20 // CLASS directory_entry diff --git a/stl/inc/xtzdb.h b/stl/inc/xtzdb.h index 3e8a95be48..734d050ecf 100644 --- a/stl/inc/xtzdb.h +++ b/stl/inc/xtzdb.h @@ -1,3 +1,5 @@ +// xtzdb.h internal header + // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -45,8 +47,8 @@ template class _Crt_allocator { public: using value_type = _Ty; - using propagate_on_container_move_assignment = _STD true_type; - using is_always_equal = _STD true_type; + using propagate_on_container_move_assignment = true_type; + using is_always_equal = true_type; constexpr _Crt_allocator() noexcept = default; diff --git a/stl/src/tzdb.cpp b/stl/src/tzdb.cpp index a43325c820..f49ff2ff79 100644 --- a/stl/src/tzdb.cpp +++ b/stl/src/tzdb.cpp @@ -13,15 +13,15 @@ _EXTERN_C __std_tzdb_registry_leap_info* __stdcall __std_tzdb_get_reg_leap_seconds( const size_t prev_reg_ls_size, size_t* const current_reg_ls_size) noexcept { // On exit--- - // *current_reg_ls_size <= prev_reg_ls_size, *reg_ls_data == nullptr --> no new data - // *current_reg_ls_size > prev_reg_ls_size, *reg_ls_data != nullptr --> new data, successfully read - // *current_reg_ls_size == 0, *reg_ls_data != nullptr --> new data, failed reading - // *current_reg_ls_size > prev_reg_ls_size, *reg_ls_data == nullptr --> new data, failed allocation + // *current_reg_ls_size <= prev_reg_ls_size, reg_ls_data == nullptr --> no new data + // *current_reg_ls_size > prev_reg_ls_size, reg_ls_data != nullptr --> new data, successfully read + // *current_reg_ls_size == 0, reg_ls_data != nullptr --> new data, failed reading + // *current_reg_ls_size > prev_reg_ls_size, reg_ls_data == nullptr --> new data, failed allocation constexpr auto reg_key_name = LR"(SYSTEM\CurrentControlSet\Control\LeapSecondInformation)"; constexpr auto reg_subkey_name = L"LeapSeconds"; *current_reg_ls_size = 0; - HKEY leap_sec_key = 0; + HKEY leap_sec_key = nullptr; LSTATUS status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_key_name, 0, KEY_READ, &leap_sec_key); if (status != ERROR_SUCCESS) { From fc5224ccf72e7db6c9448acea3c8e600b93b6b7c Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Fri, 5 Mar 2021 13:25:41 -0800 Subject: [PATCH 22/23] clang-format --- stl/inc/chrono | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 1ccb6b93ca..1fe48da272 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2175,7 +2175,7 @@ namespace chrono { #ifdef __cpp_lib_concepts // clang-format off template - requires three_way_comparable_with> + requires three_way_comparable_with> _NODISCARD constexpr auto operator<=>( const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { // clang-format on From 53e0d8cb72cc048d24255b429e651e371b52525d Mon Sep 17 00:00:00 2001 From: Miya Natsuhara Date: Mon, 8 Mar 2021 16:50:47 -0800 Subject: [PATCH 23/23] addressing review comments on test coverage --- .../test.cpp | 144 +++++++++++------- 1 file changed, 88 insertions(+), 56 deletions(-) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp index 75417402b0..cc8b5a8096 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp @@ -5,7 +5,9 @@ #include #include #include +#include #include +#include #include using namespace std; @@ -96,20 +98,26 @@ void test_is_leap_second(const year_month_day& ymd) { assert(get_leap_second_info(utc_clock::from_sys(ls) + days{1}).is_leap_second); } -void test_leap_second() { +constexpr bool test_leap_second() { constexpr int jun_leap_second_years[] = {1972, 1981, 1982, 1983, 1985, 1992, 1993, 1994, 1997, 2012, 2015}; constexpr int dec_leap_second_years[] = { 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1987, 1989, 1990, 1995, 1998, 2005, 2008, 2016}; static_assert(size(jun_leap_second_years) + size(dec_leap_second_years) == 27); - for (const auto& ls_year : jun_leap_second_years) { - test_is_leap_second(30d / June / year{ls_year}); - } - for (const auto& ls_year : dec_leap_second_years) { - test_is_leap_second(31d / December / year{ls_year}); + if (!is_constant_evaluated()) { + for (const auto& ls_year : jun_leap_second_years) { + test_is_leap_second(30d / June / year{ls_year}); + } + for (const auto& ls_year : dec_leap_second_years) { + test_is_leap_second(31d / December / year{ls_year}); + } } constexpr leap_second leap{sys_seconds{42s}, true, 0s}; + leap_second leap2(leap); // copy construct + leap_second leap3{sys_seconds{41s}, true, 0s}; + leap3 = leap2; // copy assign + constexpr sys_seconds smaller{41s}; constexpr sys_seconds equal{42s}; constexpr sys_seconds larger{43s}; @@ -168,6 +176,8 @@ void test_leap_second() { static_assert(leap_second{sys_seconds{42s}, true, 0s}.date() == sys_seconds{42s}); static_assert(leap_second{sys_seconds{42s}, true, 0s}.value() == 1s); static_assert(leap_second{sys_seconds{42s}, false, 0s}.value() == -1s); + + return true; } constexpr bool operator==(const leap_second_info& lhs, const leap_second_info& rhs) { @@ -222,6 +232,8 @@ void test_utc_clock_to_sys(const leap_second& leap) { template void test_file_clock_from_utc(const leap_second& leap) { + static_assert(is_same_v); + const auto file_leap = clock_cast(leap.date()); auto u = utc_clock::from_sys(leap.date() - Duration{1}); // just before leap second @@ -238,53 +250,6 @@ void test_file_clock_from_utc(const leap_second& leap) { assert(file_clock::to_utc(file_clock::from_utc(u)) == u); // just after } -void test_clock_cast() { - sys_days st(2020y / January / 1); - const auto ut = utc_clock::from_sys(st); - const auto tt = tai_clock::from_utc(ut); - const auto gt = gps_clock::from_utc(ut); - const auto ft = file_clock::from_utc(ut); - - assert(clock_cast(ut) == ut); - assert(clock_cast(st) == ut); - assert(clock_cast(tt) == ut); - assert(clock_cast(gt) == ut); - assert(clock_cast(ft) == ut); - - assert(clock_cast(ut) == st); - assert(clock_cast(st) == st); - assert(clock_cast(tt) == st); - assert(clock_cast(gt) == st); - assert(clock_cast(ft) == st); - - assert(clock_cast(ut) == tt); - assert(clock_cast(st) == tt); - assert(clock_cast(tt) == tt); - assert(clock_cast(gt) == tt); - assert(clock_cast(ft) == tt); - - assert(clock_cast(ut) == gt); - assert(clock_cast(st) == gt); - assert(clock_cast(tt) == gt); - assert(clock_cast(gt) == gt); - assert(clock_cast(ft) == gt); - - assert(clock_cast(ut) == ft); - assert(clock_cast(st) == ft); - assert(clock_cast(tt) == ft); - assert(clock_cast(gt) == ft); - assert(clock_cast(ft) == ft); - - // [time.clock.utc.overview]/1 Example 1 - assert(clock_cast(sys_seconds{sys_days{1970y / January / 1}}).time_since_epoch() == 0s); - assert(clock_cast(sys_seconds{sys_days{2000y / January / 1}}).time_since_epoch() == 946'684'822s); -} - -static_assert(is_clock_v); -static_assert(is_clock_v); -static_assert(is_clock_v); -static_assert(is_clock_v); - void test_utc_clock_from_sys(const leap_second& leap, seconds offset) { // Generalized from [time.clock.utc.members]/3 Example 1. auto t = leap.date() - 2ns; @@ -318,6 +283,7 @@ void test_file_clock_to_utc(const leap_second& leap, seconds offset) { if (leap > file_time_cutoff) { offset = 27s; } + offset -= duration_cast(file_clock::duration{filesystem::__std_fs_file_time_epoch_adjustment}); auto u = file_clock::to_utc(t); @@ -329,8 +295,10 @@ void test_file_clock_to_utc(const leap_second& leap, seconds offset) { t += 1us; u = file_clock::to_utc(t); - if (leap.date() <= file_time_cutoff) + if (leap.date() <= file_time_cutoff) { offset += leap.value(); + } + assert(u.time_since_epoch() - t.time_since_epoch() == offset); t += 1us; @@ -349,13 +317,77 @@ void test_gps_tai_clocks_utc() { assert(gps_clock::from_utc(gps_epoch) == gps_seconds{0s}); } +void test_clock_now() { + auto sys_now = system_clock::now(); + auto utc_now = utc_clock::now(); + auto tai_now = tai_clock::now(); + auto gps_now = gps_clock::now(); + auto file_now = file_clock::now(); + + static_assert(is_same_v, decltype(sys_now)>); + static_assert(is_same_v, decltype(utc_now)>); + static_assert(is_same_v, decltype(tai_now)>); + static_assert(is_same_v, decltype(gps_now)>); + static_assert(is_same_v, decltype(file_now)>); +} + +void test_clock_cast() { + sys_days st(2020y / January / 1); + const auto ut = utc_clock::from_sys(st); + const auto tt = tai_clock::from_utc(ut); + const auto gt = gps_clock::from_utc(ut); + const auto ft = file_clock::from_utc(ut); + + assert(clock_cast(ut) == ut); + assert(clock_cast(st) == ut); + assert(clock_cast(tt) == ut); + assert(clock_cast(gt) == ut); + assert(clock_cast(ft) == ut); + + assert(clock_cast(ut) == st); + assert(clock_cast(st) == st); + assert(clock_cast(tt) == st); + assert(clock_cast(gt) == st); + assert(clock_cast(ft) == st); + + assert(clock_cast(ut) == tt); + assert(clock_cast(st) == tt); + assert(clock_cast(tt) == tt); + assert(clock_cast(gt) == tt); + assert(clock_cast(ft) == tt); + + assert(clock_cast(ut) == gt); + assert(clock_cast(st) == gt); + assert(clock_cast(tt) == gt); + assert(clock_cast(gt) == gt); + assert(clock_cast(ft) == gt); + + assert(clock_cast(ut) == ft); + assert(clock_cast(st) == ft); + assert(clock_cast(tt) == ft); + assert(clock_cast(gt) == ft); + assert(clock_cast(ft) == ft); + + // [time.clock.utc.overview]/1 Example 1 + assert(clock_cast(sys_seconds{sys_days{1970y / January / 1}}).time_since_epoch() == 0s); + assert(clock_cast(sys_seconds{sys_days{2000y / January / 1}}).time_since_epoch() == 946'684'822s); +} + +static_assert(is_clock_v); +static_assert(is_clock_v); +static_assert(is_clock_v); +static_assert(is_clock_v); + int main() { - test_leap_second(); + assert(test_leap_second()); + static_assert(test_leap_second()); - // This is the only time duration a leap second insertion that can be represented by a duration. + // This is the only date/time of a leap second insertion that can be represented by a duration. assert(utc_clock::to_sys(utc_time>{duration{78796800.0f}}).time_since_epoch().count() == nextafter(78796800.0f, 0.0f)); + test_clock_now(); + seconds offset{0}; for (const auto& leap : get_tzdb().leap_seconds) { test_leap_second_info(leap, offset);