<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Wed, Jul 1, 2026 at 5:13 PM Jonathan Wakely <<a href="mailto:jwakely@redhat.com">jwakely@redhat.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On Wed, 1 Jul 2026 at 15:53, Tomasz Kaminski <<a href="mailto:tkaminsk@redhat.com" target="_blank">tkaminsk@redhat.com</a>> wrote:<br>
><br>
><br>
><br>
> On Wed, Jul 1, 2026 at 4:36 PM Jonathan Wakely <<a href="mailto:jwakely@redhat.com" target="_blank">jwakely@redhat.com</a>> wrote:<br>
>><br>
>> The changes in r17-2047-gb12fdd95251178 cause a bootstrap failure if<br>
>> ATOMIC_POINTER_LOCK_FREE != 2 && ATOMIC_INT_LOCK_FREE == 2 is true for<br>
>> the target:<br>
>><br>
>> .../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>)’:<br>
>> .../tzdb.cc:1617:22: error: ‘struct std::chrono::tzdb_list::_Node::NumLeapSeconds’ has no member named ‘set_locked’<br>
>>  1617 |     num_leap_seconds.set_locked(new_head_ptr->db.leap_seconds.size(), lock);<br>
>>       |                      ^~~~~~~~~~<br>
>><br>
>> This fix defines the 'set_atomically' and 'set_locked' functions<br>
>> unconditionally, and renames the former to just 'set'. This fixes the<br>
>> mismatch between the atomic pointer and atomic int conditions, as both<br>
>> functions are available for both branches of the #if/#else in<br>
>> _Node::_S_replace_head.<br>
>><br>
>> libstdc++-v3/ChangeLog:<br>
>><br>
>>         * src/c++20/tzdb.cc (_Node::NumLeapSeconds::set_atomically):<br>
>>         Rename to set and define unconditionally.<br>
>>         (_Node::NumLeapSeconds::set_locked): Define unconditionally.<br>
>> ---<br>
>><br>
>> Pushed to trunk.<br>
>><br>
>>  libstdc++-v3/src/c++20/tzdb.cc | 15 +++++++++------<br>
>>  1 file changed, 9 insertions(+), 6 deletions(-)<br>
>><br>
>> diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc<br>
>> index 751370325d76..1aae07033f73 100644<br>
>> --- a/libstdc++-v3/src/c++20/tzdb.cc<br>
>> +++ b/libstdc++-v3/src/c++20/tzdb.cc<br>
>> @@ -1454,23 +1454,26 @@ struct tzdb_list::_Node::NumLeapSeconds<br>
>>    // Called by _Node::_S_replace_head<br>
>>    // The two versions are named differently so that caller has to be explicit<br>
>>    // about which version it calls, based on whether the mutex is held.<br>
>> -#if ATOMIC_INT_LOCK_FREE == 2<br>
>>    void<br>
>> -  set_atomically(unsigned val)<br>
>> +  set(unsigned val)<br>
>>    {<br>
>> +#if ATOMIC_INT_LOCK_FREE == 2<br>
>>      atomic_ref<unsigned> ref(count);<br>
>>      // The release op here synchronizes with the acquire op in get().<br>
>>      ref.store(val, memory_order::release);<br>
>> -  }<br>
>>  #else<br>
>> +    lock_guard<mutex> l(list_mutex());<br>
>> +    set_locked(val, l);<br>
>> +#endif<br>
>> +  }<br>
>> +<br>
>>    void<br>
>>    set_locked(unsigned val, const lock_guard<mutex>&)<br>
>>    {<br>
>> -    // XXX The only caller of this function locks list_mutex() so we would<br>
>> +    // The only caller of this function locks list_mutex() so we would<br>
>>      // deadlock if we locked it again here.<br>
>>      count = val;<br>
><br>
> If ATOMIC_INT_LOCK_FREE is true, but USE_ATOMIC_SHARED_PTR is false,<br>
> we are having a data race here, as __recent_leap_seconds will not lock the mutex<br>
> before reading count in that case.  We should simply call set in this case.<br>
<br>
Ah yes, so we want:<br>
<br>
--- a/libstdc++-v3/src/c++20/tzdb.cc<br>
+++ b/libstdc++-v3/src/c++20/tzdb.cc<br>
@@ -1470,9 +1470,15 @@ struct tzdb_list::_Node::NumLeapSeconds<br>
  void<br>
  set_locked(unsigned val, const lock_guard<mutex>&)<br>
  {<br>
+#if ATOMIC_INT_LOCK_FREE == 2<br>
+    // Even though the caller locked the mutex, we still need to use an<br>
+    // atomic store in this case, because there could be concurrent loads.<br>
+    set(val);<br></blockquote><div>Yes, something like this. </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+#else </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
    // The only caller of this function locks list_mutex() so we would<br>
    // deadlock if we locked it again here.<br></blockquote><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
    count = val;<br>
+#endif<br>
  }<br>
<br>
private:<br>
<br>
<br>
<br>
<br>
><br>
><br>
>>    }<br>
>> -#endif<br>
>><br>
>>  private:<br>
>>    unsigned count = 0;<br>
>> @@ -1690,7 +1693,7 @@ constinit tzdb_list::_Node::NumLeapSeconds tzdb_list::_Node::num_leap_seconds;<br>
>><br>
>>      // This allows __recent_leap_second_info() to know that it can use<br>
>>      // get_tzdb_list()->begin()->leap_seconds to get new leap seconds.<br>
>> -    num_leap_seconds.set_atomically(new_head_ptr->db.leap_seconds.size());<br>
>> +    num_leap_seconds.set(new_head_ptr->db.leap_seconds.size());<br>
>>  #else<br>
>>      lock_guard<mutex> lock(list_mutex());<br>
>>      if (const _Node* h = _S_head_owner.get())<br>
>> --<br>
>> 2.54.0<br>
>><br>
<br>
</blockquote></div></div>