From 0d509ef7d2eea3ed4f8ade35d5d09918c811b56c Mon Sep 17 00:00:00 2001 From: Jakub Stasiak Date: Thu, 12 Nov 2015 02:05:25 +0100 Subject: [PATCH] Fix HTTPServer.serve_forever blocking whole process Original report: https://github.com/eventlet/eventlet/issues/249 Explanation is in the comments in the code. Originally reverted[1] because a commit preceding it[2] broke the build but it wasn't clear what commit was responsible. [1] 02b693a45db96bd2baf27c1d5128a8bcedb8fbfc [2] 4656eadfa5ae1237036a63ad4004dbee4572debf --- eventlet/green/selectors.py | 20 +++++++++++++ eventlet/patcher.py | 8 +++++- .../patcher_socketserver_selectors.py | 28 +++++++++++++++++++ tests/patcher_test.py | 4 +++ 4 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 tests/isolated/patcher_socketserver_selectors.py diff --git a/eventlet/green/selectors.py b/eventlet/green/selectors.py index 26427ec..9d482fe 100644 --- a/eventlet/green/selectors.py +++ b/eventlet/green/selectors.py @@ -3,9 +3,29 @@ import sys from eventlet import patcher from eventlet.green import select +__patched__ = [ + 'SelectSelector', + 'PollSelector', + 'EpollSelector', + 'DevpollSelector', + 'KqueueSelector', +] + patcher.inject('selectors', globals(), ('select', select)) del patcher if sys.platform != 'win32': SelectSelector._select = staticmethod(select.select) + + +# We only have green select so the options are: +# * leave it be and have selectors that block +# * try to pretend the "bad" selectors don't exist +# * replace all with SelectSelector for the price of possibly different +# performance characteristic and missing fileno() method (if someone +# uses it it'll result in a crash, we may want to implement it in the future) +PollSelector = SelectSelector +EpollSelector = SelectSelector +DevpollSelector = SelectSelector +KqueueSelector = SelectSelector diff --git a/eventlet/patcher.py b/eventlet/patcher.py index eb09f9a..40207f9 100644 --- a/eventlet/patcher.py +++ b/eventlet/patcher.py @@ -331,7 +331,13 @@ def _green_os_modules(): def _green_select_modules(): from eventlet.green import select - return [('select', select)] + modules = [('select', select)] + + if sys.version_info >= (3, 4): + from eventlet.green import selectors + modules.append(('selectors', selectors)) + + return modules def _green_socket_modules(): diff --git a/tests/isolated/patcher_socketserver_selectors.py b/tests/isolated/patcher_socketserver_selectors.py new file mode 100644 index 0000000..97df6e2 --- /dev/null +++ b/tests/isolated/patcher_socketserver_selectors.py @@ -0,0 +1,28 @@ +if __name__ == '__main__': + import eventlet + eventlet.monkey_patch() + + from eventlet.support.six.moves.BaseHTTPServer import ( + HTTPServer, + BaseHTTPRequestHandler, + ) + import threading + + server = HTTPServer(('localhost', 0), BaseHTTPRequestHandler) + thread = threading.Thread(target=server.serve_forever) + + # Before fixing it the code would never go pass this line because: + # * socketserver.BaseServer that's used behind the scenes here uses + # selectors.PollSelector if it's available and we don't have green poll + # implementation so this just couldn't work + # * making socketserver use selectors.SelectSelector wasn't enough as + # until now we just failed to monkey patch selectors module + # + # Due to the issues above this thread.start() call effectively behaved + # like calling server.serve_forever() directly in the current thread + # + # Original report: https://github.com/eventlet/eventlet/issues/249 + thread.start() + + server.shutdown() + print('pass') diff --git a/tests/patcher_test.py b/tests/patcher_test.py index 2e458c5..deae8ef 100644 --- a/tests/patcher_test.py +++ b/tests/patcher_test.py @@ -506,3 +506,7 @@ def test_threading_condition(): def test_threading_join(): tests.run_isolated('patcher_threading_join.py') + + +def test_socketserver_selectors(): + tests.run_isolated('patcher_socketserver_selectors.py')