Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gevent
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
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
gevent
Commits
3c624d73
Commit
3c624d73
authored
Mar 25, 2020
by
Jason Madden
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'RLock-acquire-timeout'
parents
a362a560
9b2108be
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
100 additions
and
27 deletions
+100
-27
CHANGES.rst
CHANGES.rst
+1
-1
src/gevent/_semaphore.py
src/gevent/_semaphore.py
+51
-21
src/gevent/lock.py
src/gevent/lock.py
+48
-5
No files found.
CHANGES.rst
View file @
3c624d73
...
@@ -7,7 +7,7 @@
...
@@ -7,7 +7,7 @@
1.5a5 (unreleased)
1.5a5 (unreleased)
==================
==================
-
Nothing changed yet
.
-
Make `gevent.lock.RLock.acquire` accept the *timeout* parameter
.
1.5a4 (2020-03-23)
1.5a4 (2020-03-23)
...
...
src/gevent/_semaphore.py
View file @
3c624d73
...
@@ -17,10 +17,15 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
...
@@ -17,10 +17,15 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
"""
"""
Semaphore(value=1) -> Semaphore
Semaphore(value=1) -> Semaphore
A semaphore manages a counter representing the number of release()
.. seealso:: :class:`BoundedSemaphore` for a safer version that prevents
calls minus the number of acquire() calls, plus an initial value.
some classes of bugs. If unsure, most users should opt for `BoundedSemaphore`.
The acquire() method blocks if necessary until it can return
without making the counter negative.
A semaphore manages a counter representing the number of `release`
calls minus the number of `acquire` calls, plus an initial value.
The `acquire` method blocks if necessary until it can return
without making the counter negative. A semaphore does not track ownership
by greenlets; any greenlet can call `release`, whether or not it has previously
called `acquire`.
If not given, ``value`` defaults to 1.
If not given, ``value`` defaults to 1.
...
@@ -29,16 +34,12 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
...
@@ -29,16 +34,12 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
This Semaphore's ``__exit__`` method does not call the trace function
This Semaphore's ``__exit__`` method does not call the trace function
on CPython, but does under PyPy.
on CPython, but does under PyPy.
.. seealso:: :class:`BoundedSemaphore` for a safer version that prevents
some classes of bugs.
.. versionchanged:: 1.4.0
.. versionchanged:: 1.4.0
Document that the order in which waiters are awakened is not specified. It was not
The order in which waiters are awakened is not specified. It was not
specified previously, but due to CPython implementation quirks usually went in FIFO order.
specified previously, but usually went in FIFO order.
.. versionchanged:: 1.5a3
.. versionchanged:: 1.5a3
Waiting greenlets are now awakened in the order in which they waited.
Waiting greenlets are now awakened in the order in which they waited.
.. versionchanged:: 1.5a3
.. versionchanged:: 1.5a3
The low-level ``rawlink`` method (most users won't use this) now automatically
The low-level ``rawlink`` method (most users won't use this) now automatically
unlinks waiters before calling them.
unlinks waiters before calling them.
...
@@ -56,19 +57,43 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
...
@@ -56,19 +57,43 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
return
'<%s counter=%s _links[%s]>'
%
params
return
'<%s counter=%s _links[%s]>'
%
params
def
locked
(
self
):
def
locked
(
self
):
"""Return a boolean indicating whether the semaphore can be acquired.
"""
Most useful with binary semaphores."""
Return a boolean indicating whether the semaphore can be
acquired (`False` if the semaphore *can* be acquired). Most
useful with binary semaphores (those with an initial value of 1).
:rtype: bool
"""
return
self
.
counter
<=
0
return
self
.
counter
<=
0
def
release
(
self
):
def
release
(
self
):
"""
"""
Release the semaphore, notifying any waiters if needed.
Release the semaphore, notifying any waiters if needed. There
is no return value.
.. note::
This can be used to over-release the semaphore.
(Release more times than it has been acquired or was initially
created with.)
This is usually a sign of a bug, but under some circumstances it can be
used deliberately, for example, to model the arrival of additional
resources.
:rtype: None
"""
"""
self
.
counter
+=
1
self
.
counter
+=
1
self
.
_check_and_notify
()
self
.
_check_and_notify
()
return
self
.
counter
return
self
.
counter
def
ready
(
self
):
def
ready
(
self
):
"""
Return a boolean indicating whether the semaphore can be
acquired (`True` if the semaphore can be acquired).
:rtype: bool
"""
return
self
.
counter
>
0
return
self
.
counter
>
0
def
_start_notify
(
self
):
def
_start_notify
(
self
):
...
@@ -84,18 +109,18 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
...
@@ -84,18 +109,18 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
def
wait
(
self
,
timeout
=
None
):
def
wait
(
self
,
timeout
=
None
):
"""
"""
wait(timeout=None) -> int
Wait until it is possible to acquire this semaphore, or until the optional
Wait until it is possible to acquire this semaphore, or until the optional
*timeout* elapses.
*timeout* elapses.
..
caution:: If this semaphore was initialized with a size
of 0,
..
note:: If this semaphore was initialized with a *value*
of 0,
this method will block forever if no timeout is given.
this method will block forever if no timeout is given.
:keyword float timeout: If given, specifies the maximum amount of seconds
:keyword float timeout: If given, specifies the maximum amount of seconds
this method will block.
this method will block.
:return: A number indicating how many times the semaphore can be acquired
:return: A number indicating how many times the semaphore can be acquired
before blocking.
before blocking. *This could be 0,* if other waiters acquired
the semaphore.
:rtype: int
"""
"""
if
self
.
counter
>
0
:
if
self
.
counter
>
0
:
return
self
.
counter
return
self
.
counter
...
@@ -109,15 +134,16 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
...
@@ -109,15 +134,16 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
Acquire the semaphore.
Acquire the semaphore.
..
caution:: If this semaphore was initialized with a size
of 0,
..
note:: If this semaphore was initialized with a *value*
of 0,
this method will block forever (unless a timeout is given or blocking is
this method will block forever (unless a timeout is given or blocking is
set to false).
set to false).
:keyword bool blocking: If True (the default), this function will block
:keyword bool blocking: If True (the default), this function will block
until the semaphore is acquired.
until the semaphore is acquired.
:keyword float timeout: If given, specifies the maximum amount of seconds
:keyword float timeout: If given, and *blocking* is true,
specifies the maximum amount of seconds
this method will block.
this method will block.
:return: A
boolean
indicating whether the semaphore was acquired.
:return: A
`bool`
indicating whether the semaphore was acquired.
If ``blocking`` is True and ``timeout`` is None (the default), then
If ``blocking`` is True and ``timeout`` is None (the default), then
(so long as this semaphore was initialized with a size greater than 0)
(so long as this semaphore was initialized with a size greater than 0)
this will always return True. If a timeout was given, and it expired before
this will always return True. If a timeout was given, and it expired before
...
@@ -172,9 +198,13 @@ class BoundedSemaphore(Semaphore):
...
@@ -172,9 +198,13 @@ class BoundedSemaphore(Semaphore):
self
.
_initial_value
=
self
.
counter
self
.
_initial_value
=
self
.
counter
def
release
(
self
):
def
release
(
self
):
"""
Like :meth:`Semaphore.release`, but raises :class:`ValueError`
if the semaphore is being over-released.
"""
if
self
.
counter
>=
self
.
_initial_value
:
if
self
.
counter
>=
self
.
_initial_value
:
raise
self
.
_OVER_RELEASE_ERROR
(
"Semaphore released too many times"
)
raise
self
.
_OVER_RELEASE_ERROR
(
"Semaphore released too many times"
)
Semaphore
.
release
(
self
)
return
Semaphore
.
release
(
self
)
...
...
src/gevent/lock.py
View file @
3c624d73
# Copyright (c) 2009-2012 Denis Bilenko. See LICENSE for details.
# Copyright (c) 2009-2012 Denis Bilenko. See LICENSE for details.
"""Locking primitives"""
"""
Locking primitives.
These include semaphores with arbitrary bounds (:class:`Semaphore` and
its safer subclass :class:`BoundedSemaphore`) and a semaphore with
infinite bounds (:class:`DummySemaphore`), along with a reentrant lock
(:class:`RLock`) with the same API as :class:`threading.RLock`.
"""
from
__future__
import
absolute_import
from
__future__
import
absolute_import
from
gevent.hub
import
getcurrent
from
gevent.hub
import
getcurrent
...
@@ -9,8 +16,8 @@ from gevent._semaphore import Semaphore, BoundedSemaphore # pylint:disable=no-na
...
@@ -9,8 +16,8 @@ from gevent._semaphore import Semaphore, BoundedSemaphore # pylint:disable=no-na
__all__
=
[
__all__
=
[
'Semaphore'
,
'Semaphore'
,
'DummySemaphore'
,
'BoundedSemaphore'
,
'BoundedSemaphore'
,
'DummySemaphore'
,
'RLock'
,
'RLock'
,
]
]
...
@@ -129,7 +136,8 @@ class DummySemaphore(object):
...
@@ -129,7 +136,8 @@ class DummySemaphore(object):
"""
"""
DummySemaphore(value=None) -> DummySemaphore
DummySemaphore(value=None) -> DummySemaphore
A Semaphore initialized with "infinite" initial value. None of its
An object with the same API as :class:`Semaphore`,
initialized with "infinite" initial value. None of its
methods ever block.
methods ever block.
This can be used to parameterize on whether or not to actually
This can be used to parameterize on whether or not to actually
...
@@ -166,6 +174,10 @@ class DummySemaphore(object):
...
@@ -166,6 +174,10 @@ class DummySemaphore(object):
"""A DummySemaphore is never locked so this always returns False."""
"""A DummySemaphore is never locked so this always returns False."""
return
False
return
False
def
ready
(
self
):
"""A DummySemaphore is never locked so this always returns True."""
return
True
def
release
(
self
):
def
release
(
self
):
"""Releasing a dummy semaphore does nothing."""
"""Releasing a dummy semaphore does nothing."""
...
@@ -200,8 +212,24 @@ class DummySemaphore(object):
...
@@ -200,8 +212,24 @@ class DummySemaphore(object):
class
RLock
(
object
):
class
RLock
(
object
):
"""
"""
A mutex that can be acquired more than once by the same greenlet.
A mutex that can be acquired more than once by the same greenlet.
A mutex can only be locked by one greenlet at a time. A single greenlet
can `acquire` the mutex as many times as desired, though. Each call to
`acquire` must be paired with a matching call to `release`.
It is an error for a greenlet that has not acquired the mutex
to release it.
Instances are context managers.
"""
"""
__slots__
=
(
'_block'
,
'_owner'
,
'_count'
,
'__weakref__'
,
)
def
__init__
(
self
):
def
__init__
(
self
):
self
.
_block
=
Semaphore
(
1
)
self
.
_block
=
Semaphore
(
1
)
self
.
_owner
=
None
self
.
_owner
=
None
...
@@ -215,12 +243,21 @@ class RLock(object):
...
@@ -215,12 +243,21 @@ class RLock(object):
self
.
_count
,
self
.
_count
,
self
.
_owner
)
self
.
_owner
)
def
acquire
(
self
,
blocking
=
1
):
def
acquire
(
self
,
blocking
=
True
,
timeout
=
None
):
"""
Acquire the mutex, blocking if *blocking* is true, for up to
*timeout* seconds.
.. versionchanged:: 1.5a4
Added the *timeout* parameter.
:return: A boolean indicating whether the mutex was acquired.
"""
me
=
getcurrent
()
me
=
getcurrent
()
if
self
.
_owner
is
me
:
if
self
.
_owner
is
me
:
self
.
_count
=
self
.
_count
+
1
self
.
_count
=
self
.
_count
+
1
return
1
return
1
rc
=
self
.
_block
.
acquire
(
blocking
)
rc
=
self
.
_block
.
acquire
(
blocking
,
timeout
)
if
rc
:
if
rc
:
self
.
_owner
=
me
self
.
_owner
=
me
self
.
_count
=
1
self
.
_count
=
1
...
@@ -230,6 +267,12 @@ class RLock(object):
...
@@ -230,6 +267,12 @@ class RLock(object):
return
self
.
acquire
()
return
self
.
acquire
()
def
release
(
self
):
def
release
(
self
):
"""
Release the mutex.
Only the greenlet that originally acquired the mutex can
release it.
"""
if
self
.
_owner
is
not
getcurrent
():
if
self
.
_owner
is
not
getcurrent
():
raise
RuntimeError
(
"cannot release un-acquired lock"
)
raise
RuntimeError
(
"cannot release un-acquired lock"
)
self
.
_count
=
count
=
self
.
_count
-
1
self
.
_count
=
count
=
self
.
_count
-
1
...
...
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