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
|
"""Implements the standard thread module, using greenthreads."""
import _thread as __thread
from eventlet.support import greenlets as greenlet
from eventlet import greenthread
from eventlet.timeout import with_timeout
from eventlet.lock import Lock
import sys
__patched__ = ['Lock', 'LockType', '_ThreadHandle', '_count',
'_get_main_thread_ident', '_local', '_make_thread_handle',
'allocate', 'allocate_lock', 'exit', 'get_ident',
'interrupt_main', 'stack_size', 'start_joinable_thread',
'start_new', 'start_new_thread']
error = __thread.error
LockType = Lock
__threadcount = 0
if hasattr(__thread, "_is_main_interpreter"):
_is_main_interpreter = __thread._is_main_interpreter
def _set_sentinel():
# TODO this is a dummy code, reimplementing this may be needed:
# https://hg.python.org/cpython/file/b5e9bc4352e1/Modules/_threadmodule.c#l1203
return allocate_lock()
TIMEOUT_MAX = __thread.TIMEOUT_MAX
def _count():
return __threadcount
def get_ident(gr=None):
if gr is None:
return id(greenlet.getcurrent())
else:
return id(gr)
def __thread_body(func, args, kwargs):
global __threadcount
__threadcount += 1
try:
func(*args, **kwargs)
finally:
__threadcount -= 1
class _ThreadHandle:
def __init__(self, greenthread=None):
self._greenthread = greenthread
self._done = False
def _set_done(self):
self._done = True
def is_done(self):
if self._greenthread is not None:
return self._greenthread.dead
return self._done
@property
def ident(self):
return get_ident(self._greenthread)
def join(self, timeout=None):
if not hasattr(self._greenthread, "wait"):
return
if timeout is not None:
return with_timeout(timeout, self._greenthread.wait)
return self._greenthread.wait()
def _make_thread_handle(ident):
greenthread = greenlet.getcurrent()
assert ident == get_ident(greenthread)
return _ThreadHandle(greenthread=greenthread)
def __spawn_green(function, args=(), kwargs=None, joinable=False):
if ((3, 4) <= sys.version_info < (3, 13)
and getattr(function, '__module__', '') == 'threading'
and hasattr(function, '__self__')):
# In Python 3.4-3.12, threading.Thread uses an internal lock
# automatically released when the python thread state is deleted.
# With monkey patching, eventlet uses green threads without python
# thread state, so the lock is not automatically released.
#
# Wrap _bootstrap_inner() to release explicitly the thread state lock
# when the thread completes.
thread = function.__self__
bootstrap_inner = thread._bootstrap_inner
def wrap_bootstrap_inner():
try:
bootstrap_inner()
finally:
# The lock can be cleared (ex: by a fork())
if getattr(thread, "_tstate_lock", None) is not None:
thread._tstate_lock.release()
thread._bootstrap_inner = wrap_bootstrap_inner
kwargs = kwargs or {}
spawn_func = greenthread.spawn if joinable else greenthread.spawn_n
return spawn_func(__thread_body, function, args, kwargs)
def start_joinable_thread(function, handle=None, daemon=True):
g = __spawn_green(function, joinable=True)
if handle is None:
handle = _ThreadHandle(greenthread=g)
else:
handle._greenthread = g
return handle
def start_new_thread(function, args=(), kwargs=None):
g = __spawn_green(function, args=args, kwargs=kwargs)
return get_ident(g)
start_new = start_new_thread
def _get_main_thread_ident():
greenthread = greenlet.getcurrent()
while greenthread.parent is not None:
greenthread = greenthread.parent
return get_ident(greenthread)
def allocate_lock(*a):
return LockType(1)
allocate = allocate_lock
def exit():
raise greenlet.GreenletExit
exit_thread = __thread.exit_thread
def interrupt_main():
curr = greenlet.getcurrent()
if curr.parent and not curr.parent.dead:
curr.parent.throw(KeyboardInterrupt())
else:
raise KeyboardInterrupt()
if hasattr(__thread, 'stack_size'):
__original_stack_size__ = __thread.stack_size
def stack_size(size=None):
if size is None:
return __original_stack_size__()
if size > __original_stack_size__():
return __original_stack_size__(size)
else:
pass
# not going to decrease stack_size, because otherwise other greenlets in
# this thread will suffer
from eventlet.corolocal import local as _local
if hasattr(__thread, 'daemon_threads_allowed'):
daemon_threads_allowed = __thread.daemon_threads_allowed
if hasattr(__thread, '_shutdown'):
_shutdown = __thread._shutdown
|