Wait does c.cond.Wait() but will also return if the context provided is done. All the documentation of sync.Cond.Wait() applies, but it's especially important to remember that the mutex of the cond should be held while Wait() is called (and mutex will be held once it returns)
(ctx context.Context)
| 240 | // All the documentation of sync.Cond.Wait() applies, but it's especially important to remember that the mutex of |
| 241 | // the cond should be held while Wait() is called (and mutex will be held once it returns) |
| 242 | func (c contextCond) Wait(ctx context.Context) { |
| 243 | // "condWait" goroutine does q.cond.Wait() and signals through condWait channel. |
| 244 | condWait := make(chan struct{}) |
| 245 | go func() { |
| 246 | if c.testHookBeforeWaiting != nil { |
| 247 | c.testHookBeforeWaiting() |
| 248 | } |
| 249 | c.Cond.Wait() |
| 250 | close(condWait) |
| 251 | }() |
| 252 | |
| 253 | // "waiting" goroutine: signals that the condWait goroutine has started waiting. |
| 254 | // Notice that a closed waiting channel implies that the goroutine above has started waiting |
| 255 | // (because it has unlocked the mutex), but the other way is not true: |
| 256 | // - condWait it may have unlocked and is waiting, but someone else locked the mutex faster than us: |
| 257 | // in this case that caller will eventually unlock, and we'll be able to enter here. |
| 258 | // - condWait called Wait(), unlocked, received a broadcast and locked again faster than we were able to lock here: |
| 259 | // in this case condWait channel will be closed, and this goroutine will be waiting until we unlock. |
| 260 | waiting := make(chan struct{}) |
| 261 | go func() { |
| 262 | c.L.Lock() |
| 263 | close(waiting) |
| 264 | c.L.Unlock() |
| 265 | }() |
| 266 | |
| 267 | select { |
| 268 | case <-condWait: |
| 269 | // We don't know whether the waiting goroutine is done or not, but we don't care: |
| 270 | // it will be done once nobody is fighting for the mutex anymore. |
| 271 | case <-ctx.Done(): |
| 272 | // In order to avoid leaking the condWait goroutine, we can send a broadcast. |
| 273 | // Before sending the broadcast we need to make sure that condWait goroutine is already waiting (or has already waited). |
| 274 | select { |
| 275 | case <-condWait: |
| 276 | // No need to broadcast as q.cond.Wait() has returned already. |
| 277 | return |
| 278 | case <-waiting: |
| 279 | // q.cond.Wait() might be still waiting (or maybe not!), so we'll poke it just in case. |
| 280 | c.Broadcast() |
| 281 | } |
| 282 | |
| 283 | // Make sure we are not waiting anymore, we need to do that before returning as the caller will need to unlock the mutex. |
| 284 | <-condWait |
| 285 | } |
| 286 | } |