@@ -47,7 +47,7 @@ def __init__(self, maxsize=0, *, loop=None):
4747
4848 # Futures.
4949 self ._getters = collections .deque ()
50- # Pairs of (item, Future).
50+ # Futures
5151 self ._putters = collections .deque ()
5252 self ._unfinished_tasks = 0
5353 self ._finished = locks .Event (loop = self ._loop )
@@ -98,7 +98,7 @@ def _consume_done_getters(self):
9898
9999 def _consume_done_putters (self ):
100100 # Delete waiters at the head of the put() queue who've timed out.
101- while self ._putters and self ._putters [0 ][ 1 ] .done ():
101+ while self ._putters and self ._putters [0 ].done ():
102102 self ._putters .popleft ()
103103
104104 def qsize (self ):
@@ -148,8 +148,9 @@ def put(self, item):
148148 elif self ._maxsize > 0 and self ._maxsize <= self .qsize ():
149149 waiter = futures .Future (loop = self ._loop )
150150
151- self ._putters .append (( item , waiter ) )
151+ self ._putters .append (waiter )
152152 yield from waiter
153+ self ._put (item )
153154
154155 else :
155156 self .__put_internal (item )
@@ -186,8 +187,7 @@ def get(self):
186187 self ._consume_done_putters ()
187188 if self ._putters :
188189 assert self .full (), 'queue not full, why are putters waiting?'
189- item , putter = self ._putters .popleft ()
190- self .__put_internal (item )
190+ putter = self ._putters .popleft ()
191191
192192 # When a getter runs and frees up a slot so this putter can
193193 # run, we need to defer the put for a tick to ensure that
@@ -201,9 +201,39 @@ def get(self):
201201 return self ._get ()
202202 else :
203203 waiter = futures .Future (loop = self ._loop )
204-
205204 self ._getters .append (waiter )
206- return (yield from waiter )
205+ try :
206+ return (yield from waiter )
207+ except futures .CancelledError :
208+ # if we get CancelledError, it means someone cancelled this
209+ # get() coroutine. But there is a chance that the waiter
210+ # already is ready and contains an item that has just been
211+ # removed from the queue. In this case, we need to put the item
212+ # back into the front of the queue. This get() must either
213+ # succeed without fault or, if it gets cancelled, it must be as
214+ # if it never happened.
215+ if waiter .done ():
216+ self ._put_it_back (waiter .result ())
217+ raise
218+
219+ def _put_it_back (self , item ):
220+ """
221+ This is called when we have a waiter to get() an item and this waiter
222+ gets cancelled. In this case, we put the item back: wake up another
223+ waiter or put it in the _queue.
224+ """
225+ self ._consume_done_getters ()
226+ if self ._getters :
227+ assert not self ._queue , (
228+ 'queue non-empty, why are getters waiting?' )
229+
230+ getter = self ._getters .popleft ()
231+ self ._put_internal (item )
232+
233+ # getter cannot be cancelled, we just removed done getters
234+ getter .set_result (item )
235+ else :
236+ self ._queue .appendleft (item )
207237
208238 def get_nowait (self ):
209239 """Remove and return an item from the queue.
@@ -213,8 +243,7 @@ def get_nowait(self):
213243 self ._consume_done_putters ()
214244 if self ._putters :
215245 assert self .full (), 'queue not full, why are putters waiting?'
216- item , putter = self ._putters .popleft ()
217- self .__put_internal (item )
246+ putter = self ._putters .popleft ()
218247 # Wake putter on next tick.
219248
220249 # getter cannot be cancelled, we just removed done putters
0 commit comments