Commit 914061ec authored by Jason Madden's avatar Jason Madden

Cleanup logging locks that were created before monkey patching thread. Also,...

Cleanup logging locks that were created before monkey patching thread. Also, fix monkey patching of threading.RLock under Python 3.
parent 5cdebe11
...@@ -64,7 +64,15 @@ Unreleased ...@@ -64,7 +64,15 @@ Unreleased
raises a ``LoopExit`` error (using ``ThreadPool.spawn`` and then raises a ``LoopExit`` error (using ``ThreadPool.spawn`` and then
``get`` on the result still could; you must be careful to use the ``get`` on the result still could; you must be careful to use the
correct hub). Reported in :issue:`131` by 8mayday. correct hub). Reported in :issue:`131` by 8mayday.
- When the ``threading`` module is monkey-patched, the module-level
lock in the ``logging`` module is made greenlet-aware, as are the
instance locks of any configured handlers. This makes it safer to
import modules that use the standard pattern of creating a
module-level ``Logger`` instance before monkey-patching. Configuring
``logging`` with a basic configuration and then monkey-patching is
also safer (but not configurations that involve such things as the
``SocketHandler``).
- Fix monkey-patching of ``threading.RLock`` under Python 3.
1.1a2 (Jul 8, 2015) 1.1a2 (Jul 8, 2015)
=================== ===================
......
...@@ -147,10 +147,13 @@ def patch_time(): ...@@ -147,10 +147,13 @@ def patch_time():
patch_item(time, 'sleep', sleep) patch_item(time, 'sleep', sleep)
def patch_thread(threading=True, _threading_local=True, Event=False): def patch_thread(threading=True, _threading_local=True, Event=False, logging=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_local* is true (the default), also patch ``_threading_local.local``. - 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.
""" """
patch_module('thread') patch_module('thread')
if threading: if threading:
...@@ -159,6 +162,17 @@ def patch_thread(threading=True, _threading_local=True, Event=False): ...@@ -159,6 +162,17 @@ def patch_thread(threading=True, _threading_local=True, Event=False):
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 logging and 'logging' in sys.modules:
logging = __import__('logging')
patch_item(logging, '_lock', threading.RLock())
for wr in logging._handlerList:
# In py26, these are actual handlers, not weakrefs
handler = wr() if callable(wr) else wr
if handler is None:
continue
assert hasattr(handler, 'lock'), "Unknown/unsupported handler %r" % handler
handler.lock = threading.RLock()
if _threading_local: if _threading_local:
_threading_local = __import__('_threading_local') _threading_local = __import__('_threading_local')
from gevent.local import local from gevent.local import local
......
...@@ -117,3 +117,13 @@ if sys.version_info[:2] >= (3, 3): ...@@ -117,3 +117,13 @@ if sys.version_info[:2] >= (3, 3):
__implements__.append('get_ident') __implements__.append('get_ident')
get_ident = _get_ident get_ident = _get_ident
__implements__.remove('_sleep') __implements__.remove('_sleep')
# Python 3 changed the implementation of threading.RLock
# Previously it was a factory function around threading._RLock
# which in turn used _allocate_lock. Now, it wants to use
# threading._CRLock, which is imported from _thread.RLock and as such
# is implemented in C. So it bypasses our _allocate_lock function.
# Fortunately they left the Python fallback in place
assert hasattr(__threading__, '_CRLock'), "Unsupported Python version"
_CRLock = None
__implements__.append('_CRLock')
# If the logging module is imported *before* monkey patching,
# the existing handlers are correctly monkey patched to use gevent locks
import logging
logging.basicConfig()
import threading
import sys
PY2 = sys.version_info[0] == 2
def _inner_lock(lock):
# The inner attribute changed between 2 and 3
attr = getattr(lock, '_block' if not PY2 else '_RLock__block', None)
return attr
def checkLocks(kind, ignore_none=True):
handlers = logging._handlerList
assert len(handlers) > 0
for weakref in handlers:
# In py26, these are actual handlers, not weakrefs
handler = weakref() if callable(weakref) else weakref
attr = _inner_lock(handler.lock)
if attr is None and ignore_none:
continue
assert isinstance(attr, kind), (handler.lock, attr, kind)
attr = _inner_lock(logging._lock)
if attr is None and ignore_none:
return
assert isinstance(attr, kind)
checkLocks(type(threading._allocate_lock()))
import gevent.monkey
gevent.monkey.patch_all()
import gevent.lock
checkLocks(type(gevent.thread.allocate_lock()), ignore_none=False)
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