
This includes: * patching more tests to pass * removing few unit tests which I think are redundant * repeating SSL socket reads in a loop to read all data (I suspect this is related to the fact that writelines is used in the server code there and Python 3 writelines calls write/send repeatedly while on Python 2 it calls it once; on one hand there's no guarantee that single recv/read will return all data sent by the server, on the other hand it's quite suspicious that the number of required reads seems to be connected to the number of sends on the other side of the connection) * working through Python 2/Python 3 threading and thread differences; the lock code I used is the simplest way I could make the tests pass but will likely need to be modified in order to match the original This commit includes 6bcb1dc and closes GH #153
121 lines
3.1 KiB
Python
121 lines
3.1 KiB
Python
"""Implements the standard threading module, using greenthreads."""
|
|
from eventlet import patcher
|
|
from eventlet.green import thread
|
|
from eventlet.green import time
|
|
from eventlet.support import greenlets as greenlet, six
|
|
|
|
__patched__ = ['_start_new_thread', '_allocate_lock',
|
|
'_sleep', 'local', 'stack_size', 'Lock', 'currentThread',
|
|
'current_thread', '_after_fork', '_shutdown']
|
|
|
|
if six.PY2:
|
|
__patched__ += ['_get_ident']
|
|
else:
|
|
__patched__ += ['get_ident', '_set_sentinel']
|
|
|
|
__orig_threading = patcher.original('threading')
|
|
__threadlocal = __orig_threading.local()
|
|
|
|
|
|
patcher.inject(
|
|
'threading',
|
|
globals(),
|
|
('thread' if six.PY2 else '_thread', thread),
|
|
('time', time))
|
|
|
|
del patcher
|
|
|
|
|
|
_count = 1
|
|
|
|
|
|
class _GreenThread(object):
|
|
"""Wrapper for GreenThread objects to provide Thread-like attributes
|
|
and methods"""
|
|
|
|
def __init__(self, g):
|
|
global _count
|
|
self._g = g
|
|
self._name = 'GreenThread-%d' % _count
|
|
_count += 1
|
|
|
|
def __repr__(self):
|
|
return '<_GreenThread(%s, %r)>' % (self._name, self._g)
|
|
|
|
def join(self, timeout=None):
|
|
return self._g.wait()
|
|
|
|
def getName(self):
|
|
return self._name
|
|
get_name = getName
|
|
|
|
def setName(self, name):
|
|
self._name = str(name)
|
|
set_name = setName
|
|
|
|
name = property(getName, setName)
|
|
|
|
ident = property(lambda self: id(self._g))
|
|
|
|
def isAlive(self):
|
|
return True
|
|
is_alive = isAlive
|
|
|
|
daemon = property(lambda self: True)
|
|
|
|
def isDaemon(self):
|
|
return self.daemon
|
|
is_daemon = isDaemon
|
|
|
|
|
|
__threading = None
|
|
|
|
|
|
def _fixup_thread(t):
|
|
# Some third-party packages (lockfile) will try to patch the
|
|
# threading.Thread class with a get_name attribute if it doesn't
|
|
# exist. Since we might return Thread objects from the original
|
|
# threading package that won't get patched, let's make sure each
|
|
# individual object gets patched too our patched threading.Thread
|
|
# class has been patched. This is why monkey patching can be bad...
|
|
global __threading
|
|
if not __threading:
|
|
__threading = __import__('threading')
|
|
|
|
if (hasattr(__threading.Thread, 'get_name') and
|
|
not hasattr(t, 'get_name')):
|
|
t.get_name = t.getName
|
|
return t
|
|
|
|
|
|
def current_thread():
|
|
g = greenlet.getcurrent()
|
|
if not g:
|
|
# Not currently in a greenthread, fall back to standard function
|
|
return _fixup_thread(__orig_threading.current_thread())
|
|
|
|
try:
|
|
active = __threadlocal.active
|
|
except AttributeError:
|
|
active = __threadlocal.active = {}
|
|
|
|
try:
|
|
t = active[id(g)]
|
|
except KeyError:
|
|
# Add green thread to active if we can clean it up on exit
|
|
def cleanup(g):
|
|
del active[id(g)]
|
|
try:
|
|
g.link(cleanup)
|
|
except AttributeError:
|
|
# Not a GreenThread type, so there's no way to hook into
|
|
# the green thread exiting. Fall back to the standard
|
|
# function then.
|
|
t = _fixup_thread(__orig_threading.currentThread())
|
|
else:
|
|
t = active[id(g)] = _GreenThread(g)
|
|
|
|
return t
|
|
|
|
currentThread = current_thread
|