diff options
| author | Sunil Dora <sunilkumar.dora@windriver.com> | 2025-06-17 03:08:48 -0700 |
|---|---|---|
| committer | Steve Sakoman <steve@sakoman.com> | 2025-06-20 08:06:30 -0700 |
| commit | 218c9ec684cf13229d7d9ef6cf4bc4f7be564faf (patch) | |
| tree | 592e02686862660cf804655095d7df2310b97d33 /meta/recipes-core | |
| parent | d2ca3a347ed775b584029d58f531ccc88fd7757a (diff) | |
| download | poky-218c9ec684cf13229d7d9ef6cf4bc4f7be564faf.tar.gz | |
glibc: pthreads NPTL lost wakeup fix 2
The following commits have been cherry-picked from Glibc master branch:
Bug : https://sourceware.org/bugzilla/show_bug.cgi?id=25847
Upstream-Status: Backport
[https://sourceware.org/git/?p=glibc.git;a=commit;h=1db84775f831a1494993ce9c118deaf9537cc50a]
(From OE-Core rev: c05290e51d0faf661bac587066a79626919609e8)
Signed-off-by: Sunil Dora <sunilkumar.dora@windriver.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
Diffstat (limited to 'meta/recipes-core')
| -rw-r--r-- | meta/recipes-core/glibc/glibc/0026-PR25847-1.patch | 455 | ||||
| -rw-r--r-- | meta/recipes-core/glibc/glibc_2.35.bb | 1 |
2 files changed, 456 insertions, 0 deletions
diff --git a/meta/recipes-core/glibc/glibc/0026-PR25847-1.patch b/meta/recipes-core/glibc/glibc/0026-PR25847-1.patch new file mode 100644 index 0000000000..44a2b6772c --- /dev/null +++ b/meta/recipes-core/glibc/glibc/0026-PR25847-1.patch | |||
| @@ -0,0 +1,455 @@ | |||
| 1 | From 31d9848830e496f57d4182b518467c4c63bfd4bd Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Frank Barrus <frankbarrus_sw@shaggy.cc> | ||
| 3 | Date: Mon, 16 Jun 2025 22:37:54 -0700 | ||
| 4 | Subject: [PATCH] pthreads NPTL: lost wakeup fix 2 | ||
| 5 | |||
| 6 | This fixes the lost wakeup (from a bug in signal stealing) with a change | ||
| 7 | in the usage of g_signals[] in the condition variable internal state. | ||
| 8 | It also completely eliminates the concept and handling of signal stealing, | ||
| 9 | as well as the need for signalers to block to wait for waiters to wake | ||
| 10 | up every time there is a G1/G2 switch. This greatly reduces the average | ||
| 11 | and maximum latency for pthread_cond_signal. | ||
| 12 | |||
| 13 | The g_signals[] field now contains a signal count that is relative to | ||
| 14 | the current g1_start value. Since it is a 32-bit field, and the LSB is | ||
| 15 | still reserved (though not currently used anymore), it has a 31-bit value | ||
| 16 | that corresponds to the low 31 bits of the sequence number in g1_start. | ||
| 17 | (since g1_start also has an LSB flag, this means bits 31:1 in g_signals | ||
| 18 | correspond to bits 31:1 in g1_start, plus the current signal count) | ||
| 19 | |||
| 20 | By making the signal count relative to g1_start, there is no longer | ||
| 21 | any ambiguity or A/B/A issue, and thus any checks before blocking, | ||
| 22 | including the futex call itself, are guaranteed not to block if the G1/G2 | ||
| 23 | switch occurs, even if the signal count remains the same. This allows | ||
| 24 | initially safely blocking in G2 until the switch to G1 occurs, and | ||
| 25 | then transitioning from G1 to a new G1 or G2, and always being able to | ||
| 26 | distinguish the state change. This removes the race condition and A/B/A | ||
| 27 | problems that otherwise ocurred if a late (pre-empted) waiter were to | ||
| 28 | resume just as the futex call attempted to block on g_signal since | ||
| 29 | otherwise there was no last opportunity to re-check things like whether | ||
| 30 | the current G1 group was already closed. | ||
| 31 | |||
| 32 | By fixing these issues, the signal stealing code can be eliminated, | ||
| 33 | since there is no concept of signal stealing anymore. The code to block | ||
| 34 | for all waiters to exit g_refs can also be removed, since any waiters | ||
| 35 | that are still in the g_refs region can be guaranteed to safely wake | ||
| 36 | up and exit. If there are still any left at this time, they are all | ||
| 37 | sent one final futex wakeup to ensure that they are not blocked any | ||
| 38 | longer, but there is no need for the signaller to block and wait for | ||
| 39 | them to wake up and exit the g_refs region. | ||
| 40 | |||
| 41 | The signal count is then effectively "zeroed" but since it is now | ||
| 42 | relative to g1_start, this is done by advancing it to a new value that | ||
| 43 | can be observed by any pending blocking waiters. Any late waiters can | ||
| 44 | always tell the difference, and can thus just cleanly exit if they are | ||
| 45 | in a stale G1 or G2. They can never steal a signal from the current | ||
| 46 | G1 if they are not in the current G1, since the signal value that has | ||
| 47 | to match in the cmpxchg has the low 31 bits of the g1_start value | ||
| 48 | contained in it, and that's first checked, and then it won't match if | ||
| 49 | there's a G1/G2 change. | ||
| 50 | |||
| 51 | Note: the 31-bit sequence number used in g_signals is designed to | ||
| 52 | handle wrap-around when checking the signal count, but if the entire | ||
| 53 | 31-bit wraparound (2 billion signals) occurs while there is still a | ||
| 54 | late waiter that has not yet resumed, and it happens to then match | ||
| 55 | the current g1_start low bits, and the pre-emption occurs after the | ||
| 56 | normal "closed group" checks (which are 64-bit) but then hits the | ||
| 57 | futex syscall and signal consuming code, then an A/B/A issue could | ||
| 58 | still result and cause an incorrect assumption about whether it | ||
| 59 | should block. This particular scenario seems unlikely in practice. | ||
| 60 | Note that once awake from the futex, the waiter would notice the | ||
| 61 | closed group before consuming the signal (since that's still a 64-bit | ||
| 62 | check that would not be aliased in the wrap-around in g_signals), | ||
| 63 | so the biggest impact would be blocking on the futex until the next | ||
| 64 | full wakeup from a G1/G2 switch. | ||
| 65 | |||
| 66 | The following commits have been cherry-picked from Glibc master branch: | ||
| 67 | Bug : https://sourceware.org/bugzilla/show_bug.cgi?id=25847 | ||
| 68 | |||
| 69 | Upstream-Status: Backport | ||
| 70 | [https://sourceware.org/git/?p=glibc.git;a=commit;h=1db84775f831a1494993ce9c118deaf9537cc50a] | ||
| 71 | |||
| 72 | Signed-off-by: Sunil Dora <sunilkumar.dora@windriver.com> | ||
| 73 | --- | ||
| 74 | nptl/pthread_cond_common.c | 106 +++++++++------------------ | ||
| 75 | nptl/pthread_cond_wait.c | 144 ++++++++++++------------------------- | ||
| 76 | 2 files changed, 81 insertions(+), 169 deletions(-) | ||
| 77 | |||
| 78 | diff --git a/nptl/pthread_cond_common.c b/nptl/pthread_cond_common.c | ||
| 79 | index fb035f72c3..8dd7037923 100644 | ||
| 80 | --- a/nptl/pthread_cond_common.c | ||
| 81 | +++ b/nptl/pthread_cond_common.c | ||
| 82 | @@ -201,7 +201,6 @@ static bool __attribute__ ((unused)) | ||
| 83 | __condvar_quiesce_and_switch_g1 (pthread_cond_t *cond, uint64_t wseq, | ||
| 84 | unsigned int *g1index, int private) | ||
| 85 | { | ||
| 86 | - const unsigned int maxspin = 0; | ||
| 87 | unsigned int g1 = *g1index; | ||
| 88 | |||
| 89 | /* If there is no waiter in G2, we don't do anything. The expression may | ||
| 90 | @@ -222,85 +221,46 @@ __condvar_quiesce_and_switch_g1 (pthread_cond_t *cond, uint64_t wseq, | ||
| 91 | * New waiters arriving concurrently with the group switching will all go | ||
| 92 | into G2 until we atomically make the switch. Waiters existing in G2 | ||
| 93 | are not affected. | ||
| 94 | - * Waiters in G1 will be closed out immediately by setting a flag in | ||
| 95 | - __g_signals, which will prevent waiters from blocking using a futex on | ||
| 96 | - __g_signals and also notifies them that the group is closed. As a | ||
| 97 | - result, they will eventually remove their group reference, allowing us | ||
| 98 | - to close switch group roles. */ | ||
| 99 | - | ||
| 100 | - /* First, set the closed flag on __g_signals. This tells waiters that are | ||
| 101 | - about to wait that they shouldn't do that anymore. This basically | ||
| 102 | - serves as an advance notificaton of the upcoming change to __g1_start; | ||
| 103 | - waiters interpret it as if __g1_start was larger than their waiter | ||
| 104 | - sequence position. This allows us to change __g1_start after waiting | ||
| 105 | - for all existing waiters with group references to leave, which in turn | ||
| 106 | - makes recovery after stealing a signal simpler because it then can be | ||
| 107 | - skipped if __g1_start indicates that the group is closed (otherwise, | ||
| 108 | - we would have to recover always because waiters don't know how big their | ||
| 109 | - groups are). Relaxed MO is fine. */ | ||
| 110 | - atomic_fetch_or_relaxed (cond->__data.__g_signals + g1, 1); | ||
| 111 | - | ||
| 112 | - /* Wait until there are no group references anymore. The fetch-or operation | ||
| 113 | - injects us into the modification order of __g_refs; release MO ensures | ||
| 114 | - that waiters incrementing __g_refs after our fetch-or see the previous | ||
| 115 | - changes to __g_signals and to __g1_start that had to happen before we can | ||
| 116 | - switch this G1 and alias with an older group (we have two groups, so | ||
| 117 | - aliasing requires switching group roles twice). Note that nobody else | ||
| 118 | - can have set the wake-request flag, so we do not have to act upon it. | ||
| 119 | - | ||
| 120 | - Also note that it is harmless if older waiters or waiters from this G1 | ||
| 121 | - get a group reference after we have quiesced the group because it will | ||
| 122 | - remain closed for them either because of the closed flag in __g_signals | ||
| 123 | - or the later update to __g1_start. New waiters will never arrive here | ||
| 124 | - but instead continue to go into the still current G2. */ | ||
| 125 | - unsigned r = atomic_fetch_or_release (cond->__data.__g_refs + g1, 0); | ||
| 126 | - while ((r >> 1) > 0) | ||
| 127 | - { | ||
| 128 | - for (unsigned int spin = maxspin; ((r >> 1) > 0) && (spin > 0); spin--) | ||
| 129 | - { | ||
| 130 | - /* TODO Back off. */ | ||
| 131 | - r = atomic_load_relaxed (cond->__data.__g_refs + g1); | ||
| 132 | - } | ||
| 133 | - if ((r >> 1) > 0) | ||
| 134 | - { | ||
| 135 | - /* There is still a waiter after spinning. Set the wake-request | ||
| 136 | - flag and block. Relaxed MO is fine because this is just about | ||
| 137 | - this futex word. | ||
| 138 | - | ||
| 139 | - Update r to include the set wake-request flag so that the upcoming | ||
| 140 | - futex_wait only blocks if the flag is still set (otherwise, we'd | ||
| 141 | - violate the basic client-side futex protocol). */ | ||
| 142 | - r = atomic_fetch_or_relaxed (cond->__data.__g_refs + g1, 1) | 1; | ||
| 143 | - | ||
| 144 | - if ((r >> 1) > 0) | ||
| 145 | - futex_wait_simple (cond->__data.__g_refs + g1, r, private); | ||
| 146 | - /* Reload here so we eventually see the most recent value even if we | ||
| 147 | - do not spin. */ | ||
| 148 | - r = atomic_load_relaxed (cond->__data.__g_refs + g1); | ||
| 149 | - } | ||
| 150 | - } | ||
| 151 | - /* Acquire MO so that we synchronize with the release operation that waiters | ||
| 152 | - use to decrement __g_refs and thus happen after the waiters we waited | ||
| 153 | - for. */ | ||
| 154 | - atomic_thread_fence_acquire (); | ||
| 155 | + * Waiters in G1 will be closed out immediately by the advancing of | ||
| 156 | + __g_signals to the next "lowseq" (low 31 bits of the new g1_start), | ||
| 157 | + which will prevent waiters from blocking using a futex on | ||
| 158 | + __g_signals since it provides enough signals for all possible | ||
| 159 | + remaining waiters. As a result, they can each consume a signal | ||
| 160 | + and they will eventually remove their group reference. */ | ||
| 161 | |||
| 162 | /* Update __g1_start, which finishes closing this group. The value we add | ||
| 163 | will never be negative because old_orig_size can only be zero when we | ||
| 164 | switch groups the first time after a condvar was initialized, in which | ||
| 165 | - case G1 will be at index 1 and we will add a value of 1. See above for | ||
| 166 | - why this takes place after waiting for quiescence of the group. | ||
| 167 | + case G1 will be at index 1 and we will add a value of 1. | ||
| 168 | Relaxed MO is fine because the change comes with no additional | ||
| 169 | constraints that others would have to observe. */ | ||
| 170 | __condvar_add_g1_start_relaxed (cond, | ||
| 171 | (old_orig_size << 1) + (g1 == 1 ? 1 : - 1)); | ||
| 172 | |||
| 173 | - /* Now reopen the group, thus enabling waiters to again block using the | ||
| 174 | - futex controlled by __g_signals. Release MO so that observers that see | ||
| 175 | - no signals (and thus can block) also see the write __g1_start and thus | ||
| 176 | - that this is now a new group (see __pthread_cond_wait_common for the | ||
| 177 | - matching acquire MO loads). */ | ||
| 178 | - atomic_store_release (cond->__data.__g_signals + g1, 0); | ||
| 179 | - | ||
| 180 | + unsigned int lowseq = ((old_g1_start + old_orig_size) << 1) & ~1U; | ||
| 181 | + | ||
| 182 | + /* If any waiters still hold group references (and thus could be blocked), | ||
| 183 | + then wake them all up now and prevent any running ones from blocking. | ||
| 184 | + This is effectively a catch-all for any possible current or future | ||
| 185 | + bugs that can allow the group size to reach 0 before all G1 waiters | ||
| 186 | + have been awakened or at least given signals to consume, or any | ||
| 187 | + other case that can leave blocked (or about to block) older waiters.. */ | ||
| 188 | + if ((atomic_fetch_or_release (cond->__data.__g_refs + g1, 0) >> 1) > 0) | ||
| 189 | + { | ||
| 190 | + /* First advance signals to the end of the group (i.e. enough signals | ||
| 191 | + for the entire G1 group) to ensure that waiters which have not | ||
| 192 | + yet blocked in the futex will not block. | ||
| 193 | + Note that in the vast majority of cases, this should never | ||
| 194 | + actually be necessary, since __g_signals will have enough | ||
| 195 | + signals for the remaining g_refs waiters. As an optimization, | ||
| 196 | + we could check this first before proceeding, although that | ||
| 197 | + could still leave the potential for futex lost wakeup bugs | ||
| 198 | + if the signal count was non-zero but the futex wakeup | ||
| 199 | + was somehow lost. */ | ||
| 200 | + atomic_store_release (cond->__data.__g_signals + g1, lowseq); | ||
| 201 | + | ||
| 202 | + futex_wake (cond->__data.__g_signals + g1, INT_MAX, private); | ||
| 203 | + } | ||
| 204 | /* At this point, the old G1 is now a valid new G2 (but not in use yet). | ||
| 205 | No old waiter can neither grab a signal nor acquire a reference without | ||
| 206 | noticing that __g1_start is larger. | ||
| 207 | @@ -311,6 +271,10 @@ __condvar_quiesce_and_switch_g1 (pthread_cond_t *cond, uint64_t wseq, | ||
| 208 | g1 ^= 1; | ||
| 209 | *g1index ^= 1; | ||
| 210 | |||
| 211 | + /* Now advance the new G1 g_signals to the new lowseq, giving it | ||
| 212 | + an effective signal count of 0 to start. */ | ||
| 213 | + atomic_store_release (cond->__data.__g_signals + g1, lowseq); | ||
| 214 | + | ||
| 215 | /* These values are just observed by signalers, and thus protected by the | ||
| 216 | lock. */ | ||
| 217 | unsigned int orig_size = wseq - (old_g1_start + old_orig_size); | ||
| 218 | diff --git a/nptl/pthread_cond_wait.c b/nptl/pthread_cond_wait.c | ||
| 219 | index 20c348a503..1cb3dbf7b0 100644 | ||
| 220 | --- a/nptl/pthread_cond_wait.c | ||
| 221 | +++ b/nptl/pthread_cond_wait.c | ||
| 222 | @@ -238,9 +238,7 @@ __condvar_cleanup_waiting (void *arg) | ||
| 223 | signaled), and a reference count. | ||
| 224 | |||
| 225 | The group reference count is used to maintain the number of waiters that | ||
| 226 | - are using the group's futex. Before a group can change its role, the | ||
| 227 | - reference count must show that no waiters are using the futex anymore; this | ||
| 228 | - prevents ABA issues on the futex word. | ||
| 229 | + are using the group's futex. | ||
| 230 | |||
| 231 | To represent which intervals in the waiter sequence the groups cover (and | ||
| 232 | thus also which group slot contains G1 or G2), we use a 64b counter to | ||
| 233 | @@ -300,11 +298,12 @@ __condvar_cleanup_waiting (void *arg) | ||
| 234 | last reference. | ||
| 235 | * Reference count used by waiters concurrently with signalers that have | ||
| 236 | acquired the condvar-internal lock. | ||
| 237 | - __g_signals: The number of signals that can still be consumed. | ||
| 238 | + __g_signals: The number of signals that can still be consumed, relative to | ||
| 239 | + the current g1_start. (i.e. bits 31 to 1 of __g_signals are bits | ||
| 240 | + 31 to 1 of g1_start with the signal count added) | ||
| 241 | * Used as a futex word by waiters. Used concurrently by waiters and | ||
| 242 | signalers. | ||
| 243 | - * LSB is true iff this group has been completely signaled (i.e., it is | ||
| 244 | - closed). | ||
| 245 | + * LSB is currently reserved and 0. | ||
| 246 | __g_size: Waiters remaining in this group (i.e., which have not been | ||
| 247 | signaled yet. | ||
| 248 | * Accessed by signalers and waiters that cancel waiting (both do so only | ||
| 249 | @@ -328,18 +327,6 @@ __condvar_cleanup_waiting (void *arg) | ||
| 250 | sufficient because if a waiter can see a sufficiently large value, it could | ||
| 251 | have also consume a signal in the waiters group. | ||
| 252 | |||
| 253 | - Waiters try to grab a signal from __g_signals without holding a reference | ||
| 254 | - count, which can lead to stealing a signal from a more recent group after | ||
| 255 | - their own group was already closed. They cannot always detect whether they | ||
| 256 | - in fact did because they do not know when they stole, but they can | ||
| 257 | - conservatively add a signal back to the group they stole from; if they | ||
| 258 | - did so unnecessarily, all that happens is a spurious wake-up. To make this | ||
| 259 | - even less likely, __g1_start contains the index of the current g2 too, | ||
| 260 | - which allows waiters to check if there aliasing on the group slots; if | ||
| 261 | - there wasn't, they didn't steal from the current G1, which means that the | ||
| 262 | - G1 they stole from must have been already closed and they do not need to | ||
| 263 | - fix anything. | ||
| 264 | - | ||
| 265 | It is essential that the last field in pthread_cond_t is __g_signals[1]: | ||
| 266 | The previous condvar used a pointer-sized field in pthread_cond_t, so a | ||
| 267 | PTHREAD_COND_INITIALIZER from that condvar implementation might only | ||
| 268 | @@ -435,6 +422,9 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex, | ||
| 269 | { | ||
| 270 | while (1) | ||
| 271 | { | ||
| 272 | + uint64_t g1_start = __condvar_load_g1_start_relaxed (cond); | ||
| 273 | + unsigned int lowseq = (g1_start & 1) == g ? signals : g1_start & ~1U; | ||
| 274 | + | ||
| 275 | /* Spin-wait first. | ||
| 276 | Note that spinning first without checking whether a timeout | ||
| 277 | passed might lead to what looks like a spurious wake-up even | ||
| 278 | @@ -446,35 +436,45 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex, | ||
| 279 | having to compare against the current time seems to be the right | ||
| 280 | choice from a performance perspective for most use cases. */ | ||
| 281 | unsigned int spin = maxspin; | ||
| 282 | - while (signals == 0 && spin > 0) | ||
| 283 | + while (spin > 0 && ((int)(signals - lowseq) < 2)) | ||
| 284 | { | ||
| 285 | /* Check that we are not spinning on a group that's already | ||
| 286 | closed. */ | ||
| 287 | - if (seq < (__condvar_load_g1_start_relaxed (cond) >> 1)) | ||
| 288 | - goto done; | ||
| 289 | + if (seq < (g1_start >> 1)) | ||
| 290 | + break; | ||
| 291 | |||
| 292 | /* TODO Back off. */ | ||
| 293 | |||
| 294 | /* Reload signals. See above for MO. */ | ||
| 295 | signals = atomic_load_acquire (cond->__data.__g_signals + g); | ||
| 296 | + g1_start = __condvar_load_g1_start_relaxed (cond); | ||
| 297 | + lowseq = (g1_start & 1) == g ? signals : g1_start & ~1U; | ||
| 298 | spin--; | ||
| 299 | } | ||
| 300 | |||
| 301 | - /* If our group will be closed as indicated by the flag on signals, | ||
| 302 | - don't bother grabbing a signal. */ | ||
| 303 | - if (signals & 1) | ||
| 304 | - goto done; | ||
| 305 | - | ||
| 306 | - /* If there is an available signal, don't block. */ | ||
| 307 | - if (signals != 0) | ||
| 308 | + if (seq < (g1_start >> 1)) | ||
| 309 | + { | ||
| 310 | + /* If the group is closed already, | ||
| 311 | + then this waiter originally had enough extra signals to | ||
| 312 | + consume, up until the time its group was closed. */ | ||
| 313 | + goto done; | ||
| 314 | + } | ||
| 315 | + | ||
| 316 | + /* If there is an available signal, don't block. | ||
| 317 | + If __g1_start has advanced at all, then we must be in G1 | ||
| 318 | + by now, perhaps in the process of switching back to an older | ||
| 319 | + G2, but in either case we're allowed to consume the available | ||
| 320 | + signal and should not block anymore. */ | ||
| 321 | + if ((int)(signals - lowseq) >= 2) | ||
| 322 | break; | ||
| 323 | |||
| 324 | /* No signals available after spinning, so prepare to block. | ||
| 325 | We first acquire a group reference and use acquire MO for that so | ||
| 326 | that we synchronize with the dummy read-modify-write in | ||
| 327 | __condvar_quiesce_and_switch_g1 if we read from that. In turn, | ||
| 328 | - in this case this will make us see the closed flag on __g_signals | ||
| 329 | - that designates a concurrent attempt to reuse the group's slot. | ||
| 330 | + in this case this will make us see the advancement of __g_signals | ||
| 331 | + to the upcoming new g1_start that occurs with a concurrent | ||
| 332 | + attempt to reuse the group's slot. | ||
| 333 | We use acquire MO for the __g_signals check to make the | ||
| 334 | __g1_start check work (see spinning above). | ||
| 335 | Note that the group reference acquisition will not mask the | ||
| 336 | @@ -482,15 +482,24 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex, | ||
| 337 | an atomic read-modify-write operation and thus extend the release | ||
| 338 | sequence. */ | ||
| 339 | atomic_fetch_add_acquire (cond->__data.__g_refs + g, 2); | ||
| 340 | - if (((atomic_load_acquire (cond->__data.__g_signals + g) & 1) != 0) | ||
| 341 | - || (seq < (__condvar_load_g1_start_relaxed (cond) >> 1))) | ||
| 342 | + signals = atomic_load_acquire (cond->__data.__g_signals + g); | ||
| 343 | + g1_start = __condvar_load_g1_start_relaxed (cond); | ||
| 344 | + lowseq = (g1_start & 1) == g ? signals : g1_start & ~1U; | ||
| 345 | + | ||
| 346 | + if (seq < (g1_start >> 1)) | ||
| 347 | { | ||
| 348 | - /* Our group is closed. Wake up any signalers that might be | ||
| 349 | - waiting. */ | ||
| 350 | + /* group is closed already, so don't block */ | ||
| 351 | __condvar_dec_grefs (cond, g, private); | ||
| 352 | goto done; | ||
| 353 | } | ||
| 354 | |||
| 355 | + if ((int)(signals - lowseq) >= 2) | ||
| 356 | + { | ||
| 357 | + /* a signal showed up or G1/G2 switched after we grabbed the refcount */ | ||
| 358 | + __condvar_dec_grefs (cond, g, private); | ||
| 359 | + break; | ||
| 360 | + } | ||
| 361 | + | ||
| 362 | // Now block. | ||
| 363 | struct _pthread_cleanup_buffer buffer; | ||
| 364 | struct _condvar_cleanup_buffer cbuffer; | ||
| 365 | @@ -501,7 +510,7 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex, | ||
| 366 | __pthread_cleanup_push (&buffer, __condvar_cleanup_waiting, &cbuffer); | ||
| 367 | |||
| 368 | err = __futex_abstimed_wait_cancelable64 ( | ||
| 369 | - cond->__data.__g_signals + g, 0, clockid, abstime, private); | ||
| 370 | + cond->__data.__g_signals + g, signals, clockid, abstime, private); | ||
| 371 | |||
| 372 | __pthread_cleanup_pop (&buffer, 0); | ||
| 373 | |||
| 374 | @@ -524,6 +533,8 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex, | ||
| 375 | signals = atomic_load_acquire (cond->__data.__g_signals + g); | ||
| 376 | } | ||
| 377 | |||
| 378 | + if (seq < (__condvar_load_g1_start_relaxed (cond) >> 1)) | ||
| 379 | + goto done; | ||
| 380 | } | ||
| 381 | /* Try to grab a signal. Use acquire MO so that we see an up-to-date value | ||
| 382 | of __g1_start below (see spinning above for a similar case). In | ||
| 383 | @@ -532,69 +543,6 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex, | ||
| 384 | while (!atomic_compare_exchange_weak_acquire (cond->__data.__g_signals + g, | ||
| 385 | &signals, signals - 2)); | ||
| 386 | |||
| 387 | - /* We consumed a signal but we could have consumed from a more recent group | ||
| 388 | - that aliased with ours due to being in the same group slot. If this | ||
| 389 | - might be the case our group must be closed as visible through | ||
| 390 | - __g1_start. */ | ||
| 391 | - uint64_t g1_start = __condvar_load_g1_start_relaxed (cond); | ||
| 392 | - if (seq < (g1_start >> 1)) | ||
| 393 | - { | ||
| 394 | - /* We potentially stole a signal from a more recent group but we do not | ||
| 395 | - know which group we really consumed from. | ||
| 396 | - We do not care about groups older than current G1 because they are | ||
| 397 | - closed; we could have stolen from these, but then we just add a | ||
| 398 | - spurious wake-up for the current groups. | ||
| 399 | - We will never steal a signal from current G2 that was really intended | ||
| 400 | - for G2 because G2 never receives signals (until it becomes G1). We | ||
| 401 | - could have stolen a signal from G2 that was conservatively added by a | ||
| 402 | - previous waiter that also thought it stole a signal -- but given that | ||
| 403 | - that signal was added unnecessarily, it's not a problem if we steal | ||
| 404 | - it. | ||
| 405 | - Thus, the remaining case is that we could have stolen from the current | ||
| 406 | - G1, where "current" means the __g1_start value we observed. However, | ||
| 407 | - if the current G1 does not have the same slot index as we do, we did | ||
| 408 | - not steal from it and do not need to undo that. This is the reason | ||
| 409 | - for putting a bit with G2's index into__g1_start as well. */ | ||
| 410 | - if (((g1_start & 1) ^ 1) == g) | ||
| 411 | - { | ||
| 412 | - /* We have to conservatively undo our potential mistake of stealing | ||
| 413 | - a signal. We can stop trying to do that when the current G1 | ||
| 414 | - changes because other spinning waiters will notice this too and | ||
| 415 | - __condvar_quiesce_and_switch_g1 has checked that there are no | ||
| 416 | - futex waiters anymore before switching G1. | ||
| 417 | - Relaxed MO is fine for the __g1_start load because we need to | ||
| 418 | - merely be able to observe this fact and not have to observe | ||
| 419 | - something else as well. | ||
| 420 | - ??? Would it help to spin for a little while to see whether the | ||
| 421 | - current G1 gets closed? This might be worthwhile if the group is | ||
| 422 | - small or close to being closed. */ | ||
| 423 | - unsigned int s = atomic_load_relaxed (cond->__data.__g_signals + g); | ||
| 424 | - while (__condvar_load_g1_start_relaxed (cond) == g1_start) | ||
| 425 | - { | ||
| 426 | - /* Try to add a signal. We don't need to acquire the lock | ||
| 427 | - because at worst we can cause a spurious wake-up. If the | ||
| 428 | - group is in the process of being closed (LSB is true), this | ||
| 429 | - has an effect similar to us adding a signal. */ | ||
| 430 | - if (((s & 1) != 0) | ||
| 431 | - || atomic_compare_exchange_weak_relaxed | ||
| 432 | - (cond->__data.__g_signals + g, &s, s + 2)) | ||
| 433 | - { | ||
| 434 | - /* If we added a signal, we also need to add a wake-up on | ||
| 435 | - the futex. We also need to do that if we skipped adding | ||
| 436 | - a signal because the group is being closed because | ||
| 437 | - while __condvar_quiesce_and_switch_g1 could have closed | ||
| 438 | - the group, it might stil be waiting for futex waiters to | ||
| 439 | - leave (and one of those waiters might be the one we stole | ||
| 440 | - the signal from, which cause it to block using the | ||
| 441 | - futex). */ | ||
| 442 | - futex_wake (cond->__data.__g_signals + g, 1, private); | ||
| 443 | - break; | ||
| 444 | - } | ||
| 445 | - /* TODO Back off. */ | ||
| 446 | - } | ||
| 447 | - } | ||
| 448 | - } | ||
| 449 | - | ||
| 450 | done: | ||
| 451 | |||
| 452 | /* Confirm that we have been woken. We do that before acquiring the mutex | ||
| 453 | -- | ||
| 454 | 2.49.0 | ||
| 455 | |||
diff --git a/meta/recipes-core/glibc/glibc_2.35.bb b/meta/recipes-core/glibc/glibc_2.35.bb index 1ea4d5a252..f15e031971 100644 --- a/meta/recipes-core/glibc/glibc_2.35.bb +++ b/meta/recipes-core/glibc/glibc_2.35.bb | |||
| @@ -62,6 +62,7 @@ SRC_URI = "${GLIBC_GIT_URI};branch=${SRCBRANCH};name=glibc \ | |||
| 62 | file://0023-timezone-Make-shell-interpreter-overridable-in-tzsel.patch \ | 62 | file://0023-timezone-Make-shell-interpreter-overridable-in-tzsel.patch \ |
| 63 | file://0024-fix-create-thread-failed-in-unprivileged-process-BZ-.patch \ | 63 | file://0024-fix-create-thread-failed-in-unprivileged-process-BZ-.patch \ |
| 64 | file://0025-CVE-2025-4802.patch \ | 64 | file://0025-CVE-2025-4802.patch \ |
| 65 | file://0026-PR25847-1.patch \ | ||
| 65 | \ | 66 | \ |
| 66 | file://0001-Revert-Linux-Implement-a-useful-version-of-_startup_.patch \ | 67 | file://0001-Revert-Linux-Implement-a-useful-version-of-_startup_.patch \ |
| 67 | file://0002-get_nscd_addresses-Fix-subscript-typos-BZ-29605.patch \ | 68 | file://0002-get_nscd_addresses-Fix-subscript-typos-BZ-29605.patch \ |
