diff --git a/eventlet/green/thread.py b/eventlet/green/thread.py index 7bd24d4..5c7446f 100644 --- a/eventlet/green/thread.py +++ b/eventlet/green/thread.py @@ -52,10 +52,20 @@ def start_new_thread(function, args=(), kwargs=None): # With monkey patching, eventlet uses green threads without python # thread state, so the lock is not automatically released. # - # Disable the thread state lock to avoid dead locks. + # Wrap _bootstrap_inner() to release explicitly the thread state lock + # when the thread completes. thread = function.__self__ - thread._set_tstate_lock = lambda: None - thread._wait_for_tstate_lock = lambda *args, **kw: None + bootstrap_inner = thread._bootstrap_inner + + def wrap_bootstrap_inner(): + try: + bootstrap_inner() + finally: + # The lock can be cleared (ex: by a fork()) + if thread._tstate_lock is not None: + thread._tstate_lock.release() + + thread._bootstrap_inner = wrap_bootstrap_inner kwargs = kwargs or {} g = greenthread.spawn_n(__thread_body, function, args, kwargs) diff --git a/tests/isolated/patcher_threading_join.py b/tests/isolated/patcher_threading_join.py new file mode 100644 index 0000000..4361f52 --- /dev/null +++ b/tests/isolated/patcher_threading_join.py @@ -0,0 +1,23 @@ +# Issue #223: test threading.Thread.join with monkey-patching +import eventlet + +# no standard tests in this file, ignore +__test__ = False + + +if __name__ == '__main__': + eventlet.monkey_patch() + + import threading + import time + + sleeper = threading.Thread(target=time.sleep, args=(1,)) + start = time.time() + sleeper.start() + sleeper.join() + dt = time.time() - start + + if dt < 1.0: + raise Exception("test failed: dt=%s" % dt) + + print('pass') diff --git a/tests/patcher_test.py b/tests/patcher_test.py index 2a42e3c..2e458c5 100644 --- a/tests/patcher_test.py +++ b/tests/patcher_test.py @@ -502,3 +502,7 @@ def test_importlib_lock(): def test_threading_condition(): tests.run_isolated('patcher_threading_condition.py') + + +def test_threading_join(): + tests.run_isolated('patcher_threading_join.py')