Commit fc3b2514 authored by Jason Madden's avatar Jason Madden

Patch existing locks held at the time of monkey-patching. Fixes #615.

parent 914061ec
...@@ -73,6 +73,12 @@ Unreleased ...@@ -73,6 +73,12 @@ Unreleased
also safer (but not configurations that involve such things as the also safer (but not configurations that involve such things as the
``SocketHandler``). ``SocketHandler``).
- Fix monkey-patching of ``threading.RLock`` under Python 3. - Fix monkey-patching of ``threading.RLock`` under Python 3.
- Under Python 3, monkey-patching at the top-level of a module that
was imported by another module could result in a ``RuntimeError``
from ``importlib``. Reported in :issue:`615` by Daniel Mizyrycki.
(The same thing could happen under Python 2 if a ``threading.RLock``
was held around the monkey-patching call; this is less likely but
not impossible with import hooks.)
1.1a2 (Jul 8, 2015) 1.1a2 (Jul 8, 2015)
=================== ===================
...@@ -178,7 +184,7 @@ Unreleased ...@@ -178,7 +184,7 @@ Unreleased
- ``gevent.pool.Group.imap`` and ``imap_unordered`` now accept - ``gevent.pool.Group.imap`` and ``imap_unordered`` now accept
multiple iterables like ``itertools.imap``. :issue:`565` reported by multiple iterables like ``itertools.imap``. :issue:`565` reported by
Thomas Steinacher. Thomas Steinacher.
- Potentially breaking change: ``gevent.baseserver.BaseServer`` and - *Compatibility note*: ``gevent.baseserver.BaseServer`` and
its subclass ``gevent.server.StreamServer`` now deterministically its subclass ``gevent.server.StreamServer`` now deterministically
close the client socket when the request handler returns. close the client socket when the request handler returns.
Previously, the socket was left at the mercies of the garbage Previously, the socket was left at the mercies of the garbage
...@@ -188,6 +194,13 @@ Unreleased ...@@ -188,6 +194,13 @@ Unreleased
future and under CPython 3.x a ResourceWarning could be generated. future and under CPython 3.x a ResourceWarning could be generated.
This was undocumented behaviour, and the client socket could be kept This was undocumented behaviour, and the client socket could be kept
open after the request handler returned either accidentally or intentionally. open after the request handler returned either accidentally or intentionally.
- *Compatibility note*: ``pywsgi`` now ensures that headers can be
encoded in latin-1 (ISO-8859-1). This improves adherence to the HTTP
standard (and is necessary under Python 3). Under certain
conditions, previous versions could have allowed non-ISO-8859-1
headers to be sent, but their interpretation by a conforming
recipient is unknown; now, a UnicodeError will be raised. See :issue:`614`.
Release 1.0.2 Release 1.0.2
============= =============
......
...@@ -147,13 +147,47 @@ def patch_time(): ...@@ -147,13 +147,47 @@ def patch_time():
patch_item(time, 'sleep', sleep) patch_item(time, 'sleep', sleep)
def patch_thread(threading=True, _threading_local=True, Event=False, logging=True): def _patch_existing_locks(threading):
if len(list(threading.enumerate())) != 1:
return
tid = threading.current_thread().ident
rlock_type = type(threading.RLock())
try:
import importlib._bootstrap
except NameError:
class _ModuleLock(object):
pass
else:
_ModuleLock = importlib._bootstrap._ModuleLock
# It might be possible to walk up all the existing stack frames to find
# locked objects...at least if they use `with`. To be sure, we look at every object
# Since we're supposed to be done very early in the process, there shouldn't be
# too many.
gc = __import__('gc')
for o in gc.get_objects():
if isinstance(o, rlock_type):
if o._RLock__owner is not None:
# By definition there's only one thread running,
# so this was the old (native) thread id. Make
# it our current greenlet id so that when it wants to unlock
# and compare self.__owner with _get_ident(), they match
o._RLock__owner = tid
elif isinstance(o, _ModuleLock):
if o.owner is not None:
o.owner = tid
def patch_thread(threading=True, _threading_local=True, Event=False, logging=True,
existing_locks=True):
"""Replace the standard :mod:`thread` module to make it greenlet-based. """Replace the standard :mod:`thread` module to make it greenlet-based.
- If *threading* is true (the default), also patch ``threading``. - If *threading* is true (the default), also patch ``threading``.
- If *_threading_local* is true (the default), also patch ``_threading_local.local``. - If *_threading_local* is true (the default), also patch ``_threading_local.local``.
- If *logging* is True (the default), also patch locks taken if the logging module has - If *logging* is True (the default), also patch locks taken if the logging module has
been configured. been configured.
- If *existing_locks* is True (the default), and the process is still single threaded,
make sure than any :class:`threading.RLock` (and, under Python 3, :class:`importlib._bootstrap._ModuleLock`)
instances that are currently locked can be properly unlocked.
""" """
patch_module('thread') patch_module('thread')
if threading: if threading:
...@@ -162,6 +196,10 @@ def patch_thread(threading=True, _threading_local=True, Event=False, logging=Tru ...@@ -162,6 +196,10 @@ def patch_thread(threading=True, _threading_local=True, Event=False, logging=Tru
if Event: if Event:
from gevent.event import Event from gevent.event import Event
patch_item(threading, 'Event', Event) patch_item(threading, 'Event', Event)
if existing_locks:
_patch_existing_locks(threading)
if logging and 'logging' in sys.modules: if logging and 'logging' in sys.modules:
logging = __import__('logging') logging = __import__('logging')
patch_item(logging, '_lock', threading.RLock()) patch_item(logging, '_lock', threading.RLock())
...@@ -179,6 +217,7 @@ def patch_thread(threading=True, _threading_local=True, Event=False, logging=Tru ...@@ -179,6 +217,7 @@ def patch_thread(threading=True, _threading_local=True, Event=False, logging=Tru
patch_item(_threading_local, 'local', local) patch_item(_threading_local, 'local', local)
if sys.version_info[:2] >= (3, 4): if sys.version_info[:2] >= (3, 4):
# Issue 18808 changes the nature of Thread.join() to use # Issue 18808 changes the nature of Thread.join() to use
# locks. This means that a greenlet spawned in the main thread # locks. This means that a greenlet spawned in the main thread
# (which is already running) cannot wait for the main thread---it # (which is already running) cannot wait for the main thread---it
......
import gevent.monkey
gevent.monkey.patch_all()
# https://github.com/gevent/gevent/issues/615
# Under Python 3, with its use of importlib,
# if the monkey patch is done when the importlib import lock is held
# (e.g., during recursive imports) we could fail to release the lock.
# This is surprisingly common.
import _import_import_patch
from gevent import monkey
import threading
# Make sure that we can patch gevent while holding
# a threading lock. Under Python2, where RLock is implemented
# in python code, this used to throw RuntimeErro("Cannot release un-acquired lock")
# See https://github.com/gevent/gevent/issues/615
with threading.RLock():
monkey.patch_all()
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