[Python-checkins] cpython (2.7): Issue #7978: socketserver now restarts the select() call when EINTR is returned.

antoine.pitrou python-checkins at python.org
Mon Apr 9 01:12:45 CEST 2012


http://hg.python.org/cpython/rev/d941d1fcc6e6
changeset:   76167:d941d1fcc6e6
branch:      2.7
parent:      76163:96de17c95a7f
user:        Antoine Pitrou <solipsis at pitrou.net>
date:        Mon Apr 09 00:47:24 2012 +0200
summary:
  Issue #7978: socketserver now restarts the select() call when EINTR is returned.
This avoids crashing the server loop when a signal is received.
Patch by Jerzy Kozera.

files:
  Lib/SocketServer.py           |  15 ++++++++-
  Lib/test/test_socketserver.py |  34 +++++++++++++++++++++++
  Misc/ACKS                     |   1 +
  Misc/NEWS                     |   4 ++
  4 files changed, 52 insertions(+), 2 deletions(-)


diff --git a/Lib/SocketServer.py b/Lib/SocketServer.py
--- a/Lib/SocketServer.py
+++ b/Lib/SocketServer.py
@@ -133,6 +133,7 @@
 import select
 import sys
 import os
+import errno
 try:
     import threading
 except ImportError:
@@ -147,6 +148,15 @@
                     "ThreadingUnixStreamServer",
                     "ThreadingUnixDatagramServer"])
 
+def _eintr_retry(func, *args):
+    """restart a system call interrupted by EINTR"""
+    while True:
+        try:
+            return func(*args)
+        except OSError as e:
+            if e.errno != errno.EINTR:
+                raise
+
 class BaseServer:
 
     """Base class for server classes.
@@ -222,7 +232,8 @@
                 # connecting to the socket to wake this up instead of
                 # polling. Polling reduces our responsiveness to a
                 # shutdown request and wastes cpu at all other times.
-                r, w, e = select.select([self], [], [], poll_interval)
+                r, w, e = _eintr_retry(select.select, [self], [], [],
+                                       poll_interval)
                 if self in r:
                     self._handle_request_noblock()
         finally:
@@ -262,7 +273,7 @@
             timeout = self.timeout
         elif self.timeout is not None:
             timeout = min(timeout, self.timeout)
-        fd_sets = select.select([self], [], [], timeout)
+        fd_sets = _eintr_retry(select.select, [self], [], [], timeout)
         if not fd_sets[0]:
             self.handle_timeout()
             return
diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py
--- a/Lib/test/test_socketserver.py
+++ b/Lib/test/test_socketserver.py
@@ -8,6 +8,8 @@
 import select
 import signal
 import socket
+import select
+import errno
 import tempfile
 import unittest
 import SocketServer
@@ -225,6 +227,38 @@
                                 SocketServer.DatagramRequestHandler,
                                 self.dgram_examine)
 
+    @contextlib.contextmanager
+    def mocked_select_module(self):
+        """Mocks the select.select() call to raise EINTR for first call"""
+        old_select = select.select
+
+        class MockSelect:
+            def __init__(self):
+                self.called = 0
+
+            def __call__(self, *args):
+                self.called += 1
+                if self.called == 1:
+                    # raise the exception on first call
+                    raise OSError(errno.EINTR, os.strerror(errno.EINTR))
+                else:
+                    # Return real select value for consecutive calls
+                    return old_select(*args)
+
+        select.select = MockSelect()
+        try:
+            yield select.select
+        finally:
+            select.select = old_select
+
+    def test_InterruptServerSelectCall(self):
+        with self.mocked_select_module() as mock_select:
+            pid = self.run_server(SocketServer.TCPServer,
+                                  SocketServer.StreamRequestHandler,
+                                  self.stream_examine)
+            # Make sure select was called again:
+            self.assertGreater(mock_select.called, 1)
+
     # Alas, on Linux (at least) recvfrom() doesn't return a meaningful
     # client address so this cannot work:
 
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -461,6 +461,7 @@
 Damon Kohler
 Marko Kohtala
 Joseph Koshy
+Jerzy Kozera
 Maksim Kozyarchuk
 Stefan Krah
 Bob Kras
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -44,6 +44,10 @@
 Library
 -------
 
+- Issue #7978: SocketServer now restarts the select() call when EINTR is
+  returned.  This avoids crashing the server loop when a signal is received.
+  Patch by Jerzy Kozera.
+
 - Issue #14409: IDLE now properly executes commands in the Shell window
   when it cannot read the normal config files on startup and
   has to use the built-in default key bindings.

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list