mirror of https://github.com/python/cpython
Also rewrote the guts of asyncio.Semaphore (patch by manipopopo). (Merge 3.5->3.6.)
This commit is contained in:
commit
a6b1031b76
|
@ -411,6 +411,13 @@ class Semaphore(_ContextManagerMixin):
|
||||||
extra = '{},waiters:{}'.format(extra, len(self._waiters))
|
extra = '{},waiters:{}'.format(extra, len(self._waiters))
|
||||||
return '<{} [{}]>'.format(res[1:-1], extra)
|
return '<{} [{}]>'.format(res[1:-1], extra)
|
||||||
|
|
||||||
|
def _wake_up_next(self):
|
||||||
|
while self._waiters:
|
||||||
|
waiter = self._waiters.popleft()
|
||||||
|
if not waiter.done():
|
||||||
|
waiter.set_result(None)
|
||||||
|
return
|
||||||
|
|
||||||
def locked(self):
|
def locked(self):
|
||||||
"""Returns True if semaphore can not be acquired immediately."""
|
"""Returns True if semaphore can not be acquired immediately."""
|
||||||
return self._value == 0
|
return self._value == 0
|
||||||
|
@ -425,18 +432,19 @@ class Semaphore(_ContextManagerMixin):
|
||||||
called release() to make it larger than 0, and then return
|
called release() to make it larger than 0, and then return
|
||||||
True.
|
True.
|
||||||
"""
|
"""
|
||||||
if not self._waiters and self._value > 0:
|
while self._value <= 0:
|
||||||
self._value -= 1
|
|
||||||
return True
|
|
||||||
|
|
||||||
fut = futures.Future(loop=self._loop)
|
fut = futures.Future(loop=self._loop)
|
||||||
self._waiters.append(fut)
|
self._waiters.append(fut)
|
||||||
try:
|
try:
|
||||||
yield from fut
|
yield from fut
|
||||||
|
except:
|
||||||
|
# See the similar code in Queue.get.
|
||||||
|
fut.cancel()
|
||||||
|
if self._value > 0 and not fut.cancelled():
|
||||||
|
self._wake_up_next()
|
||||||
|
raise
|
||||||
self._value -= 1
|
self._value -= 1
|
||||||
return True
|
return True
|
||||||
finally:
|
|
||||||
self._waiters.remove(fut)
|
|
||||||
|
|
||||||
def release(self):
|
def release(self):
|
||||||
"""Release a semaphore, incrementing the internal counter by one.
|
"""Release a semaphore, incrementing the internal counter by one.
|
||||||
|
@ -444,10 +452,7 @@ class Semaphore(_ContextManagerMixin):
|
||||||
become larger than zero again, wake up that coroutine.
|
become larger than zero again, wake up that coroutine.
|
||||||
"""
|
"""
|
||||||
self._value += 1
|
self._value += 1
|
||||||
for waiter in self._waiters:
|
self._wake_up_next()
|
||||||
if not waiter.done():
|
|
||||||
waiter.set_result(True)
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
class BoundedSemaphore(Semaphore):
|
class BoundedSemaphore(Semaphore):
|
||||||
|
|
|
@ -7,7 +7,6 @@ import re
|
||||||
import asyncio
|
import asyncio
|
||||||
from asyncio import test_utils
|
from asyncio import test_utils
|
||||||
|
|
||||||
|
|
||||||
STR_RGX_REPR = (
|
STR_RGX_REPR = (
|
||||||
r'^<(?P<class>.*?) object at (?P<address>.*?)'
|
r'^<(?P<class>.*?) object at (?P<address>.*?)'
|
||||||
r'\[(?P<extras>'
|
r'\[(?P<extras>'
|
||||||
|
@ -783,22 +782,20 @@ class SemaphoreTests(test_utils.TestCase):
|
||||||
|
|
||||||
test_utils.run_briefly(self.loop)
|
test_utils.run_briefly(self.loop)
|
||||||
self.assertEqual(0, sem._value)
|
self.assertEqual(0, sem._value)
|
||||||
self.assertEqual([1, 2, 3], result)
|
self.assertEqual(3, len(result))
|
||||||
self.assertTrue(sem.locked())
|
self.assertTrue(sem.locked())
|
||||||
self.assertEqual(1, len(sem._waiters))
|
self.assertEqual(1, len(sem._waiters))
|
||||||
self.assertEqual(0, sem._value)
|
self.assertEqual(0, sem._value)
|
||||||
|
|
||||||
self.assertTrue(t1.done())
|
self.assertTrue(t1.done())
|
||||||
self.assertTrue(t1.result())
|
self.assertTrue(t1.result())
|
||||||
self.assertTrue(t2.done())
|
race_tasks = [t2, t3, t4]
|
||||||
self.assertTrue(t2.result())
|
done_tasks = [t for t in race_tasks if t.done() and t.result()]
|
||||||
self.assertTrue(t3.done())
|
self.assertTrue(2, len(done_tasks))
|
||||||
self.assertTrue(t3.result())
|
|
||||||
self.assertFalse(t4.done())
|
|
||||||
|
|
||||||
# cleanup locked semaphore
|
# cleanup locked semaphore
|
||||||
sem.release()
|
sem.release()
|
||||||
self.loop.run_until_complete(t4)
|
self.loop.run_until_complete(asyncio.gather(*race_tasks))
|
||||||
|
|
||||||
def test_acquire_cancel(self):
|
def test_acquire_cancel(self):
|
||||||
sem = asyncio.Semaphore(loop=self.loop)
|
sem = asyncio.Semaphore(loop=self.loop)
|
||||||
|
@ -809,7 +806,44 @@ class SemaphoreTests(test_utils.TestCase):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
asyncio.CancelledError,
|
asyncio.CancelledError,
|
||||||
self.loop.run_until_complete, acquire)
|
self.loop.run_until_complete, acquire)
|
||||||
self.assertFalse(sem._waiters)
|
self.assertTrue((not sem._waiters) or
|
||||||
|
all(waiter.done() for waiter in sem._waiters))
|
||||||
|
|
||||||
|
def test_acquire_cancel_before_awoken(self):
|
||||||
|
sem = asyncio.Semaphore(value=0, loop=self.loop)
|
||||||
|
|
||||||
|
t1 = asyncio.Task(sem.acquire(), loop=self.loop)
|
||||||
|
t2 = asyncio.Task(sem.acquire(), loop=self.loop)
|
||||||
|
t3 = asyncio.Task(sem.acquire(), loop=self.loop)
|
||||||
|
t4 = asyncio.Task(sem.acquire(), loop=self.loop)
|
||||||
|
|
||||||
|
test_utils.run_briefly(self.loop)
|
||||||
|
|
||||||
|
sem.release()
|
||||||
|
t1.cancel()
|
||||||
|
t2.cancel()
|
||||||
|
|
||||||
|
test_utils.run_briefly(self.loop)
|
||||||
|
num_done = sum(t.done() for t in [t3, t4])
|
||||||
|
self.assertEqual(num_done, 1)
|
||||||
|
|
||||||
|
t3.cancel()
|
||||||
|
t4.cancel()
|
||||||
|
test_utils.run_briefly(self.loop)
|
||||||
|
|
||||||
|
def test_acquire_hang(self):
|
||||||
|
sem = asyncio.Semaphore(value=0, loop=self.loop)
|
||||||
|
|
||||||
|
t1 = asyncio.Task(sem.acquire(), loop=self.loop)
|
||||||
|
t2 = asyncio.Task(sem.acquire(), loop=self.loop)
|
||||||
|
|
||||||
|
test_utils.run_briefly(self.loop)
|
||||||
|
|
||||||
|
sem.release()
|
||||||
|
t1.cancel()
|
||||||
|
|
||||||
|
test_utils.run_briefly(self.loop)
|
||||||
|
self.assertTrue(sem.locked())
|
||||||
|
|
||||||
def test_release_not_acquired(self):
|
def test_release_not_acquired(self):
|
||||||
sem = asyncio.BoundedSemaphore(loop=self.loop)
|
sem = asyncio.BoundedSemaphore(loop=self.loop)
|
||||||
|
|
|
@ -193,7 +193,8 @@ Library
|
||||||
- Issue #25034: Fix string.Formatter problem with auto-numbering and
|
- Issue #25034: Fix string.Formatter problem with auto-numbering and
|
||||||
nested format_specs. Patch by Anthon van der Neut.
|
nested format_specs. Patch by Anthon van der Neut.
|
||||||
|
|
||||||
- Issue #25233: Rewrite the guts of asyncio.Queue to be more understandable and correct.
|
- Issue #25233: Rewrite the guts of asyncio.Queue and
|
||||||
|
asyncio.Semaphore to be more understandable and correct.
|
||||||
|
|
||||||
- Issue #23600: Default implementation of tzinfo.fromutc() was returning
|
- Issue #23600: Default implementation of tzinfo.fromutc() was returning
|
||||||
wrong results in some cases.
|
wrong results in some cases.
|
||||||
|
|
Loading…
Reference in New Issue