Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
1b7203fd
Commit
1b7203fd
authored
Nov 04, 2013
by
Guido van Rossum
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
asyncio: Locks improvements by Arnaud Faure: better repr(), change Conditio\
n structure.
parent
9c6f1096
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
124 additions
and
25 deletions
+124
-25
Lib/asyncio/locks.py
Lib/asyncio/locks.py
+54
-24
Lib/test/test_asyncio/test_locks.py
Lib/test/test_asyncio/test_locks.py
+70
-1
No files found.
Lib/asyncio/locks.py
View file @
1b7203fd
...
...
@@ -155,9 +155,11 @@ class Event:
self
.
_loop
=
events
.
get_event_loop
()
def
__repr__
(
self
):
# TODO: add waiters:N if > 0.
res
=
super
().
__repr__
()
return
'<{} [{}]>'
.
format
(
res
[
1
:
-
1
],
'set'
if
self
.
_value
else
'unset'
)
extra
=
'set'
if
self
.
_value
else
'unset'
if
self
.
_waiters
:
extra
=
'{},waiters:{}'
.
format
(
extra
,
len
(
self
.
_waiters
))
return
'<{} [{}]>'
.
format
(
res
[
1
:
-
1
],
extra
)
def
is_set
(
self
):
"""Return true if and only if the internal flag is true."""
...
...
@@ -201,20 +203,38 @@ class Event:
self
.
_waiters
.
remove
(
fut
)
# TODO: Why is this a Lock subclass? threading.Condition *has* a lock.
class
Condition
(
Lock
):
"""A Condition implementation.
class
Condition
:
"""A Condition implementation, our equivalent to threading.Condition.
This class implements condition variable objects. A condition variable
allows one or more coroutines to wait until they are notified by another
coroutine.
A new Lock object is created and used as the underlying lock.
"""
def
__init__
(
self
,
*
,
loop
=
None
):
super
().
__init__
(
loop
=
loop
)
self
.
_condition_waiters
=
collections
.
deque
()
if
loop
is
not
None
:
self
.
_loop
=
loop
else
:
self
.
_loop
=
events
.
get_event_loop
()
# TODO: Add __repr__() with len(_condition_waiters).
# Lock as an attribute as in threading.Condition.
lock
=
Lock
(
loop
=
self
.
_loop
)
self
.
_lock
=
lock
# Export the lock's locked(), acquire() and release() methods.
self
.
locked
=
lock
.
locked
self
.
acquire
=
lock
.
acquire
self
.
release
=
lock
.
release
self
.
_waiters
=
collections
.
deque
()
def
__repr__
(
self
):
res
=
super
().
__repr__
()
extra
=
'locked'
if
self
.
locked
()
else
'unlocked'
if
self
.
_waiters
:
extra
=
'{},waiters:{}'
.
format
(
extra
,
len
(
self
.
_waiters
))
return
'<{} [{}]>'
.
format
(
res
[
1
:
-
1
],
extra
)
@
tasks
.
coroutine
def
wait
(
self
):
...
...
@@ -228,19 +248,19 @@ class Condition(Lock):
the same condition variable in another coroutine. Once
awakened, it re-acquires the lock and returns True.
"""
if
not
self
.
_locked
:
if
not
self
.
locked
()
:
raise
RuntimeError
(
'cannot wait on un-acquired lock'
)
keep_lock
=
True
self
.
release
()
try
:
fut
=
futures
.
Future
(
loop
=
self
.
_loop
)
self
.
_
condition_
waiters
.
append
(
fut
)
self
.
_waiters
.
append
(
fut
)
try
:
yield
from
fut
return
True
finally
:
self
.
_
condition_
waiters
.
remove
(
fut
)
self
.
_waiters
.
remove
(
fut
)
except
GeneratorExit
:
keep_lock
=
False
# Prevent yield in finally clause.
...
...
@@ -275,11 +295,11 @@ class Condition(Lock):
wait() call until it can reacquire the lock. Since notify() does
not release the lock, its caller should.
"""
if
not
self
.
_locked
:
if
not
self
.
locked
()
:
raise
RuntimeError
(
'cannot notify on un-acquired lock'
)
idx
=
0
for
fut
in
self
.
_
condition_
waiters
:
for
fut
in
self
.
_waiters
:
if
idx
>=
n
:
break
...
...
@@ -293,7 +313,17 @@ class Condition(Lock):
calling thread has not acquired the lock when this method is called,
a RuntimeError is raised.
"""
self
.
notify
(
len
(
self
.
_condition_waiters
))
self
.
notify
(
len
(
self
.
_waiters
))
def
__enter__
(
self
):
return
self
.
_lock
.
__enter__
()
def
__exit__
(
self
,
*
args
):
return
self
.
_lock
.
__exit__
(
*
args
)
def
__iter__
(
self
):
yield
from
self
.
acquire
()
return
self
class
Semaphore
:
...
...
@@ -310,10 +340,10 @@ class Semaphore:
counter; it defaults to 1. If the value given is less than 0,
ValueError is raised.
The second optional argument determin
s can semophore be released more than
initial internal counter value; it defaults to False. If the value given
is True and number of release() is more than number of successfull
acquire() calls ValueError is raised.
The second optional argument determin
es if the semaphore can be released
more than initial internal counter value; it defaults to False. If the
value given is True and number of release() is more than number of
successful
acquire() calls ValueError is raised.
"""
def
__init__
(
self
,
value
=
1
,
bound
=
False
,
*
,
loop
=
None
):
...
...
@@ -330,12 +360,12 @@ class Semaphore:
self
.
_loop
=
events
.
get_event_loop
()
def
__repr__
(
self
):
# TODO: add waiters:N if > 0.
res
=
super
().
__repr__
()
return
'<{} [{}]>'
.
format
(
res
[
1
:
-
1
],
'locked'
if
self
.
_locked
else
'unlocked,value:{}'
.
format
(
self
.
_value
))
extra
=
'locked'
if
self
.
_locked
else
'unlocked,value:{}'
.
format
(
self
.
_value
)
if
self
.
_waiters
:
extra
=
'{},waiters:{}'
.
format
(
extra
,
len
(
self
.
_waiters
))
return
'<{} [{}]>'
.
format
(
res
[
1
:
-
1
],
extra
)
def
locked
(
self
):
"""Returns True if semaphore can not be acquired immediately."""
...
...
@@ -373,7 +403,7 @@ class Semaphore:
When it was zero on entry and another coroutine is waiting for it to
become larger than zero again, wake up that coroutine.
If Semaphore is create
with "bound" param
ter equals true, then
If Semaphore is create
d with "bound" parame
ter equals true, then
release() method checks to make sure its current value doesn't exceed
its initial value. If it does, ValueError is raised.
"""
...
...
Lib/test/test_asyncio/test_locks.py
View file @
1b7203fd
...
...
@@ -2,6 +2,7 @@
import
unittest
import
unittest.mock
import
re
from
asyncio
import
events
from
asyncio
import
futures
...
...
@@ -10,6 +11,15 @@ from asyncio import tasks
from
asyncio
import
test_utils
STR_RGX_REPR
=
(
r'^<(?P<class>.*?) object at (?P<address>.*?)'
r'\
[(?P<ex
tras>'
r'(set|unset|locked|unlocked)(,value:\
d)?(,w
aiters:\
d+)?
'
r'
)
\
]
>
\
Z
'
)
RGX_REPR = re.compile(STR_RGX_REPR)
class LockTests(unittest.TestCase):
def setUp(self):
...
...
@@ -38,6 +48,7 @@ class LockTests(unittest.TestCase):
def test_repr(self):
lock = locks.Lock(loop=self.loop)
self.assertTrue(repr(lock).endswith('
[
unlocked
]
>
'))
self.assertTrue(RGX_REPR.match(repr(lock)))
@tasks.coroutine
def acquire_lock():
...
...
@@ -45,6 +56,7 @@ class LockTests(unittest.TestCase):
self.loop.run_until_complete(acquire_lock())
self.assertTrue(repr(lock).endswith('
[
locked
]
>
'))
self.assertTrue(RGX_REPR.match(repr(lock)))
def test_lock(self):
lock = locks.Lock(loop=self.loop)
...
...
@@ -239,9 +251,16 @@ class EventTests(unittest.TestCase):
def
test_repr
(
self
):
ev
=
locks
.
Event
(
loop
=
self
.
loop
)
self
.
assertTrue
(
repr
(
ev
).
endswith
(
'[unset]>'
))
match
=
RGX_REPR
.
match
(
repr
(
ev
))
self
.
assertEqual
(
match
.
group
(
'extras'
),
'unset'
)
ev
.
set
()
self
.
assertTrue
(
repr
(
ev
).
endswith
(
'[set]>'
))
self
.
assertTrue
(
RGX_REPR
.
match
(
repr
(
ev
)))
ev
.
_waiters
.
append
(
unittest
.
mock
.
Mock
())
self
.
assertTrue
(
'waiters:1'
in
repr
(
ev
))
self
.
assertTrue
(
RGX_REPR
.
match
(
repr
(
ev
)))
def
test_wait
(
self
):
ev
=
locks
.
Event
(
loop
=
self
.
loop
)
...
...
@@ -440,7 +459,7 @@ class ConditionTests(unittest.TestCase):
self
.
assertRaises
(
futures
.
CancelledError
,
self
.
loop
.
run_until_complete
,
wait
)
self
.
assertFalse
(
cond
.
_
condition_
waiters
)
self
.
assertFalse
(
cond
.
_waiters
)
self
.
assertTrue
(
cond
.
locked
())
def
test_wait_unacquired
(
self
):
...
...
@@ -600,6 +619,45 @@ class ConditionTests(unittest.TestCase):
cond
=
locks
.
Condition
(
loop
=
self
.
loop
)
self
.
assertRaises
(
RuntimeError
,
cond
.
notify_all
)
def
test_repr
(
self
):
cond
=
locks
.
Condition
(
loop
=
self
.
loop
)
self
.
assertTrue
(
'unlocked'
in
repr
(
cond
))
self
.
assertTrue
(
RGX_REPR
.
match
(
repr
(
cond
)))
self
.
loop
.
run_until_complete
(
cond
.
acquire
())
self
.
assertTrue
(
'locked'
in
repr
(
cond
))
cond
.
_waiters
.
append
(
unittest
.
mock
.
Mock
())
self
.
assertTrue
(
'waiters:1'
in
repr
(
cond
))
self
.
assertTrue
(
RGX_REPR
.
match
(
repr
(
cond
)))
cond
.
_waiters
.
append
(
unittest
.
mock
.
Mock
())
self
.
assertTrue
(
'waiters:2'
in
repr
(
cond
))
self
.
assertTrue
(
RGX_REPR
.
match
(
repr
(
cond
)))
def
test_context_manager
(
self
):
cond
=
locks
.
Condition
(
loop
=
self
.
loop
)
@
tasks
.
coroutine
def
acquire_cond
():
return
(
yield
from
cond
)
with
self
.
loop
.
run_until_complete
(
acquire_cond
()):
self
.
assertTrue
(
cond
.
locked
())
self
.
assertFalse
(
cond
.
locked
())
def
test_context_manager_no_yield
(
self
):
cond
=
locks
.
Condition
(
loop
=
self
.
loop
)
try
:
with
cond
:
self
.
fail
(
'RuntimeError is not raised in with expression'
)
except
RuntimeError
as
err
:
self
.
assertEqual
(
str
(
err
),
'"yield from" should be used as context manager expression'
)
class
SemaphoreTests
(
unittest
.
TestCase
):
...
...
@@ -629,9 +687,20 @@ class SemaphoreTests(unittest.TestCase):
def
test_repr
(
self
):
sem
=
locks
.
Semaphore
(
loop
=
self
.
loop
)
self
.
assertTrue
(
repr
(
sem
).
endswith
(
'[unlocked,value:1]>'
))
self
.
assertTrue
(
RGX_REPR
.
match
(
repr
(
sem
)))
self
.
loop
.
run_until_complete
(
sem
.
acquire
())
self
.
assertTrue
(
repr
(
sem
).
endswith
(
'[locked]>'
))
self
.
assertTrue
(
'waiters'
not
in
repr
(
sem
))
self
.
assertTrue
(
RGX_REPR
.
match
(
repr
(
sem
)))
sem
.
_waiters
.
append
(
unittest
.
mock
.
Mock
())
self
.
assertTrue
(
'waiters:1'
in
repr
(
sem
))
self
.
assertTrue
(
RGX_REPR
.
match
(
repr
(
sem
)))
sem
.
_waiters
.
append
(
unittest
.
mock
.
Mock
())
self
.
assertTrue
(
'waiters:2'
in
repr
(
sem
))
self
.
assertTrue
(
RGX_REPR
.
match
(
repr
(
sem
)))
def
test_semaphore
(
self
):
sem
=
locks
.
Semaphore
(
loop
=
self
.
loop
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment