Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1ab0ed9

Browse files
committed
sockerserver.serve_forever: Use another file descriptor for fast shutdown
1 parent92b5dc7 commit1ab0ed9

File tree

3 files changed

+83
-24
lines changed

3 files changed

+83
-24
lines changed

‎Doc/library/socketserver.rst

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,19 +212,28 @@ Server Objects
212212
will return.
213213

214214

215-
..method::serve_forever(poll_interval=0.5)
215+
..method::serve_forever(poll_interval=None, fast_shutdown=True)
216216

217-
Handle requests until an explicit:meth:`shutdown` request. Poll for
218-
shutdown every *poll_interval* seconds.
217+
Handle requests until an explicit:meth:`shutdown` request.
219218
Ignores the:attr:`timeout` attribute. It
220219
also calls:meth:`service_actions`, which may be used by a subclass or mixin
221220
to provide actions specific to a given service. For example, the
222221
:class:`ForkingMixIn` class uses:meth:`service_actions` to clean up zombie
223222
child processes.
224223

224+
Poll for shutdown every *poll_interval* seconds.
225+
*poll_interval* defaults to 0.5.
226+
227+
If fast shutdown is supported (see:attr:`supports_fast_shutdown`)
228+
and *fast_shutdown* is true, then:meth:`shutdown` will return almost
229+
immediately. In this case, polling is still used but *poll_interval*
230+
defaults to 60.
231+
225232
..versionchanged::3.3
226233
Added ``service_actions`` call to the ``serve_forever`` method.
227234

235+
..versionchanged::3.12
236+
Added the *fast_shutdown* argument.
228237

229238
..method::service_actions()
230239

@@ -234,6 +243,13 @@ Server Objects
234243

235244
..versionadded::3.3
236245

246+
..attribute::supports_fast_shutdown
247+
248+
True if the server supports fast shutdown. True on platforms that
249+
have:func:`os.pipe`. May be set to False in subclasses.
250+
251+
..versionadded::3.12
252+
237253
..method::shutdown()
238254

239255
Tell the:meth:`serve_forever` loop to stop and wait until it does.

‎Lib/socketserver.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ class will essentially render the service "deaf" while one request is
150150
_ServerSelector=selectors.SelectSelector
151151

152152

153+
try:
154+
_os_pipe=os.pipe
155+
exceptAttributeError:
156+
_os_pipe=NONE
157+
158+
153159
classBaseServer:
154160

155161
"""Base class for server classes.
@@ -196,13 +202,15 @@ class BaseServer:
196202
"""
197203

198204
timeout=None
205+
supports_fast_shutdown=bool(_os_pipe)
199206

200207
def__init__(self,server_address,RequestHandlerClass):
201208
"""Constructor. May be extended, do not override."""
202209
self.server_address=server_address
203210
self.RequestHandlerClass=RequestHandlerClass
204211
self.__is_shut_down=threading.Event()
205212
self.__shutdown_request=False
213+
self.__shutdown_pipe=None
206214

207215
defserver_activate(self):
208216
"""Called by constructor to activate the server.
@@ -212,21 +220,30 @@ def server_activate(self):
212220
"""
213221
pass
214222

215-
defserve_forever(self,poll_interval=0.5):
223+
defserve_forever(self,poll_interval=None,fast_shutdown=True):
216224
"""Handle one request at a time until shutdown.
217225
218226
Polls for shutdown every poll_interval seconds. Ignores
219227
self.timeout. If you need to do periodic tasks, do them in
220228
another thread.
221229
"""
222230
self.__is_shut_down.clear()
231+
shutdown_pipe_r=shutdown_pipe_w=None
232+
iffast_shutdownandself.supports_fast_shutdown:
233+
shutdown_pipe_r,shutdown_pipe_w=_os_pipe()
234+
ifpoll_intervalisNone:
235+
ifshutdown_pipe_r:
236+
# When fast shutdown doesn't work for any reason,
237+
# fall-back to very slow polling
238+
poll_interval=60
239+
else:
240+
poll_interval=0.5
223241
try:
224-
# XXX: Consider using another file descriptor or connecting to the
225-
# socket to wake this up instead of polling. Polling reduces our
226-
# responsiveness to a shutdown request and wastes cpu at all other
227-
# times.
242+
self.__shutdown_pipe=shutdown_pipe_w
228243
with_ServerSelector()asselector:
229244
selector.register(self,selectors.EVENT_READ)
245+
ifshutdown_pipe_r:
246+
selector.register(shutdown_pipe_r,selectors.EVENT_READ)
230247

231248
whilenotself.__shutdown_request:
232249
ready=selector.select(poll_interval)
@@ -240,6 +257,11 @@ def serve_forever(self, poll_interval=0.5):
240257
finally:
241258
self.__shutdown_request=False
242259
self.__is_shut_down.set()
260+
ifshutdown_pipe_r:
261+
os.close(shutdown_pipe_r)
262+
ifshutdown_pipe_w:
263+
os.close(shutdown_pipe_w)
264+
self.__shutdown_pipe=None
243265

244266
defshutdown(self):
245267
"""Stops the serve_forever loop.
@@ -249,6 +271,8 @@ def shutdown(self):
249271
deadlock.
250272
"""
251273
self.__shutdown_request=True
274+
ifself.__shutdown_pipe:
275+
os.write(self.__shutdown_pipe,b'.')
252276
self.__is_shut_down.wait()
253277

254278
defservice_actions(self):

‎Lib/test/test_socketserver.py

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
importthreading
1313
importunittest
1414
importsocketserver
15+
importtime
1516

1617
importtest.support
1718
fromtest.supportimportreap_children,verbose
@@ -133,22 +134,40 @@ def run_server(self, svrcls, hdlrbase, testfunc):
133134
print("ADDR =",addr)
134135
print("CLASS =",svrcls)
135136

136-
t=threading.Thread(
137-
name='%s serving'%svrcls,
138-
target=server.serve_forever,
139-
# Short poll interval to make the test finish quickly.
140-
# Time between requests is short enough that we won't wake
141-
# up spuriously too many times.
142-
kwargs={'poll_interval':0.01})
143-
t.daemon=True# In case this function raises.
144-
t.start()
145-
ifverbose:print("server running")
146-
foriinrange(3):
147-
ifverbose:print("test client",i)
148-
testfunc(svrcls.address_family,addr)
149-
ifverbose:print("waiting for server")
150-
server.shutdown()
151-
t.join()
137+
# Short poll interval to make the test finish quickly.
138+
# Time between requests is short enough that we won't wake
139+
# up spuriously too many times.
140+
short_interval=0.01
141+
# Use a longer poll interval (used when polling shouldn't kick in)
142+
long_interval=test.support.LOOPBACK_TIMEOUT*2
143+
cases= [{'fast_shutdown':False,'poll_interval':short_interval}]
144+
ifserver.supports_fast_shutdown:
145+
cases.append({'poll_interval':long_interval})
146+
cases.append({'fast_shutdown':True,'poll_interval':long_interval})
147+
cases.append({'fast_shutdown':True})
148+
else:
149+
# fast_shutdown argument doesn't matter
150+
cases= [{'poll_interval':short_interval}]
151+
cases= [{'fast_shutdown':True,'poll_interval':short_interval}]
152+
153+
forkwargsincases:
154+
withself.subTest(kwargs):
155+
t=threading.Thread(
156+
name='%s serving'%svrcls,
157+
target=server.serve_forever,
158+
kwargs=kwargs)
159+
t.daemon=True# In case this function raises.
160+
t.start()
161+
ifverbose:print("server running")
162+
foriinrange(3):
163+
ifverbose:print("test client",i)
164+
testfunc(svrcls.address_family,addr)
165+
ifverbose:print("waiting for server")
166+
start_time=time.monotonic()
167+
server.shutdown()
168+
t.join()
169+
time_taken=time.monotonic()-start_time
170+
self.assertLess(time_taken,test.support.LOOPBACK_TIMEOUT)
152171
server.server_close()
153172
self.assertEqual(-1,server.socket.fileno())
154173
ifHAVE_FORKINGandisinstance(server,socketserver.ForkingMixIn):

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp