On Mon, Jul 20, 2026 at 12:49 PM Tomasz Kaminski <[email protected]> wrote:
> > > On Mon, Jul 20, 2026 at 12:41 PM Jonathan Wakely <[email protected]> > wrote: > >> On Mon, 20 Jul 2026 at 11:16, Jonathan Wakely <[email protected]> wrote: >> > >> > On Mon, 20 Jul 2026 at 10:54 +0200, Tomasz Kamiński wrote: >> > >From: Jonathan Wakely <[email protected]> >> > > >> > >This change allows the hardcoded list of leap seconds in <chrono> to be >> > >used even when the program is executing after the hardcoded expiry date >> > >in that header. >> > > >> > >For times after the hardcoded expiry, the inline __get_leap_second_info >> > >function calls a new library function which compares the number of >> > >hardcoded leap seconds in the header with the number of leap seconds >> > >defined in the tzdata leapseconds file (usually provided by the OS). >> > > >> > >There are three leap second lists that are relevant here. The first is >> > >the hardcoded list (and its expiry time) in the <chrono> header. That >> is >> > >fixed when the user code is compiled, and might be out of date by the >> > >time the application runs. The second list (and its expiry time) is >> > >hardcoded in tzdb.cc in the libstdc++.so library. If the application >> > >uses a newer libstdc++.so at runtime than the <chrono> header used at >> > >compile time, we can still avoid going to the filesystem for dates >> > >within the expiry time of the list in libstdc++.so. The third list is >> > >the most up-to-date one which is read from the leapseconds file. >> > > >> > >The code added by this commit tries to avoid reading the file when >> > >possible (because that's slower than using the in-memory lists), and if >> > >it does have to go to the file system, it tries to avoid doing so again >> > >next time the leap seconds are needed. The later is handled by storing >> > >> > "by storing the number" >> > >> > >of recently know leaps seconds in num_leap_seconds of struct >> > >NumLeapSeconds, which decides whether to use std::atomic_ref<unsigned> >> > >or perform all accesses while holding a lock on the pre-existing mutex >> > >used for the tzdb_list singleton. To set_locked/set functions are used >> > >> > "Two" >> > >> > >to differentiate #if/#else branches of _Node::_S_replace_head that >> > >already held corresponding mutext and use atomic respectivelly. >> > >> > "mutex" >> > "atomics" >> > "respectively" >> > >> > > >> > >libstdc++-v3/ChangeLog: >> > > >> > > PR libstdc++/123165 >> > > * acinclude.m4 (libtool_VERSION): Bump version. >> > > * config/abi/pre/gnu.ver (GLIBCXX_3.4.36): Add new symbol >> > > version and export new symbol. >> > > * configure: Regenerate. >> > > * include/std/chrono (__detail::__recent_leap_second_info): >> > > Declare new function and make it a friend of varous classes. >> > > (leap_second): Make private constructor constexpr. Remove friend >> > > declaration for get_leap_second_info. >> > > (__detail::__get_leap_second_info): Use new function for times >> > > past the hardcoded expiry. >> > > * src/c++20/tzdb.cc (tzdb_list::_Node::fixed_leaps): Move array >> > > of leap seconds here from _S_read_leap_seconds. >> > > (_Node::NumLeapSeconds): New class. >> > > (_Node::num_leap_seconds): New static variable. >> > > (__detail::__recent_leap_second_info): Define new function. >> > > (tzdb_list::_Node::_S_read_leap_seconds): Populate vector from >> > > _Node::fixed_leaps. Rename bool variable to clarify meaning. >> > > (tzdb_list::_Node::_S_replace_head): Update num_leap_seconds >> > > when updating the tzdb_list. >> > > * testsuite/util/testsuite_abi.cc: Update known_versions and >> > > latestp. >> > > * testsuite/std/time/clock/utc/leap_second_info-2.cc: New test. >> > > >> > >Co-authored-by: Tomasz Kamiński <[email protected]> >> > >Signed-off-by: Tomasz Kamiński <[email protected]> >> > >(cherry picked from commit e79f0f818c0e42d0d84c90a54684dfb269ac388c) >> > >(cherry picked from commit b12fdd952511786bf8d285e7080e94deeb32aa1d) >> > >(cherry picked from commit adb4f4a064c009cd616106fa4c648182c18f96d9) >> > >(cherry picked from commit a7ad4bee6fe7cd77aceb6e86380dcaeb6da82791) >> > >--- >> > >As suggested by cherry-pick lines this squashes the original and >> > >tree follow-up patches. Please double check if I missed something. >> > >> > Thanks for squashing these, you got all the relevant commits. >> > >> > > >> > >I am landing this first as this does introduce >> > >6.36 version that my format patches uses. >> > > >> > >I have updated the commit description a bit, please check if you >> > >have any suggestions. >> > > >> > >Testing on x86_64-linux.. OK for gcc-16 when all test passes? >> > > >> > > libstdc++-v3/acinclude.m4 | 2 +- >> > > libstdc++-v3/config/abi/pre/gnu.ver | 7 + >> > > libstdc++-v3/configure | 2 +- >> > > libstdc++-v3/include/std/chrono | 104 +++---- >> > > libstdc++-v3/src/c++20/tzdb.cc | 294 +++++++++++++++--- >> > > .../std/time/clock/utc/leap_second_info-2.cc | 108 +++++++ >> > > libstdc++-v3/testsuite/util/testsuite_abi.cc | 3 +- >> > > 7 files changed, 420 insertions(+), 100 deletions(-) >> > > create mode 100644 >> libstdc++-v3/testsuite/std/time/clock/utc/leap_second_info-2.cc >> > > >> > >diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 >> > >index e6e4f7b394a..a615692e7be 100644 >> > >--- a/libstdc++-v3/acinclude.m4 >> > >+++ b/libstdc++-v3/acinclude.m4 >> > >@@ -4096,7 +4096,7 @@ changequote([,])dnl >> > > fi >> > > >> > > # For libtool versioning info, format is CURRENT:REVISION:AGE >> > >-libtool_VERSION=6:35:0 >> > >+libtool_VERSION=6:36:0 >> > > >> > > # Everything parsed; figure out what files and settings to use. >> > > case $enable_symvers in >> > >diff --git a/libstdc++-v3/config/abi/pre/gnu.ver >> b/libstdc++-v3/config/abi/pre/gnu.ver >> > >index bd4da641829..06892cd2d18 100644 >> > >--- a/libstdc++-v3/config/abi/pre/gnu.ver >> > >+++ b/libstdc++-v3/config/abi/pre/gnu.ver >> > >@@ -2623,6 +2623,13 @@ GLIBCXX_3.4.35 { >> > > >> > > } GLIBCXX_3.4.34; >> > > >> > >+# GCC 17.1.0 >> > >> > This needs to be 16.2.0 (I see you've fixed this locally now). >> > >> > And we need a follow-up patch on trunk to bump the version to 3.4.37 >> > and move the newer symbols (allocate_at_least etc.) to the new >> > version. Maybe that should happen first, so that when the gcc-16 >> > branch is changed, it matches what's already on trunk. >> >> I've just approved your patch to do exactly that on trunk, so this one >> is OK for gcc-16 now (with the commit msg typos fixed), thanks. >> > OK, will merge the trunk patch first. Then this patch to GCC-16 > (after the full test suite finishes), and after that my format changes. > If this change allows them to apply cleanly, I will not send new revision. > The patch adding GLIBCXX_3.4.37 was merged to trunk and this patch was pushed to GCC-16 release branch. > >> >> > >> > >> > >+GLIBCXX_3.4.36 { >> > >+ >> > >+ >> _ZNSt6chrono8__detail25__recent_leap_second_infoERNS_16leap_second_infoEj; >> > >+ >> > >+} GLIBCXX_3.4.35; >> > >+ >> > > # Symbols in the support library (libsupc++) have their own tag. >> > > CXXABI_1.3 { >> > > >> > >diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure >> > >index 359545c4bce..bc9dce5e288 100755 >> > >--- a/libstdc++-v3/configure >> > >+++ b/libstdc++-v3/configure >> > >@@ -51429,7 +51429,7 @@ $as_echo "$as_me: WARNING: === Symbol >> versioning will be disabled." >&2;} >> > > fi >> > > >> > > # For libtool versioning info, format is CURRENT:REVISION:AGE >> > >-libtool_VERSION=6:35:0 >> > >+libtool_VERSION=6:36:0 >> > > >> > > # Everything parsed; figure out what files and settings to use. >> > > case $enable_symvers in >> > >diff --git a/libstdc++-v3/include/std/chrono >> b/libstdc++-v3/include/std/chrono >> > >index 674f867dcdc..692fd6025e7 100644 >> > >--- a/libstdc++-v3/include/std/chrono >> > >+++ b/libstdc++-v3/include/std/chrono >> > >@@ -131,6 +131,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> > > leap_second_info >> > > get_leap_second_info(const utc_time<_Duration>& __ut); >> > > >> > >+ /// @cond undocumented >> > >+ namespace __detail >> > >+ { >> > >+ bool __recent_leap_second_info(leap_second_info&, unsigned); >> > >+ } >> > >+ /// @endcond >> > >+ >> > > /** A clock that measures Universal Coordinated Time (UTC). >> > > * >> > > * The epoch is 1970-01-01 00:00:00. >> > >@@ -2818,6 +2825,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> > > friend class leap_second; >> > > friend struct time_zone::_Impl; >> > > friend class time_zone_link; >> > >+ >> > >+ friend bool >> > >+ __detail::__recent_leap_second_info(leap_second_info&, >> unsigned); >> > > }; >> > > >> > > class time_zone_link >> > >@@ -2943,16 +2953,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> > > { return __x.date() <=> __y; } >> > > >> > > private: >> > >- explicit leap_second(seconds::rep __s) : _M_s(__s) { } >> > >+ constexpr explicit leap_second(seconds::rep __s) : _M_s(__s) { } >> > > >> > > friend struct tzdb_list::_Node; >> > > >> > > friend const tzdb& reload_tzdb(); >> > > >> > >- template<typename _Duration> >> > >- friend leap_second_info >> > >- get_leap_second_info(const utc_time<_Duration>&); >> > >- >> > > seconds _M_s; // == date().time_since_epoch() * value().count() >> > > }; >> > > >> > >@@ -3217,6 +3223,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> > > >> > > namespace __detail >> > > { >> > >+ // This function is inline to support fast conversions between >> utc_time >> > >+ // and sys_time when possible, without requiring a chrono::tzdb >> object >> > >+ // to be constructed. >> > > inline leap_second_info >> > > __get_leap_second_info(sys_seconds __ss, bool __is_utc) >> > > { >> > >@@ -3252,65 +3261,56 @@ namespace __detail >> > > 1435708800, // 1 Jul 2015 >> > > 1483228800, // 1 Jan 2017 >> > > }; >> > >+ >> > >+ // The default result for times after the last leap year. >> > >+ constexpr leap_second_info __after_last{ >> > >+ .is_leap_second = false, >> > >+ .elapsed = seconds(std::size(__leaps)) >> > >+ }; >> > >+ >> > > // The list above is known to be valid until (at least) this >> date >> > > // and only contains positive leap seconds. >> > > constexpr sys_seconds __expires(1798416000s); // 2026-12-28 >> 00:00:00 UTC >> > > >> > >-#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI >> > > if (__ss > __expires) >> > > { >> > >- // Use updated leap_seconds from tzdb. >> > >- size_t __n = std::size(__leaps); >> > >- >> > >- auto __db = get_tzdb_list().begin(); >> > >- auto __first = __db->leap_seconds.begin() + __n; >> > >- auto __last = __db->leap_seconds.end(); >> > >- auto __pos = std::upper_bound(__first, __last, __ss); >> > >- seconds __elapsed(__n); >> > >- for (auto __i = __first; __i != __pos; ++__i) >> > >- __elapsed += __i->value(); >> > >- >> > >- if (__is_utc) >> > >- { >> > >- // Convert utc_time to sys_time: >> > >- __ss -= __elapsed; >> > >- // See if that sys_time is before (or during) previous >> leap sec: >> > >- if (__pos != __first && __ss < __pos[-1]) >> > >- { >> > >- if ((__ss + 1s) >= __pos[-1]) >> > >- return {true, __elapsed}; >> > >- __elapsed -= __pos[-1].value(); >> > >- } >> > >- } >> > >- return {false, __elapsed}; >> > >- } >> > >- else >> > >+#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI // use >> chrono::tzdb >> > >+ // Call into the library because it might have knowledge of >> new >> > >+ // leap seconds loaded at runtime from the tzdata files. >> > >+ >> > >+ // We misuse leap_second_info here to pass {bool, seconds} >> inputs: >> > >+ leap_second_info __info{ .is_leap_second = __is_utc, >> > >+ .elapsed = __ss.time_since_epoch() }; >> > >+ // If this returns true, then __info holds the output result: >> > >+ if (__detail::__recent_leap_second_info(__info, >> std::size(__leaps))) >> > >+ return __info; >> > > #endif >> > >- { >> > >- seconds::rep __s = __ss.time_since_epoch().count(); >> > >- const seconds::rep* __first = std::begin(__leaps); >> > >- const seconds::rep* __last = std::end(__leaps); >> > >+ return __after_last; >> > >+ } >> > > >> > >- // Don't bother searching the list if we're after the last >> one. >> > >- if (__s > (__last[-1] + (__last - __first) + 1)) >> > >- return { false, seconds(__last - __first) }; >> > >+ seconds::rep __s = __ss.time_since_epoch().count(); >> > >+ const seconds::rep* __first = std::begin(__leaps); >> > >+ const seconds::rep* __last = std::end(__leaps); >> > > >> > >- auto __pos = std::upper_bound(__first, __last, __s); >> > >- seconds __elapsed{__pos - __first}; >> > >- if (__is_utc) >> > >+ // Don't bother searching the list if we're after the last one. >> > >+ if (__s > (__last[-1] + (__last - __first) + 1)) >> > >+ return __after_last; >> > >+ >> > >+ auto __pos = std::upper_bound(__first, __last, __s); >> > >+ seconds __elapsed{__pos - __first}; >> > >+ if (__is_utc) >> > >+ { >> > >+ // Convert utc_time to sys_time: >> > >+ __s -= __elapsed.count(); >> > >+ // See if that sys_time is before (or during) previous leap >> sec: >> > >+ if (__pos != __first && __s < __pos[-1]) >> > > { >> > >- // Convert utc_time to sys_time: >> > >- __s -= __elapsed.count(); >> > >- // See if that sys_time is before (or during) previous >> leap sec: >> > >- if (__pos != __first && __s < __pos[-1]) >> > >- { >> > >- if ((__s + 1) >= __pos[-1]) >> > >- return {true, __elapsed}; >> > >- --__elapsed; >> > >- } >> > >+ if ((__s + 1) >= __pos[-1]) >> > >+ return {true, __elapsed}; >> > >+ --__elapsed; >> > > } >> > >- return {false, __elapsed}; >> > > } >> > >+ return {false, __elapsed}; >> > > } >> > > } // namespace __detail >> > > >> > >diff --git a/libstdc++-v3/src/c++20/tzdb.cc >> b/libstdc++-v3/src/c++20/tzdb.cc >> > >index b0fbfc46a6d..0013ad1877c 100644 >> > >--- a/libstdc++-v3/src/c++20/tzdb.cc >> > >+++ b/libstdc++-v3/src/c++20/tzdb.cc >> > >@@ -36,6 +36,7 @@ >> > > #include <memory> // atomic<shared_ptr<T>> >> > > #include <mutex> // mutex >> > > #include <iomanip> // quoted >> > >+#include <span> >> > > #if defined __GTHREADS && ! defined _GLIBCXX_HAS_GTHREADS >> > > # include <ext/concurrence.h> // __gnu_cxx::__mutex >> > > #endif >> > >@@ -66,6 +67,7 @@ >> > > #endif >> > > >> > > #if USE_ATOMIC_SHARED_PTR && ! USE_ATOMIC_LIST_HEAD >> > >+// Cannot use atomic<shared_ptr<T>> without lock-free atomic<T*>. >> > > # error Unsupported combination >> > > #endif >> > > >> > >@@ -196,6 +198,13 @@ namespace std::chrono >> > > static const tzdb& _S_replace_head(shared_ptr<_Node>, >> shared_ptr<_Node>); >> > > >> > > static pair<vector<leap_second>, bool> _S_read_leap_seconds(); >> > >+ >> > >+ // This is here because _Node is a friend so can call private >> constructor. >> > >+ static const leap_second fixed_leaps[]; >> > >+ >> > >+ // This is a member so that it can access fixed_leaps. >> > >+ struct NumLeapSeconds; >> > >+ static NumLeapSeconds num_leap_seconds; >> > > }; >> > > >> > > // Implementation of the private constructor used for the singleton >> object. >> > >@@ -1251,56 +1260,240 @@ namespace std::chrono >> > > } >> > > #endif // TZDB_DISABLED >> > > >> > >+// These are the same values as the array in the <chrono> header, but >> might >> > >+// contain additional leap seconds if the libstdc++.so used at >> runtime is >> > >+// newer than the <chrono> header used to compile parts of the >> application. >> > >+constexpr leap_second tzdb_list::_Node::fixed_leaps[] { >> > >+#define LS leap_second >> > >+ LS( 78796800), // 1 Jul 1972 >> > >+ LS( 94694400), // 1 Jan 1973 >> > >+ LS( 126230400), // 1 Jan 1974 >> > >+ LS( 157766400), // 1 Jan 1975 >> > >+ LS( 189302400), // 1 Jan 1976 >> > >+ LS( 220924800), // 1 Jan 1977 >> > >+ LS( 252460800), // 1 Jan 1978 >> > >+ LS( 283996800), // 1 Jan 1979 >> > >+ LS( 315532800), // 1 Jan 1980 >> > >+ LS( 362793600), // 1 Jul 1981 >> > >+ LS( 394329600), // 1 Jul 1982 >> > >+ LS( 425865600), // 1 Jul 1983 >> > >+ LS( 489024000), // 1 Jul 1985 >> > >+ LS( 567993600), // 1 Jan 1988 >> > >+ LS( 631152000), // 1 Jan 1990 >> > >+ LS( 662688000), // 1 Jan 1991 >> > >+ LS( 709948800), // 1 Jul 1992 >> > >+ LS( 741484800), // 1 Jul 1993 >> > >+ LS( 773020800), // 1 Jul 1994 >> > >+ LS( 820454400), // 1 Jan 1996 >> > >+ LS( 867715200), // 1 Jul 1997 >> > >+ LS( 915148800), // 1 Jan 1999 >> > >+ LS(1136073600), // 1 Jan 2006 >> > >+ LS(1230768000), // 1 Jan 2009 >> > >+ LS(1341100800), // 1 Jul 2012 >> > >+ LS(1435708800), // 1 Jul 2015 >> > >+ LS(1483228800), // 1 Jan 2017 >> > >+ // If new leap seconds get defined they should be added here. >> > >+ // Negative leap seconds are represented as -1 * timestamp. >> > >+#undef LS >> > >+}; >> > >+ >> > >+namespace >> > >+{ >> > >+ // The expiry date corresponding to the list above. >> > >+ // tzdata 2026a leapseconds list expires at 2026-12-28 00:00:00 UTC >> > >+ constexpr seconds fixed_expiry{1798416000u}; >> > >+} >> > >+ >> > >+// This holds the most up-to-date number of leap seconds known at >> runtime. >> > >+// Initially zero, updated when _S_read_leap_seconds() is called. >> > >+struct tzdb_list::_Node::NumLeapSeconds >> > >+{ >> > >+ // Called by __recent_leap_second_info to read num_leap_seconds. >> > >+ unsigned >> > >+ get() >> > >+ { >> > >+#if ATOMIC_INT_LOCK_FREE == 2 >> > >+ atomic_ref<unsigned> ref(count); >> > >+ auto num = ref.load(memory_order::relaxed); >> > >+ >> > >+ if (num == std::size(_Node::fixed_leaps)) >> > >+ // A leapseconds file has been read and has no new leap seconds. >> > >+ return num; >> > >+ >> > >+ if (num == 0) >> > >+ // No leapseconds file has been read yet. >> > >+ return 0; >> > >+ >> > >+ // The tzdb_list has been initialized and contains a tzdb object >> with >> > >+ // new leap seconds, which the caller is going to use. >> > >+ // The relaxed load above does not synchronize with anything, so >> to >> > >+ // ensure that the get_tzdb_list() in the caller will see a tzdb >> object >> > >+ // set by _S_replace_head, we load num_leap_seconds again with >> acquire >> > >+ // ordering: >> > >+ return ref.load(memory_order::acquire); >> > >+#else >> > >+ lock_guard<mutex> l(list_mutex()); // This ensures acquire >> ordering. >> > >+ return count; >> > >+#endif >> > >+ } >> > >+ >> > >+ // Called by __recent_leap_second_info to set num_leap_seconds when >> > >+ // we have determined there are no new leap seconds in a >> leapseconds file. >> > >+ void >> > >+ set_to_fixed_size() >> > >+ { >> > >+#if ATOMIC_INT_LOCK_FREE == 2 >> > >+ atomic_ref<unsigned> ref(count); >> > >+ unsigned expected = 0; >> > >+ ref.compare_exchange_strong(expected, >> std::size(_Node::fixed_leaps), >> > >+ memory_order::relaxed); >> > >+#else >> > >+ lock_guard<mutex> l(list_mutex()); >> > >+ if (count == 0) >> > >+ count = std::size(_Node::fixed_leaps); >> > >+#endif >> > >+ } >> > >+ >> > >+ // Called by _Node::_S_replace_head >> > >+ // The two versions are named differently so that caller has to be >> explicit >> > >+ // about which version it calls, based on whether the mutex is held. >> > >+ void >> > >+ set(unsigned val) >> > >+ { >> > >+#if ATOMIC_INT_LOCK_FREE == 2 >> > >+ atomic_ref<unsigned> ref(count); >> > >+ // The release op here synchronizes with the acquire op in get(). >> > >+ ref.store(val, memory_order::release); >> > >+#else >> > >+ lock_guard<mutex> l(list_mutex()); >> > >+ count = val; >> > >+#endif >> > >+ } >> > >+ >> > >+ void >> > >+ set_locked(unsigned val, const lock_guard<mutex>&) >> > >+ { >> > >+#if ATOMIC_INT_LOCK_FREE == 2 >> > >+ // Even though the caller locked the mutex, we still need to use >> an >> > >+ // atomic store in this case, because there could be concurrent >> loads. >> > >+ set(val); >> > >+#else >> > >+ // The only caller of this function locks list_mutex() so we would >> > >+ // deadlock if we locked it again here. >> > >+ count = val; >> > >+#endif >> > >+ } >> > >+ >> > >+private: >> > >+ unsigned count = 0; >> > >+}; >> > >+ >> > >+constinit tzdb_list::_Node::NumLeapSeconds >> tzdb_list::_Node::num_leap_seconds; >> > >+ >> > >+ namespace __detail >> > >+ { >> > >+ // Called by chrono::__detail::__get_leap_second_info in <chrono> >> > >+ // to get leap_second_info for times after the expiry date in the >> header. >> > >+ // The caller provides the time being queried in `info.elapsed` >> and >> > >+ // whether that is a UTC time in `info.is_leap_second`. >> > >+ // If it returns true, this function did the lookup and updated >> `info`. >> > >+ // If this returns false, it means the hardcoded list of leap >> seconds >> > >+ // in the header should be used for the lookup. >> > >+ bool >> > >+ __recent_leap_second_info(leap_second_info& info, >> > >+ unsigned num_positive_leaps) >> > >+ { >> > >+ // Extract the input args from info: >> > >+ const auto [is_utc, secs] = info; >> > >+ // And then reuse info for the output result: >> > >+ info.is_leap_second = false; >> > >+ info.elapsed = seconds(num_positive_leaps); >> > >+ >> > >+ auto update_info = [&](span<const leap_second> leaps) >> > >+ { >> > >+ if (leaps.size() == num_positive_leaps) >> > >+ return false; // No new leap seconds, use the array in the >> header. >> > >+ >> > >+ // info.elapsed already contains the first N leap seconds, >> > >+ // so we only search the end of the span. >> > >+ auto first = leaps.begin() + num_positive_leaps; >> > >+ auto pos = std::upper_bound(first, leaps.end(), >> sys_seconds(secs)); >> > >+ for (auto i = first; i != pos; ++i) >> > >+ info.elapsed += i->value(); >> > >+ >> > >+ if (is_utc) >> > >+ { >> > >+ // This should never happen, but check it so that pos[-1] >> is valid: >> > >+ if (num_positive_leaps == 0) [[unlikely]] >> > >+ return false; >> > >+ >> > >+ // Convert utc_time to sys_time: >> > >+ sys_seconds ss(secs - info.elapsed); >> > >+ // See if that sys_time is before (or during) previous leap >> sec: >> > >+ if (ss < pos[-1]) >> > >+ { >> > >+ if ((ss + 1s) >= pos[-1]) >> > >+ info.is_leap_second = true; >> > >+ else >> > >+ info.elapsed -= pos[-1].value(); >> > >+ } >> > >+ } >> > >+ return true; >> > >+ }; >> > >+ >> > >+ using _Node = tzdb_list::_Node; >> > >+ >> > >+ // If the caller was compiled using an older GCC with an older >> expiry >> > >+ // time in the header than the `fixed_expiry` defined above, we >> might >> > >+ // be able to answer the query easily using the static >> `fixed_leaps`. >> > >+ if (secs <= fixed_expiry) >> > >+ return update_info(_Node::fixed_leaps); >> > >+ >> > >+ constexpr auto num_fixed_leaps = std::size(_Node::fixed_leaps); >> > >+ >> > >+ auto num_leaps = _Node::num_leap_seconds.get(); >> > >+ if (num_leaps == num_fixed_leaps) >> > >+ // A leapseconds file has been read and has no new leap seconds: >> > >+ return update_info(_Node::fixed_leaps); >> > >+ else if (num_leaps == 0) >> > >+ { >> > >+ // The tzdb_list has not been initialized yet, so we don't >> know >> > >+ // the correct number of leap seconds. >> > >+ // We use _S_read_leap_seconds() to read the leapseconds file. >> > >+ // If that tells us there are no new leapseconds, we don't >> need >> > >+ // to parse all of tzdata.zi and initialize a whole tzdb >> object. >> > >+ if (_Node::_S_read_leap_seconds().first.size() == >> num_fixed_leaps) >> > >+ { >> > >+ // There are no new leap seconds. Remember that so that >> the next >> > >+ // call to this function can just use fixed_leaps. >> > >+ _Node::num_leap_seconds.set_to_fixed_size(); >> > >+ return update_info(_Node::fixed_leaps); >> > >+ } >> > >+ // else there are new leap seconds. We init tzdb_list so that >> the >> > >+ // new leap seconds are persisted in a tzdb object. >> > >+ } >> > >+ >> > >+ // Use updated leap_seconds from tzdb. >> > >+ return update_info(get_tzdb_list().begin()->leap_seconds); >> > >+ } >> > >+ } >> > >+ >> > > // Return leap_second values, and a bool indicating whether the >> values are >> > > // current (true), or potentially out of date (false). >> > > pair<vector<leap_second>, bool> >> > > tzdb_list::_Node::_S_read_leap_seconds() >> > > { >> > >- // This list is valid until at least 2026-12-28 00:00:00 UTC. >> > >- constexpr auto expires = sys_days{2026y/12/28}; >> > >- vector<leap_second> leaps >> > >- { >> > >- (leap_second) 78796800, // 1 Jul 1972 >> > >- (leap_second) 94694400, // 1 Jan 1973 >> > >- (leap_second) 126230400, // 1 Jan 1974 >> > >- (leap_second) 157766400, // 1 Jan 1975 >> > >- (leap_second) 189302400, // 1 Jan 1976 >> > >- (leap_second) 220924800, // 1 Jan 1977 >> > >- (leap_second) 252460800, // 1 Jan 1978 >> > >- (leap_second) 283996800, // 1 Jan 1979 >> > >- (leap_second) 315532800, // 1 Jan 1980 >> > >- (leap_second) 362793600, // 1 Jul 1981 >> > >- (leap_second) 394329600, // 1 Jul 1982 >> > >- (leap_second) 425865600, // 1 Jul 1983 >> > >- (leap_second) 489024000, // 1 Jul 1985 >> > >- (leap_second) 567993600, // 1 Jan 1988 >> > >- (leap_second) 631152000, // 1 Jan 1990 >> > >- (leap_second) 662688000, // 1 Jan 1991 >> > >- (leap_second) 709948800, // 1 Jul 1992 >> > >- (leap_second) 741484800, // 1 Jul 1993 >> > >- (leap_second) 773020800, // 1 Jul 1994 >> > >- (leap_second) 820454400, // 1 Jan 1996 >> > >- (leap_second) 867715200, // 1 Jul 1997 >> > >- (leap_second) 915148800, // 1 Jan 1999 >> > >- (leap_second)1136073600, // 1 Jan 2006 >> > >- (leap_second)1230768000, // 1 Jan 2009 >> > >- (leap_second)1341100800, // 1 Jul 2012 >> > >- (leap_second)1435708800, // 1 Jul 2015 >> > >- (leap_second)1483228800, // 1 Jan 2017 >> > >- }; >> > >+ // Populate the vector with the leap seconds we already know >> about: >> > >+ vector<leap_second> leaps(fixed_leaps, std::end(fixed_leaps)); >> > > >> > >-#if 0 >> > >- // This optimization isn't valid if the file has additional leap >> seconds >> > >- // defined since the library was compiled, but the system clock >> has been >> > >- // set to a time before the hardcoded expiration date. >> > >- if (system_clock::now() < expires) >> > >- return {std::move(leaps), true}; >> > >-#endif >> > >+ bool read_leaps_file = false; >> > > >> > > #ifndef TZDB_DISABLED >> > > if (ifstream ls{zoneinfo_file(leaps_file)}) >> > > { >> > >- auto exp_year = year_month_day(expires).year(); >> > >+ constexpr year exp_year >> > >+ = >> year_month_day(sys_days(duration_cast<days>(fixed_expiry))).year(); >> > >+ >> > > std::string s, w; >> > > s.reserve(80); // Avoid later reallocations. >> > > while (std::getline(ls, s)) >> > >@@ -1309,6 +1502,7 @@ namespace std::chrono >> > > >> > > if (!s.starts_with("Leap")) >> > > continue; >> > >+ >> > > istringstream li(std::move(s)); >> > > li.exceptions(ios::failbit); >> > > li.ignore(4); >> > >@@ -1339,12 +1533,14 @@ namespace std::chrono >> > > leaps.push_back(ls); >> > > } >> > > } >> > >- s = std::move(li).str(); // return storage to s >> > >+ s = std::move(li).str(); // give allocated storage back to s >> > > } >> > >- return {std::move(leaps), true}; >> > >+ >> > >+ read_leaps_file = true; >> > > } >> > > #endif >> > >- return {std::move(leaps), false}; >> > >+ >> > >+ return {std::move(leaps), read_leaps_file}; >> > > } >> > > >> > > #ifndef TZDB_DISABLED >> > >@@ -1402,8 +1598,13 @@ namespace std::chrono >> > > new_head_ptr->next = curr; >> > > } >> > > // XXX small window here where _S_head_cache still points to >> previous tzdb. >> > >+ _S_cache_list_head(new_head_ptr); >> > >+ >> > >+ // This allows __recent_leap_second_info() to know that it can use >> > >+ // get_tzdb_list()->begin()->leap_seconds to get new leap seconds. >> > >+ num_leap_seconds.set(new_head_ptr->db.leap_seconds.size()); >> > > #else >> > >- lock_guard<mutex> l(list_mutex()); >> > >+ lock_guard<mutex> lock(list_mutex()); >> > > if (const _Node* h = _S_head_owner.get()) >> > > { >> > > if (h->db.version == new_head_ptr->db.version) >> > >@@ -1411,8 +1612,11 @@ namespace std::chrono >> > > new_head_ptr->next = _S_head_owner; >> > > } >> > > _S_head_owner = std::move(new_head); >> > >-#endif >> > > _S_cache_list_head(new_head_ptr); >> > >+ >> > >+ num_leap_seconds.set_locked(new_head_ptr->db.leap_seconds.size(), >> lock); >> > >+#endif >> > >+ >> > > return new_head_ptr->db; >> > > } >> > > >> > >diff --git >> a/libstdc++-v3/testsuite/std/time/clock/utc/leap_second_info-2.cc >> b/libstdc++-v3/testsuite/std/time/clock/utc/leap_second_info-2.cc >> > >new file mode 100644 >> > >index 00000000000..090a28291c8 >> > >--- /dev/null >> > >+++ b/libstdc++-v3/testsuite/std/time/clock/utc/leap_second_info-2.cc >> > >@@ -0,0 +1,108 @@ >> > >+// { dg-do run { target c++20 } } >> > >+// { dg-require-effective-target tzdb } >> > >+// { dg-require-effective-target cxx11_abi } >> > >+// { dg-xfail-run-if "no weak override on AIX" { powerpc-ibm-aix* } } >> > >+ >> > >+#include <chrono> >> > >+#include <fstream> >> > >+#include <testsuite_hooks.h> >> > >+ >> > >+using namespace std::chrono_literals; >> > >+ >> > >+static bool override_used = false; >> > >+ >> > >+namespace __gnu_cxx >> > >+{ >> > >+ const char* zoneinfo_dir_override() { >> > >+ override_used = true; >> > >+ return "./"; >> > >+ } >> > >+} >> > >+ >> > >+void >> > >+test_known_leaps() >> > >+{ >> > >+ // Test some values within the list of known leap seconds. >> > >+ auto s = std::chrono::utc_seconds(-1s); >> > >+ auto lsi = get_leap_second_info(s); >> > >+ VERIFY( lsi.is_leap_second == false ); >> > >+ VERIFY( lsi.elapsed == 0s ); >> > >+ >> > >+ s = std::chrono::utc_seconds(126230402s); // 1 Jan 1974 >> > >+ lsi = get_leap_second_info(s); >> > >+ VERIFY( lsi.is_leap_second == true ); >> > >+ VERIFY( lsi.elapsed == 3s ); >> > >+ >> > >+ s = std::chrono::utc_seconds(1483228826s); // 1 Jan 2017 >> > >+ lsi = get_leap_second_info(s); >> > >+ VERIFY( lsi.is_leap_second == true ); >> > >+ VERIFY( lsi.elapsed == 27s ); >> > >+ >> > >+ // We should not have checked the filesystem for the times above. >> > >+ VERIFY( ! override_used ); >> > >+} >> > >+ >> > >+void >> > >+test_future_leaps() >> > >+{ >> > >+ // XXX adjust this if real leap seconds are added to the hardcoded >> lists: >> > >+ const auto hardcoded_count = 27s; >> > >+ >> > >+ // Make sure there's no old file from a previous test run. >> > >+ std::ofstream("leapseconds") << ""; >> > >+ >> > >+ using std::chrono::years; >> > >+ auto s = std::chrono::utc_seconds(1483228826s + years(100)); // 1 >> Jan 2117 >> > >+ auto lsi = get_leap_second_info(s); >> > >+ VERIFY( override_used ); // If this fails then XFAIL for the target. >> > >+ VERIFY( lsi.is_leap_second == false ); >> > >+ VERIFY( lsi.elapsed == hardcoded_count ); >> > >+ lsi = get_leap_second_info(s + years(10)); >> > >+ VERIFY( lsi.is_leap_second == false ); >> > >+ VERIFY( lsi.elapsed == hardcoded_count ); >> > >+ >> > >+ std::ofstream("leapseconds") << R"( >> > >+# No need to repeat the real leap seconds here, they're hardcoded in >> the lib. >> > >+# These are fake new leap seconds for testing purposes: >> > >+Leap 2099 Dec 31 23:59:60 + S >> > >+Leap 2120 Jun 30 23:59:59 - S >> > >+Leap 2120 Dec 30 23:59:59 - S >> > >+)"; >> > >+ >> > >+ override_used = false; >> > >+ std::chrono::reload_tzdb(); >> > >+ VERIFY( override_used ); >> > >+ >> > >+ // Check the same dates again using the custom leapseconds file: >> > >+ lsi = get_leap_second_info(s); >> > >+ VERIFY( lsi.is_leap_second == false ); >> > >+ VERIFY( lsi.elapsed == hardcoded_count + 1s ); // One positive leap >> second. >> > >+ lsi = get_leap_second_info(s + years(10)); >> > >+ VERIFY( lsi.is_leap_second == false ); >> > >+ VERIFY( lsi.elapsed == hardcoded_count - 1s ); // Two negative leap >> seconds. >> > >+ >> > >+ // Overwrite the custom file again: >> > >+ override_used = false; >> > >+ std::ofstream("leapseconds") << ""; >> > >+ >> > >+ // This should not re-read the custom file, the head of the >> tzdb_list >> > >+ // should already have been populated by calling >> get_leap_second_info(s): >> > >+ auto& tzdb = std::chrono::get_tzdb(); >> > >+ // The file was not read again: >> > >+ VERIFY( ! override_used ); >> > >+ // The list in the tzdb contains the three fake leap seconds: >> > >+ VERIFY( tzdb.leap_seconds.size() == hardcoded_count.count() + 3 ); >> > >+ // And repeating the queries above gives the same results: >> > >+ lsi = get_leap_second_info(s); >> > >+ VERIFY( lsi.is_leap_second == false ); >> > >+ VERIFY( lsi.elapsed == hardcoded_count + 1s ); // One positive leap >> second. >> > >+ lsi = get_leap_second_info(s + years(10)); >> > >+ VERIFY( lsi.is_leap_second == false ); >> > >+ VERIFY( lsi.elapsed == hardcoded_count - 1s ); // Two negative leap >> seconds. >> > >+} >> > >+ >> > >+int main() >> > >+{ >> > >+ test_known_leaps(); >> > >+ test_future_leaps(); >> > >+} >> > >diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc >> b/libstdc++-v3/testsuite/util/testsuite_abi.cc >> > >index 8fb38355cad..4e80c5f184a 100644 >> > >--- a/libstdc++-v3/testsuite/util/testsuite_abi.cc >> > >+++ b/libstdc++-v3/testsuite/util/testsuite_abi.cc >> > >@@ -217,6 +217,7 @@ check_version(symbol& test, bool added) >> > > known_versions.push_back("GLIBCXX_3.4.33"); >> > > known_versions.push_back("GLIBCXX_3.4.34"); >> > > known_versions.push_back("GLIBCXX_3.4.35"); >> > >+ known_versions.push_back("GLIBCXX_3.4.36"); >> > > known_versions.push_back("GLIBCXX_LDBL_3.4.31"); >> > > known_versions.push_back("GLIBCXX_IEEE128_3.4.29"); >> > > known_versions.push_back("GLIBCXX_IEEE128_3.4.30"); >> > >@@ -260,7 +261,7 @@ check_version(symbol& test, bool added) >> > > test.version_status = symbol::incompatible; >> > > >> > > // Check that added symbols are added in the latest pre-release >> version. >> > >- bool latestp = (test.version_name == "GLIBCXX_3.4.35" >> > >+ bool latestp = (test.version_name == "GLIBCXX_3.4.36" >> > > || test.version_name == "CXXABI_1.3.17" >> > > || test.version_name == "CXXABI_FLOAT128" >> > > || test.version_name == "CXXABI_TM_1"); >> > >-- >> > >2.55.0 >> > > >> > > >> > >> >>
