Commit c9e8e3c4 authored by Antoine Pitrou's avatar Antoine Pitrou

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.
parents f998810d b0a9c66a
...@@ -133,6 +133,7 @@ import socket ...@@ -133,6 +133,7 @@ import socket
import select import select
import sys import sys
import os import os
import errno
try: try:
import threading import threading
except ImportError: except ImportError:
...@@ -147,6 +148,15 @@ if hasattr(socket, "AF_UNIX"): ...@@ -147,6 +148,15 @@ if hasattr(socket, "AF_UNIX"):
"ThreadingUnixStreamServer", "ThreadingUnixStreamServer",
"ThreadingUnixDatagramServer"]) "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: class BaseServer:
"""Base class for server classes. """Base class for server classes.
...@@ -223,7 +233,8 @@ class BaseServer: ...@@ -223,7 +233,8 @@ class BaseServer:
# connecting to the socket to wake this up instead of # connecting to the socket to wake this up instead of
# polling. Polling reduces our responsiveness to a # polling. Polling reduces our responsiveness to a
# shutdown request and wastes cpu at all other times. # 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: if self in r:
self._handle_request_noblock() self._handle_request_noblock()
...@@ -273,7 +284,7 @@ class BaseServer: ...@@ -273,7 +284,7 @@ class BaseServer:
timeout = self.timeout timeout = self.timeout
elif self.timeout is not None: elif self.timeout is not None:
timeout = min(timeout, self.timeout) timeout = min(timeout, self.timeout)
fd_sets = select.select([self], [], [], timeout) fd_sets = _eintr_retry(select.select, [self], [], [], timeout)
if not fd_sets[0]: if not fd_sets[0]:
self.handle_timeout() self.handle_timeout()
return return
......
...@@ -8,6 +8,8 @@ import os ...@@ -8,6 +8,8 @@ import os
import select import select
import signal import signal
import socket import socket
import select
import errno
import tempfile import tempfile
import unittest import unittest
import socketserver import socketserver
...@@ -226,6 +228,38 @@ class SocketServerTest(unittest.TestCase): ...@@ -226,6 +228,38 @@ class SocketServerTest(unittest.TestCase):
socketserver.DatagramRequestHandler, socketserver.DatagramRequestHandler,
self.dgram_examine) 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 # Alas, on Linux (at least) recvfrom() doesn't return a meaningful
# client address so this cannot work: # client address so this cannot work:
......
...@@ -559,6 +559,7 @@ Jacek Konieczny ...@@ -559,6 +559,7 @@ Jacek Konieczny
Arkady Koplyarov Arkady Koplyarov
Vlad Korolev Vlad Korolev
Joseph Koshy Joseph Koshy
Jerzy Kozera
Maksim Kozyarchuk Maksim Kozyarchuk
Stefan Krah Stefan Krah
Bob Kras Bob Kras
......
...@@ -19,6 +19,10 @@ Core and Builtins ...@@ -19,6 +19,10 @@ Core and Builtins
Library 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 #14522: Avoid duplicating socket handles in multiprocessing.connection. - Issue #14522: Avoid duplicating socket handles in multiprocessing.connection.
Patch by sbt. Patch by sbt.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment