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
Hide 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 @@
1.5a5 (unreleased)
==================
-
Nothing changed yet
.
-
Make `gevent.lock.RLock.acquire` accept the *timeout* parameter
.
1.5a4 (2020-03-23)
...
...
src/gevent/_semaphore.py
View file @
3c624d73
...
...
@@ -17,10 +17,15 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
"""
Semaphore(value=1) -> Semaphore
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.
.. seealso:: :class:`BoundedSemaphore` for a safer version that prevents
some classes of bugs. If unsure, most users should opt for `BoundedSemaphore`.
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.
...
...
@@ -29,16 +34,12 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
This Semaphore's ``__exit__`` method does not call the trace function
on CPython, but does under PyPy.
.. seealso:: :class:`BoundedSemaphore` for a safer version that prevents
some classes of bugs.
.. versionchanged:: 1.4.0
The order in which waiters are awakened is not specified. It was not
specified previously, but usually went in FIFO order.
Document that 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.
.. versionchanged:: 1.5a3
Waiting greenlets are now awakened in the order in which they waited.
.. versionchanged:: 1.5a3
The low-level ``rawlink`` method (most users won't use this) now automatically
unlinks waiters before calling them.
...
...
@@ -56,19 +57,43 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
return
'<%s counter=%s _links[%s]>'
%
params
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
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
.
_check_and_notify
()
return
self
.
counter
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
def
_start_notify
(
self
):
...
...
@@ -84,18 +109,18 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
def
wait
(
self
,
timeout
=
None
):
"""
wait(timeout=None) -> int
Wait until it is possible to acquire this semaphore, or until the optional
*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.
:keyword float timeout: If given, specifies the maximum amount of seconds
this method will block.
: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
:
return
self
.
counter
...
...
@@ -109,15 +134,16 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
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
set to false).
:keyword bool blocking: If True (the default), this function will block
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.
: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
(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
...
...
@@ -172,9 +198,13 @@ class BoundedSemaphore(Semaphore):
self
.
_initial_value
=
self
.
counter
def
release
(
self
):
"""
Like :meth:`Semaphore.release`, but raises :class:`ValueError`
if the semaphore is being over-released.
"""
if
self
.
counter
>=
self
.
_initial_value
:
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.
"""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
gevent.hub
import
getcurrent
...
...
@@ -9,8 +16,8 @@ from gevent._semaphore import Semaphore, BoundedSemaphore # pylint:disable=no-na
__all__
=
[
'Semaphore'
,
'DummySemaphore'
,
'BoundedSemaphore'
,
'DummySemaphore'
,
'RLock'
,
]
...
...
@@ -129,7 +136,8 @@ class DummySemaphore(object):
"""
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.
This can be used to parameterize on whether or not to actually
...
...
@@ -166,6 +174,10 @@ class DummySemaphore(object):
"""A DummySemaphore is never locked so this always returns False."""
return
False
def
ready
(
self
):
"""A DummySemaphore is never locked so this always returns True."""
return
True
def
release
(
self
):
"""Releasing a dummy semaphore does nothing."""
...
...
@@ -200,8 +212,24 @@ class DummySemaphore(object):
class
RLock
(
object
):
"""
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
):
self
.
_block
=
Semaphore
(
1
)
self
.
_owner
=
None
...
...
@@ -215,12 +243,21 @@ class RLock(object):
self
.
_count
,
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
()
if
self
.
_owner
is
me
:
self
.
_count
=
self
.
_count
+
1
return
1
rc
=
self
.
_block
.
acquire
(
blocking
)
rc
=
self
.
_block
.
acquire
(
blocking
,
timeout
)
if
rc
:
self
.
_owner
=
me
self
.
_count
=
1
...
...
@@ -230,6 +267,12 @@ class RLock(object):
return
self
.
acquire
()
def
release
(
self
):
"""
Release the mutex.
Only the greenlet that originally acquired the mutex can
release it.
"""
if
self
.
_owner
is
not
getcurrent
():
raise
RuntimeError
(
"cannot release un-acquired lock"
)
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