Skip to content

Commit 4f6789b

Browse files
authored
Merge pull request #377 from Pylons/bugfix/select-closed-socket-race
Bugfix: Retry if a thread closes a socket before we select() on it
2 parents 1952050 + c7a3d7e commit 4f6789b

File tree

3 files changed

+15
-12
lines changed

3 files changed

+15
-12
lines changed

src/waitress/channel.py

+10-8
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,10 @@ def handle_write(self):
126126
if self.will_close:
127127
self.handle_close()
128128

129-
def _flush_exception(self, flush):
129+
def _flush_exception(self, flush, do_close=True):
130130
if flush:
131131
try:
132-
return (flush(), False)
132+
return (flush(do_close=do_close), False)
133133
except OSError:
134134
if self.adj.log_socket_errors:
135135
self.logger.exception("Socket error")
@@ -240,20 +240,20 @@ def received(self, data):
240240

241241
return True
242242

243-
def _flush_some_if_lockable(self):
243+
def _flush_some_if_lockable(self, do_close=True):
244244
# Since our task may be appending to the outbuf, we try to acquire
245245
# the lock, but we don't block if we can't.
246246

247247
if self.outbuf_lock.acquire(False):
248248
try:
249-
self._flush_some()
249+
self._flush_some(do_close=do_close)
250250

251251
if self.total_outbufs_len < self.adj.outbuf_high_watermark:
252252
self.outbuf_lock.notify()
253253
finally:
254254
self.outbuf_lock.release()
255255

256-
def _flush_some(self):
256+
def _flush_some(self, do_close=True):
257257
# Send as much data as possible to our client
258258

259259
sent = 0
@@ -267,7 +267,7 @@ def _flush_some(self):
267267

268268
while outbuflen > 0:
269269
chunk = outbuf.get(self.sendbuf_len)
270-
num_sent = self.send(chunk)
270+
num_sent = self.send(chunk, do_close=do_close)
271271

272272
if num_sent:
273273
outbuf.skip(num_sent, True)
@@ -374,7 +374,9 @@ def write_soon(self, data):
374374
self.total_outbufs_len += num_bytes
375375

376376
if self.total_outbufs_len >= self.adj.send_bytes:
377-
(flushed, exception) = self._flush_exception(self._flush_some)
377+
(flushed, exception) = self._flush_exception(
378+
self._flush_some, do_close=False
379+
)
378380

379381
if (
380382
exception
@@ -392,7 +394,7 @@ def _flush_outbufs_below_high_watermark(self):
392394

393395
if self.total_outbufs_len > self.adj.outbuf_high_watermark:
394396
with self.outbuf_lock:
395-
(_, exception) = self._flush_exception(self._flush_some)
397+
(_, exception) = self._flush_exception(self._flush_some, do_close=False)
396398

397399
if exception:
398400
# An exception happened while flushing, wake up the main

src/waitress/wasyncore.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -426,15 +426,16 @@ def accept(self):
426426
else:
427427
return conn, addr
428428

429-
def send(self, data):
429+
def send(self, data, do_close=True):
430430
try:
431431
result = self.socket.send(data)
432432
return result
433433
except OSError as why:
434434
if why.args[0] == EWOULDBLOCK:
435435
return 0
436436
elif why.args[0] in _DISCONNECTED:
437-
self.handle_close()
437+
if do_close:
438+
self.handle_close()
438439
return 0
439440
else:
440441
raise

tests/test_channel.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ def test_handle_write_no_notify_after_flush(self):
376376
inst.total_outbufs_len = len(inst.outbufs[0])
377377
inst.adj.send_bytes = 1
378378
inst.adj.outbuf_high_watermark = 2
379-
sock.send = lambda x: False
379+
sock.send = lambda x, do_close=True: False
380380
inst.will_close = False
381381
inst.last_activity = 0
382382
result = inst.handle_write()
@@ -453,7 +453,7 @@ def get(self, numbytes):
453453

454454
buf = DummyHugeOutbuffer()
455455
inst.outbufs = [buf]
456-
inst.send = lambda *arg: 0
456+
inst.send = lambda *arg, do_close: 0
457457
result = inst._flush_some()
458458
# we are testing that _flush_some doesn't raise an OverflowError
459459
# when one of its outbufs has a __len__ that returns gt sys.maxint

0 commit comments

Comments
 (0)