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
>> > >
>> > >
>> >
>>
>>

Reply via email to