diff -Naur eventlet-0.9.16.orig/eventlet/green/threading.py eventlet-0.9.16/eventlet/green/threading.py --- eventlet-0.9.16.orig/eventlet/green/threading.py 2012-03-27 11:39:17.557782270 +0000 +++ eventlet-0.9.16/eventlet/green/threading.py 2012-03-27 11:39:42.604113535 +0000 @@ -1,9 +1,16 @@ +"""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 __patched__ = ['_start_new_thread', '_allocate_lock', '_get_ident', '_sleep', - 'local', 'stack_size', 'Lock'] + 'local', 'stack_size', 'Lock', 'currentThread', + 'current_thread', '_after_fork', '_shutdown'] + +__orig_threading = patcher.original('threading') +__threadlocal = __orig_threading.local() + patcher.inject('threading', globals(), @@ -11,3 +18,103 @@ ('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() + + @property + def name(self): + return self._name + + @name.setter + def name(self, name): + self._name = str(name) + + def getName(self): + return self.name + get_name = getName + + def setName(self, name): + self.name = name + set_name = setName + + @property + def ident(self): + return id(self._g) + + def isAlive(self): + return True + is_alive = isAlive + + @property + def daemon(self): + return 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.current_thread()) + else: + t = active[id(g)] = _GreenThread(g) + + return t + +currentThread = current_thread diff -Naur eventlet-0.9.16.orig/eventlet/patcher.py eventlet-0.9.16/eventlet/patcher.py --- eventlet-0.9.16.orig/eventlet/patcher.py 2012-03-27 11:39:17.558782283 +0000 +++ eventlet-0.9.16/eventlet/patcher.py 2012-03-27 11:39:35.148014914 +0000 @@ -223,7 +223,6 @@ on.setdefault(modname, default_on) modules_to_patch = [] - patched_thread = False if on['os'] and not already_patched.get('os'): modules_to_patch += _green_os_modules() already_patched['os'] = True @@ -234,7 +233,6 @@ modules_to_patch += _green_socket_modules() already_patched['socket'] = True if on['thread'] and not already_patched.get('thread'): - patched_thread = True modules_to_patch += _green_thread_modules() already_patched['thread'] = True if on['time'] and not already_patched.get('time'): @@ -266,27 +264,9 @@ patched_attr = getattr(mod, attr_name, None) if patched_attr is not None: setattr(orig_mod, attr_name, patched_attr) - - # hacks ahead; this is necessary to prevent a KeyError on program exit - if patched_thread: - _patch_main_thread(sys.modules['threading']) finally: imp.release_lock() -def _patch_main_thread(mod): - """This is some gnarly patching specific to the threading module; - threading will always be initialized prior to monkeypatching, and - its _active dict will have the wrong key (it uses the real thread - id but once it's patched it will use the greenlet ids); so what we - do is rekey the _active dict so that the main thread's entry uses - the greenthread key. Other threads' keys are ignored.""" - thread = original('thread') - curthread = mod._active.pop(thread.get_ident(), None) - if curthread: - import eventlet.green.thread - mod._active[eventlet.green.thread.get_ident()] = curthread - - def is_monkey_patched(module): """Returns True if the given module is monkeypatched currently, False if not. *module* can be either the module itself or its name. diff -Naur eventlet-0.9.16.orig/tests/patcher_test.py eventlet-0.9.16/tests/patcher_test.py --- eventlet-0.9.16.orig/tests/patcher_test.py 2012-03-27 11:39:17.560782309 +0000 +++ eventlet-0.9.16/tests/patcher_test.py 2012-03-27 11:39:42.604113535 +0000 @@ -293,5 +293,183 @@ self.assertEqual(output, "done\n", output) +class Threading(ProcessBase): + def test_orig_thread(self): + new_mod = """import eventlet +eventlet.monkey_patch() +from eventlet import patcher +import threading +_threading = patcher.original('threading') +def test(): + print repr(threading.current_thread()) +t = _threading.Thread(target=test) +t.start() +t.join() +print len(threading._active) +print len(_threading._active) +""" + self.write_to_tempfile("newmod", new_mod) + output, lines = self.launch_subprocess('newmod') + self.assertEqual(len(lines), 4, "\n".join(lines)) + self.assert_(lines[0].startswith('