Checking patch nptl/pthread_cond_common.c... error: while searching for: * New waiters arriving concurrently with the group switching will all go into G2 until we atomically make the switch. Waiters existing in G2 are not affected. * Waiters in G1 will be closed out immediately by setting a flag in __g_signals, which will prevent waiters from blocking using a futex on __g_signals and also notifies them that the group is closed. As a result, they will eventually remove their group reference, allowing us to close switch group roles. */ /* First, set the closed flag on __g_signals. This tells waiters that are about to wait that they shouldn't do that anymore. This basically serves as an advance notificaton of the upcoming change to __g1_start; waiters interpret it as if __g1_start was larger than their waiter sequence position. This allows us to change __g1_start after waiting for all existing waiters with group references to leave, which in turn makes recovery after stealing a signal simpler because it then can be skipped if __g1_start indicates that the group is closed (otherwise, we would have to recover always because waiters don't know how big their groups are). Relaxed MO is fine. */ atomic_fetch_or_relaxed (cond->__data.__g_signals + g1, 1); /* Wait until there are no group references anymore. The fetch-or operation injects us into the modification order of __g_refs; release MO ensures that waiters incrementing __g_refs after our fetch-or see the previous changes to __g_signals and to __g1_start that had to happen before we can switch this G1 and alias with an older group (we have two groups, so aliasing requires switching group roles twice). Note that nobody else can have set the wake-request flag, so we do not have to act upon it. Also note that it is harmless if older waiters or waiters from this G1 get a group reference after we have quiesced the group because it will remain closed for them either because of the closed flag in __g_signals or the later update to __g1_start. New waiters will never arrive here but instead continue to go into the still current G2. */ unsigned r = atomic_fetch_or_release (cond->__data.__g_refs + g1, 0); while ((r >> 1) > 0) { for (unsigned int spin = maxspin; ((r >> 1) > 0) && (spin > 0); spin--) { /* TODO Back off. */ r = atomic_load_relaxed (cond->__data.__g_refs + g1); } if ((r >> 1) > 0) { /* There is still a waiter after spinning. Set the wake-request flag and block. Relaxed MO is fine because this is just about this futex word. Update r to include the set wake-request flag so that the upcoming futex_wait only blocks if the flag is still set (otherwise, we'd violate the basic client-side futex protocol). */ r = atomic_fetch_or_relaxed (cond->__data.__g_refs + g1, 1) | 1; if ((r >> 1) > 0) futex_wait_simple (cond->__data.__g_refs + g1, r, private); /* Reload here so we eventually see the most recent value even if we do not spin. */ r = atomic_load_relaxed (cond->__data.__g_refs + g1); } } /* Acquire MO so that we synchronize with the release operation that waiters use to decrement __g_refs and thus happen after the waiters we waited for. */ atomic_thread_fence_acquire (); /* Update __g1_start, which finishes closing this group. The value we add will never be negative because old_orig_size can only be zero when we switch groups the first time after a condvar was initialized, in which case G1 will be at index 1 and we will add a value of 1. See above for why this takes place after waiting for quiescence of the group. Relaxed MO is fine because the change comes with no additional constraints that others would have to observe. */ __condvar_add_g1_start_relaxed (cond, (old_orig_size << 1) + (g1 == 1 ? 1 : - 1)); /* Now reopen the group, thus enabling waiters to again block using the futex controll error: patch failed: nptl/pthread_cond_common.c:222 error: nptl/pthread_cond_common.c: patch does not apply Checking patch nptl/pthread_cond_wait.c... error: while searching for: while (!atomic_compare_exchange_weak_acquire (cond->__data.__g_signals + g, &signals, signals - 2)); /* We consumed a signal but we could have consumed from a more recent group that aliased with ours due to being in the same group slot. If this might be the case our group must be closed as visible through __g1_start. */ uint64_t g1_start = __condvar_load_g1_start_relaxed (cond); if (seq < (g1_start >> 1)) { /* We potentially stole a signal from a more recent group but we do not know which group we really consumed from. We do not care about groups older than current G1 because they are closed; we could have stolen from these, but then we just add a spurious wake-up for the current groups. We will never steal a signal from current G2 that was really intended for G2 because G2 never receives signals (until it becomes G1). We could have stolen a signal from G2 that was conservatively added by a previous waiter that also thought it stole a signal -- but given that that signal was added unnecessarily, it's not a problem if we steal it. Thus, the remaining case is that we could have stolen from the current G1, where "current" means the __g1_start value we observed. However, if the current G1 does not have the same slot index as we do, we did not steal from it and do not need to undo that. This is the reason for putting a bit with G2's index into__g1_start as well. */ if (((g1_start & 1) ^ 1) == g) { /* We have to conservatively undo our potential mistake of stealing a signal. We can stop trying to do that when the current G1 changes because other spinning waiters will notice this too and __condvar_quiesce_and_switch_g1 has checked that there are no futex waiters anymore before switching G1. Relaxed MO is fine for the __g1_start load because we need to merely be able to observe this fact and not have to observe something else as well. ??? Would it help to spin for a little while to see whether the current G1 gets closed? This might be worthwhile if the group is small or close to being closed. */ unsigned int s = atomic_load_relaxed (cond->__data.__g_signals + g); while (__condvar_load_g1_start_relaxed (cond) == g1_start) { /* Try to add a signal. We don't need to acquire the lock because at worst we can cause a spurious wake-up. If the group is in the process of being closed (LSB is true), this has an effect similar to us adding a signal. */ if (((s & 1) != 0) || atomic_compare_exchange_weak_relaxed (cond->__data.__g_signals + g, &s, s + 2)) { /* If we added a signal, we also need to add a wake-up on the futex. We also need to do that if we skipped adding a signal because the group is being closed because while __condvar_quiesce_and_switch_g1 could have closed the group, it might stil be waiting for futex waiters to leave (and one of those waiters might be the one we stole the signal from, which cause it to block using the futex). */ futex_wake (cond->__data.__g_signals + g, 1, private); break; } /* TODO Back off. */ } } } done: /* Confirm that we have been woken. We do that before acquiring the mutex error: patch failed: nptl/pthread_cond_wait.c:532 error: nptl/pthread_cond_wait.c: patch does not apply