1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
|
"""
Implementation of the standard :mod:`threading` using greenlets.
.. note::
This module is a helper for :mod:`gevent.monkey` and is not
intended to be used directly. For spawning greenlets in your
applications, prefer higher level constructs like
:class:`gevent.Greenlet` class or :func:`gevent.spawn`. Attributes
in this module like ``__threading__`` are implementation artifacts subject
to change at any time.
.. versionchanged:: 1.2.3
Defer adjusting the stdlib's list of active threads until we are
monkey patched. Previously this was done at import time. We are
documented to only be used as a helper for monkey patching, so this should
functionally be the same, but some applications ignore the documentation and
directly import this module anyway.
A positive consequence is that ``import gevent.threading,
threading; threading.current_thread()`` will no longer return a DummyThread
before monkey-patching.
"""
from __future__ import absolute_import
__implements__ = [
'local',
'_start_new_thread',
'_allocate_lock',
'Lock',
'_get_ident',
'_sleep',
'_DummyThread',
]
import threading as __threading__
_DummyThread_ = __threading__._DummyThread
from gevent.local import local
from gevent.thread import start_new_thread as _start_new_thread, allocate_lock as _allocate_lock, get_ident as _get_ident
from gevent.hub import sleep as _sleep, getcurrent
# Exports, prevent unused import warnings
local = local
start_new_thread = _start_new_thread
allocate_lock = _allocate_lock
_get_ident = _get_ident
_sleep = _sleep
getcurrent = getcurrent
Lock = _allocate_lock
def _cleanup(g):
__threading__._active.pop(_get_ident(g), None)
def _make_cleanup_id(gid):
def _(_r):
__threading__._active.pop(gid, None)
return _
_weakref = None
class _DummyThread(_DummyThread_):
# We avoid calling the superclass constructor. This makes us about
# twice as fast (1.16 vs 0.68usec on PyPy, 29.3 vs 17.7usec on
# CPython 2.7), and has the important effect of avoiding
# allocation and then immediate deletion of _Thread__block, a
# lock. This is especially important on PyPy where locks go
# through the cpyext API and Cython, which is known to be slow and
# potentially buggy (e.g.,
# https://bitbucket.org/pypy/pypy/issues/2149/memory-leak-for-python-subclass-of-cpyext#comment-22347393)
# These objects are constructed quite frequently in some cases, so
# the optimization matters: for example, in gunicorn, which uses
# pywsgi.WSGIServer, every request is handled in a new greenlet,
# and every request uses a logging.Logger to write the access log,
# and every call to a log method captures the current thread (by
# default).
#
# (Obviously we have to duplicate the effects of the constructor,
# at least for external state purposes, which is potentially
# slightly fragile.)
# For the same reason, instances of this class will cleanup their own entry
# in ``threading._active``
# This class also solves a problem forking process with subprocess: after forking,
# Thread.__stop is called, which throws an exception when __block doesn't
# exist.
# Capture the static things as class vars to save on memory/
# construction time.
# In Py2, they're all private; in Py3, they become protected
_Thread__stopped = _is_stopped = _stopped = False
_Thread__initialized = _initialized = True
_Thread__daemonic = _daemonic = True
_Thread__args = _args = ()
_Thread__kwargs = _kwargs = None
_Thread__target = _target = None
_Thread_ident = _ident = None
_Thread__started = _started = __threading__.Event()
_Thread__started.set()
_tstate_lock = None
def __init__(self): # pylint:disable=super-init-not-called
#_DummyThread_.__init__(self)
# It'd be nice to use a pattern like "greenlet-%d", but maybe somebody out
# there is checking thread names...
self._name = self._Thread__name = __threading__._newname("DummyThread-%d")
# All dummy threads in the same native thread share the same ident
# (that of the native thread)
self._set_ident()
g = getcurrent()
gid = _get_ident(g)
__threading__._active[gid] = self
rawlink = getattr(g, 'rawlink', None)
if rawlink is not None:
# raw greenlet.greenlet greenlets don't
# have rawlink...
rawlink(_cleanup)
else:
# ... so for them we use weakrefs.
# See https://github.com/gevent/gevent/issues/918
global _weakref
if _weakref is None:
_weakref = __import__('weakref')
ref = _weakref.ref(g, _make_cleanup_id(gid))
self.__raw_ref = ref
def _Thread__stop(self):
pass
_stop = _Thread__stop # py3
def _wait_for_tstate_lock(self, *args, **kwargs):
# pylint:disable=arguments-differ
pass
if hasattr(__threading__, 'main_thread'): # py 3.4+
def main_native_thread():
return __threading__.main_thread() # pylint:disable=no-member
else:
def main_native_thread():
main_threads = [v for v in __threading__._active.values()
if isinstance(v, __threading__._MainThread)]
assert len(main_threads) == 1, "Too many main threads"
return main_threads[0]
import sys
if sys.version_info[:2] >= (3, 4):
# XXX: Issue 18808 breaks us on Python 3.4.
# Thread objects now expect a callback from the interpreter itself
# (threadmodule.c:release_sentinel). Because this never happens
# when a greenlet exits, join() and friends will block forever.
# The solution below involves capturing the greenlet when it is
# started and deferring the known broken methods to it.
class Thread(__threading__.Thread):
_greenlet = None
def is_alive(self):
return bool(self._greenlet)
isAlive = is_alive
def _set_tstate_lock(self):
self._greenlet = getcurrent()
def run(self):
try:
super(Thread, self).run()
finally:
# avoid ref cycles, but keep in __dict__ so we can
# distinguish the started/never-started case
self._greenlet = None
self._stop() # mark as finished
def join(self, timeout=None):
if '_greenlet' not in self.__dict__:
raise RuntimeError("Cannot join an inactive thread")
if self._greenlet is None:
return
self._greenlet.join(timeout=timeout)
def _wait_for_tstate_lock(self, *args, **kwargs):
# pylint:disable=arguments-differ
raise NotImplementedError()
__implements__.append('Thread')
class Timer(Thread, __threading__.Timer): # pylint:disable=abstract-method,inherit-non-class
pass
__implements__.append('Timer')
# The main thread is patched up with more care
# in _gevent_will_monkey_patch
if sys.version_info[:2] >= (3, 3):
__implements__.remove('_get_ident')
__implements__.append('get_ident')
get_ident = _get_ident
__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')
def _gevent_will_monkey_patch(native_module, items, warn): # pylint:disable=unused-argument
# Make sure the MainThread can be found by our current greenlet ID,
# otherwise we get a new DummyThread, which cannot be joined.
# Fixes tests in test_threading_2 under PyPy.
main_thread = main_native_thread()
if __threading__.current_thread() != main_thread:
warn("Monkey-patching outside the main native thread. Some APIs "
"will not be available. Expect a KeyError to be printed at shutdown.")
return
if _get_ident() not in __threading__._active:
main_id = main_thread.ident
del __threading__._active[main_id]
main_thread._ident = main_thread._Thread__ident = _get_ident()
__threading__._active[_get_ident()] = main_thread
|