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
also safer (but not configurations that involve such things as the
``SocketHandler``).
- 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)
===================
......@@ -178,7 +184,7 @@ Unreleased
- ``gevent.pool.Group.imap`` and ``imap_unordered`` now accept
multiple iterables like ``itertools.imap``. :issue:`565` reported by
Thomas Steinacher.
- Potentially breaking change: ``gevent.baseserver.BaseServer`` and
- *Compatibility note*: ``gevent.baseserver.BaseServer`` and
its subclass ``gevent.server.StreamServer`` now deterministically
close the client socket when the request handler returns.
Previously, the socket was left at the mercies of the garbage
......@@ -188,6 +194,13 @@ Unreleased
future and under CPython 3.x a ResourceWarning could be generated.
This was undocumented behaviour, and the client socket could be kept
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
=============
......
......@@ -147,13 +147,47 @@ def patch_time():
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.
- If *threading* is true (the default), also patch ``threading``.
- 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
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')
if threading:
......@@ -162,6 +196,10 @@ def patch_thread(threading=True, _threading_local=True, Event=False, logging=Tru
if Event:
from gevent.event import Event
patch_item(threading, 'Event', Event)
if existing_locks:
_patch_existing_locks(threading)
if logging and 'logging' in sys.modules:
logging = __import__('logging')
patch_item(logging, '_lock', threading.RLock())
......@@ -179,6 +217,7 @@ def patch_thread(threading=True, _threading_local=True, Event=False, logging=Tru
patch_item(_threading_local, 'local', local)
if sys.version_info[:2] >= (3, 4):
# Issue 18808 changes the nature of Thread.join() to use
# locks. This means that a greenlet spawned in the main thread
# (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