Skip to content

Commit 5617df1

Browse files
Issue python#8865: Concurrent invocation of select.poll.poll() now raises a
RuntimeError exception. Patch by Christian Schubert.
2 parents edd0de5 + b1973c2 commit 5617df1

4 files changed

Lines changed: 57 additions & 2 deletions

File tree

Lib/test/test_poll.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
# Test case for the os.poll() function
22

3-
import os, select, random, unittest, subprocess
3+
import os
4+
import random
5+
import select
46
import _testcapi
5-
from test.support import TESTFN, run_unittest
7+
try:
8+
import threading
9+
except ImportError:
10+
threading = None
11+
import time
12+
import unittest
13+
from test.support import TESTFN, run_unittest, reap_threads
614

715
try:
816
select.poll
@@ -161,6 +169,36 @@ def test_poll3(self):
161169
self.assertRaises(OverflowError, pollster.poll, _testcapi.INT_MAX + 1)
162170
self.assertRaises(OverflowError, pollster.poll, _testcapi.UINT_MAX + 1)
163171

172+
@unittest.skipUnless(threading, 'Threading required for this test.')
173+
@reap_threads
174+
def test_threaded_poll(self):
175+
r, w = os.pipe()
176+
self.addCleanup(os.close, r)
177+
self.addCleanup(os.close, w)
178+
rfds = []
179+
for i in range(10):
180+
fd = os.dup(r)
181+
self.addCleanup(os.close, fd)
182+
rfds.append(fd)
183+
pollster = select.poll()
184+
for fd in rfds:
185+
pollster.register(fd, select.POLLIN)
186+
187+
t = threading.Thread(target=pollster.poll)
188+
t.start()
189+
try:
190+
time.sleep(0.5)
191+
# trigger ufds array reallocation
192+
for fd in rfds:
193+
pollster.unregister(fd)
194+
pollster.register(w, select.POLLOUT)
195+
self.assertRaises(RuntimeError, pollster.poll)
196+
finally:
197+
# and make the call to poll() from the thread return
198+
os.write(w, b'spam')
199+
t.join()
200+
201+
164202
def test_main():
165203
run_unittest(PollTests)
166204

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,7 @@ Arvin Schnell
11311131
Scott Schram
11321132
Robin Schreiber
11331133
Chad J. Schroeder
1134+
Christian Schubert
11341135
Sam Schulenburg
11351136
Stefan Schwarzer
11361137
Dietmar Schwertberger

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ Core and Builtins
3838
Library
3939
-------
4040

41+
- Issue #8865: Concurrent invocation of select.poll.poll() now raises a
42+
RuntimeError exception. Patch by Christian Schubert.
43+
4144
- Issue #18777: The ssl module now uses the new CRYPTO_THREADID API of
4245
OpenSSL 1.0.0+ instead of the deprecated CRYPTO id callback function.
4346

Modules/selectmodule.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ typedef struct {
327327
int ufd_uptodate;
328328
int ufd_len;
329329
struct pollfd *ufds;
330+
int poll_running;
330331
} pollObject;
331332

332333
static PyTypeObject poll_Type;
@@ -523,16 +524,27 @@ poll_poll(pollObject *self, PyObject *args)
523524
return NULL;
524525
}
525526

527+
/* Avoid concurrent poll() invocation, issue 8865 */
528+
if (self->poll_running) {
529+
PyErr_SetString(PyExc_RuntimeError,
530+
"concurrent poll() invocation");
531+
return NULL;
532+
}
533+
526534
/* Ensure the ufd array is up to date */
527535
if (!self->ufd_uptodate)
528536
if (update_ufd_array(self) == 0)
529537
return NULL;
530538

539+
self->poll_running = 1;
540+
531541
/* call poll() */
532542
Py_BEGIN_ALLOW_THREADS
533543
poll_result = poll(self->ufds, self->ufd_len, timeout);
534544
Py_END_ALLOW_THREADS
535545

546+
self->poll_running = 0;
547+
536548
if (poll_result < 0) {
537549
PyErr_SetFromErrno(PyExc_OSError);
538550
return NULL;
@@ -609,6 +621,7 @@ newPollObject(void)
609621
array pointed to by ufds matches the contents of the dictionary. */
610622
self->ufd_uptodate = 0;
611623
self->ufds = NULL;
624+
self->poll_running = 0;
612625
self->dict = PyDict_New();
613626
if (self->dict == NULL) {
614627
Py_DECREF(self);

0 commit comments

Comments
 (0)