On Wed, 1 Jul 2026 at 13:49, Torbjorn SVENSSON <[email protected]> wrote: > > Hi, > > When trying to build r17-2056-g44e33b2d621d6d, I get the following > compilation error: > > libtool: compile: /build/r17-2056-g44e33b2d621d6dgcc-final/./gcc/xgcc > -shared-libgcc -B/build/r17-2056-g44e33b2d621d6dgcc-final/./gcc -nostdinc++ > -L/build/r17-2056-g44e33b2d621d6dgcc-final/arm-none-eabi/thumb/v7/nofp/libstdc++-v3/src > > -L/build/r17-2056-g44e33b2d621d6dgcc-final/arm-none-eabi/thumb/v7/nofp/libstdc++-v3/src/.libs > > -L/build/r17-2056-g44e33b2d621d6dgcc-final/arm-none-eabi/thumb/v7/nofp/libstdc++-v3/libsupc++/.libs > -B/build/r17-2056-g44e33b2d621d6d/arm-none-eabi/bin/ > -B/build/r17-2056-g44e33b2d621d6d/arm-none-eabi/lib/thumb/v7/nofp > -B/build/r17-2056-g44e33b2d621d6d/arm-none-eabi/lib/ -isystem > /build/r17-2056-g44e33b2d621d6d/arm-none-eabi/include -isystem > /build/r17-2056-g44e33b2d621d6d/arm-none-eabi/sys-include -mthumb > -march=armv7 -mfloat-abi=soft -I/build/gcc_src/libstdc++-v3/../libgcc > -I/build/r17-2056-g44e33b2d621d6dgcc-final/arm-none-eabi/thumb/v7/nofp/libstdc++-v3/include/arm-none-eabi > > -I/build/r17-2056-g44e33b2d621d6dgcc-final/arm-none-eabi/thumb/v7/nofp/libstdc++-v3/include > -I/build/gcc_src/libstdc++-v3/libsupc++ -std=gnu++20 -fno-implicit-templates > -Wall -Wextra -Wwrite-strings -Wcast-qual -Wabi=19 > -fdiagnostics-show-location=once -ffunction-sections -fdata-sections > -frandom-seed=tzdb.lo -fimplicit-templates -g -O2 -mthumb -march=armv7 > -mfloat-abi=soft -c /build/gcc_src/libstdc++-v3/src/c++20/tzdb.cc -o tzdb.o > ... > /build/gcc_src/libstdc++-v3/src/c++20/tzdb.cc: In static member function > 'static const std::chrono::tzdb& > std::chrono::tzdb_list::_Node::_S_replace_head(std::shared_ptr<std::chrono::tzdb_list::_Node>, > std::shared_ptr<std::chrono::tzdb_list::_Node>)': > /build/gcc_src/libstdc++-v3/src/c++20/tzdb.cc:1617:22: error: 'struct > std::chrono::tzdb_list::_Node::NumLeapSeconds' has no member named > 'set_locked' > 1617 | > num_leap_seconds.set_locked(new_head_ptr->db.leap_seconds.size(), lock); > | ^~~~~~~~~~ > Makefile:575: recipe for target 'tzdb.lo' failed > make[9]: *** [tzdb.lo] Error 1 > make[9]: *** Waiting for unfinished jobs....
Oops, sorry. That should be fixed at r17-2061-gadb4f4a064c009 > > > I am configured GCC with the following options: > > /build/gcc_src/configure \ > --target=arm-none-eabi \ > --prefix=/build/r17-2056-g44e33b2d621d6d \ > --libexecdir=/build/r17-2056-g44e33b2d621d6d/lib \ > > --infodir=/build/r17-2056-g44e33b2d621d6d/share/doc/gcc-arm-none-eabi/info \ > > --mandir=/build/r17-2056-g44e33b2d621d6d/share/doc/gcc-arm-none-eabi/man \ > > --htmldir=/build/r17-2056-g44e33b2d621d6d/share/doc/gcc-arm-none-eabi/html \ > > --pdfdir=/build/r17-2056-g44e33b2d621d6d/share/doc/gcc-arm-none-eabi/pdf \ > --enable-checking=release \ > --enable-languages=c,c++ \ > --enable-plugins \ > --disable-decimal-float \ > --disable-libffi \ > --disable-libgomp \ > --disable-libmudflap \ > --disable-libquadmath \ > --disable-libssp \ > --disable-libstdcxx-pch \ > --disable-nls \ > --disable-shared \ > --disable-threads \ > --disable-tls \ > --with-gnu-as \ > --with-gnu-ld \ > --with-newlib \ > --with-headers=yes \ > --with-python-dir=share/gcc-arm-none-eabi \ > --with-sysroot=/build/r17-2056-g44e33b2d621d6d/arm-none-eabi \ > --with-zstd=no \ > --build=x86_64-linux-gnu \ > --host=x86_64-linux-gnu \ > --with-gmp=/build/host-libs/usr \ > --with-mpfr=/build/host-libs/usr \ > --with-mpc=/build/host-libs/usr \ > --with-isl=/build/host-libs/usr \ > '--with-host-libstdcxx=-static-libgcc -Wl,-Bstatic,-lstdc++,-Bdynamic > -lm' \ > '--with-pkgversion=r17-2056-g44e33b2d621d6d' \ > --with-multilib-list=rmprofile,aprofile > > > On 2026-06-30 19:41, Jonathan Wakely wrote: > > My r17-471-ge79f0f818c0e42 change to optimize handling of leap seconds > > introduced a hard dependency on std::atomic<unsigned>, which causes > > problems for targets without atomic word operations, like Cortex-M0.: > > https://gcc.gnu.org/pipermail/gcc-patches/2026-June/719704.html > > > > This patch replaces the num_leap_seconds variable with a struct 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. > > > > The workaround is a bit ugly, because it assumes that there is only one > > caller of num_leap_seconds.set and that the list_mutex() is locked by > > that caller iff the tzdb_list doesn't use atomic<shared_ptr<>>. To make > > the assumption explicit, there are two different functions used to > > update the value, depending on whether the mutex is used or not. > > > > libstdc++-v3/ChangeLog: > > > > * src/c++20/tzdb.cc (_Node::NumLeapSeconds): New class. > > (_Node::num_leap_seconds): New static variable. > > (num_leap_seconds): Remove. > > (__detail::__recent_leap_second_info): Replace uses of > > num_leap_seconds with _Node::num_leap_seconds. > > (_Node::_S_replace_head): Likewise. > > --- > > > > v2: Replace NumLeapSeconds::set by two members, set_atomically and > > set_locked. > > > > Tested x86_64-linux. > > > > libstdc++-v3/src/c++20/tzdb.cc | 119 ++++++++++++++++++++++++++------- > > 1 file changed, 94 insertions(+), 25 deletions(-) > > > > diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc > > index 5793155b6d89..9e601fc176f3 100644 > > --- a/libstdc++-v3/src/c++20/tzdb.cc > > +++ b/libstdc++-v3/src/c++20/tzdb.cc > > @@ -67,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 > > > > @@ -200,6 +201,10 @@ namespace std::chrono > > > > // 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. > > @@ -1301,12 +1306,85 @@ 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. > > - constinit atomic<unsigned> num_leap_seconds{0}; > > } > > > > +// 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. > > +#if ATOMIC_INT_LOCK_FREE == 2 > > + void > > + set_atomically(unsigned val) > > + { > > + atomic_ref<unsigned> ref(count); > > + // The release op here synchronizes with the acquire op in get(). > > + ref.store(val, memory_order::release); > > + } > > +#else > > + void > > + set_locked(unsigned val, const lock_guard<mutex>&) > > + { > > + // XXX 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> > > @@ -1368,19 +1446,11 @@ namespace > > > > constexpr auto num_fixed_leaps = std::size(_Node::fixed_leaps); > > > > - auto num_leaps = num_leap_seconds.load(memory_order::relaxed); > > + 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 been initialized and contains a tzdb object > > - // with new leap seconds, which we want to use here. > > - // The relaxed load above does not synchronize with anything, so to > > - // ensure that the get_tzdb_list() below will see a tzdb object set > > - // by _S_replace_head, we load num_leap_seconds again with acquire > > - // ordering: > > - (void) num_leap_seconds.load(memory_order::acquire); > > - else > > + else if (num_leaps == 0) > > { > > // The tzdb_list has not been initialized yet, so we don't know > > // the correct number of leap seconds. > > @@ -1389,11 +1459,9 @@ namespace > > // 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 > > + // There are no new leap seconds. Remember that so that the next > > // call to this function can just use fixed_leaps. > > - num_leap_seconds.compare_exchange_strong(num_leaps, > > - num_fixed_leaps, > > - memory_order::relaxed); > > + _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 > > @@ -1525,8 +1593,13 @@ namespace > > 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_atomically(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) > > @@ -1534,14 +1607,10 @@ namespace > > new_head_ptr->next = _S_head_owner; > > } > > _S_head_owner = std::move(new_head); > > -#endif > > _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. > > - // The release op here synchronizes with the acquire op there. > > - num_leap_seconds.store(new_head_ptr->db.leap_seconds.size(), > > - memory_order::release); > > + num_leap_seconds.set_locked(new_head_ptr->db.leap_seconds.size(), > > lock); > > Looks like the pre-processor condition here is missaligned with the > preprocessor condition for the definition of set_locked(). > > Hope this helps to figure out how to get trunk buildable again. > > Kind regards, > Torbjörn > > > +#endif > > > > return new_head_ptr->db; > > } >
