Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b0a9c66

Browse files
committed
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.
1 parent 317c8d2 commit b0a9c66

4 files changed

Lines changed: 52 additions & 2 deletions

File tree

Lib/socketserver.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ class will essentially render the service "deaf" while one request is
133133
import select
134134
import sys
135135
import os
136+
import errno
136137
try:
137138
import threading
138139
except ImportError:
@@ -147,6 +148,15 @@ class will essentially render the service "deaf" while one request is
147148
"ThreadingUnixStreamServer",
148149
"ThreadingUnixDatagramServer"])
149150

151+
def _eintr_retry(func, *args):
152+
"""restart a system call interrupted by EINTR"""
153+
while True:
154+
try:
155+
return func(*args)
156+
except OSError as e:
157+
if e.errno != errno.EINTR:
158+
raise
159+
150160
class BaseServer:
151161

152162
"""Base class for server classes.
@@ -222,7 +232,8 @@ def serve_forever(self, poll_interval=0.5):
222232
# connecting to the socket to wake this up instead of
223233
# polling. Polling reduces our responsiveness to a
224234
# shutdown request and wastes cpu at all other times.
225-
r, w, e = select.select([self], [], [], poll_interval)
235+
r, w, e = _eintr_retry(select.select, [self], [], [],
236+
poll_interval)
226237
if self in r:
227238
self._handle_request_noblock()
228239
finally:
@@ -262,7 +273,7 @@ def handle_request(self):
262273
timeout = self.timeout
263274
elif self.timeout is not None:
264275
timeout = min(timeout, self.timeout)
265-
fd_sets = select.select([self], [], [], timeout)
276+
fd_sets = _eintr_retry(select.select, [self], [], [], timeout)
266277
if not fd_sets[0]:
267278
self.handle_timeout()
268279
return

Lib/test/test_socketserver.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import select
99
import signal
1010
import socket
11+
import select
12+
import errno
1113
import tempfile
1214
import unittest
1315
import socketserver
@@ -226,6 +228,38 @@ def test_ForkingUDPServer(self):
226228
socketserver.DatagramRequestHandler,
227229
self.dgram_examine)
228230

231+
@contextlib.contextmanager
232+
def mocked_select_module(self):
233+
"""Mocks the select.select() call to raise EINTR for first call"""
234+
old_select = select.select
235+
236+
class MockSelect:
237+
def __init__(self):
238+
self.called = 0
239+
240+
def __call__(self, *args):
241+
self.called += 1
242+
if self.called == 1:
243+
# raise the exception on first call
244+
raise OSError(errno.EINTR, os.strerror(errno.EINTR))
245+
else:
246+
# Return real select value for consecutive calls
247+
return old_select(*args)
248+
249+
select.select = MockSelect()
250+
try:
251+
yield select.select
252+
finally:
253+
select.select = old_select
254+
255+
def test_InterruptServerSelectCall(self):
256+
with self.mocked_select_module() as mock_select:
257+
pid = self.run_server(socketserver.TCPServer,
258+
socketserver.StreamRequestHandler,
259+
self.stream_examine)
260+
# Make sure select was called again:
261+
self.assertGreater(mock_select.called, 1)
262+
229263
# Alas, on Linux (at least) recvfrom() doesn't return a meaningful
230264
# client address so this cannot work:
231265

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ Damon Kohler
510510
Marko Kohtala
511511
Vlad Korolev
512512
Joseph Koshy
513+
Jerzy Kozera
513514
Maksim Kozyarchuk
514515
Stefan Krah
515516
Bob Kras

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ Core and Builtins
3939
Library
4040
-------
4141

42+
- Issue #7978: socketserver now restarts the select() call when EINTR is
43+
returned. This avoids crashing the server loop when a signal is received.
44+
Patch by Jerzy Kozera.
45+
4246
- Issue #14496: Fix wrong name in idlelib/tabbedpages.py.
4347
Patch by Popa Claudiu.
4448

0 commit comments

Comments
 (0)