@@ -89,6 +89,9 @@ type snapshotRequester interface {
8989type SnapshotQueue struct {
9090 snapshotGranter granter
9191 mu struct {
92+ // Reminder: this mutex must not be held when calling into
93+ // snapshotGranter, as documented near the declaration of requester and
94+ // granter.
9295 syncutil.Mutex
9396 q * queue.Queue [* snapshotWorkItem ]
9497 }
@@ -164,6 +167,13 @@ func (s *SnapshotQueue) Admit(ctx context.Context, count int64) error {
164167 return nil
165168 }
166169 // We were unable to get tokens for admission, so we queue.
170+ //
171+ // Reminder: there is a race here where a call to hasWaitingRequests after
172+ // tryGet and before the mutex is acquired and the item is added to the
173+ // queue will not see the waiting request. Such a race would result in an
174+ // end state where the granter has resources and the requester has waiting
175+ // requests. This is harmless since hasWaitingRequests is called
176+ // periodically at a high enough frequency when tokens are limited.
167177 shouldRelease := true
168178 item := newSnapshotWorkItem (count )
169179 defer func () {
@@ -182,11 +192,15 @@ func (s *SnapshotQueue) Admit(ctx context.Context, count int64) error {
182192 select {
183193 case <- ctx .Done ():
184194 waitDur := timeutil .Since (item .enqueueingTime ).Nanoseconds ()
195+ // INVARIANT: tokensToReturn >= 0, since item.count >= 0.
196+ var tokensToReturn int64
185197 func () {
186198 s .mu .Lock ()
187199 defer s .mu .Unlock ()
188200 if ! item .mu .inQueue {
189- s .snapshotGranter .returnGrant (item .count )
201+ // NB: we must call snapshotGranter.returnGrant after releasing the
202+ // mutex.
203+ tokensToReturn = item .count
190204 }
191205 // TODO(aaditya): Ideally, we also remove the item from the actual queue.
192206 // Right now, if we cancel the work, it remains in the queue. A call to
@@ -196,6 +210,9 @@ func (s *SnapshotQueue) Admit(ctx context.Context, count int64) error {
196210 // non-ideal behavior, but still provides accurate token accounting.
197211 item .mu .cancelled = true
198212 }()
213+ if tokensToReturn != 0 {
214+ s .snapshotGranter .returnGrant (tokensToReturn )
215+ }
199216 shouldRelease = false
200217 s .metrics .WaitDurations .RecordValue (waitDur )
201218 var deadlineSubstring string
0 commit comments