Commit a4a55361 authored by Jason Madden's avatar Jason Madden

Use Python objects instead of C long for Semaphore._multithreaded

Because thread.get_ident() returns types of different signedness on Py2/Py3,
and some, possibly 32-bit ILP32, Py3 systems return values that won't fit in a long.

Cython conditional compilation doesnt work because we can only publish one source distribution.

Fixes #1733
parent 0bf8cbcb
Make gevent's ``Semaphore`` objects properly handle native thread
identifiers larger than can be stored in a C ``long`` on Python 3,
instead of raising an ``OverflowError``.
Reported by TheYOSH.
......@@ -12,14 +12,25 @@ cdef _native_sleep
cdef monotonic
cdef spawn_raw
cdef _UNSET
cdef _MULTI
cdef class _LockReleaseLink(object):
cdef object lock
cdef class Semaphore(AbstractLinkable):
cdef public int counter
# On Python 3, thread.get_ident() returns a ``unsigned long``; on
# Python 2, it's a plain ``long``. We can conditionally change
# the type here (depending on which version is cythonizing the
# .py files), but: our algorithm for testing whether it has been
# set or not was initially written with ``long`` in mind and used
# -1 as a sentinel. That doesn't work on Python 3. Thus, we can't
# use the native C type and must keep the wrapped Python object, which
# we can test for None.
cdef object _multithreaded
cdef long _multithreaded
cpdef bint locked(self)
cpdef int release(self) except -1000
......
......@@ -42,6 +42,9 @@ class _LockReleaseLink(object):
def __call__(self, _):
self.lock.release()
_UNSET = object()
_MULTI = object()
class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
"""
Semaphore(value=1) -> Semaphore
......@@ -78,9 +81,11 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
__slots__ = (
'counter',
# Integer. Set to 0 initially. Set to the ident of the first
# thread that acquires us. If we later see a different thread
# ident, set to -1.
# long integer, signed (Py2) or unsigned (Py3); see comments
# in the .pxd file for why we store as Python object. Set to ``_UNSET``
# initially. Set to the ident of the first thread that
# acquires us. If we later see a different thread ident, set
# to ``_MULTI``.
'_multithreaded',
)
......@@ -90,7 +95,7 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
raise ValueError("semaphore initial value must be >= 0")
super(Semaphore, self).__init__(hub)
self._notify_all = False
self._multithreaded = 0
self._multithreaded = _UNSET
def __str__(self):
return '<%s at 0x%x counter=%s _links[%s]>' % (
......@@ -196,10 +201,10 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
"""
# pylint:disable=too-many-return-statements,too-many-branches
# Sadly, the body of this method is rather complicated.
if self._multithreaded == 0:
if self._multithreaded is _UNSET:
self._multithreaded = self._get_thread_ident()
elif self._multithreaded != self._get_thread_ident():
self._multithreaded = -1
self._multithreaded = _MULTI
# We conceptually now belong to the hub of the thread that
# called this, whether or not we have to block. Note that we
......@@ -225,7 +230,7 @@ class Semaphore(AbstractLinkable): # pylint:disable=undefined-variable
if not blocking:
return False
if self._multithreaded != -1 and self.hub is None: # pylint:disable=access-member-before-definition
if self._multithreaded is not _MULTI and self.hub is None: # pylint:disable=access-member-before-definition
self.hub = get_hub() # pylint:disable=attribute-defined-outside-init
if self.hub is None and not invalid_thread_use:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment