summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2013-08-20 17:38:21 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2013-08-20 17:38:21 (GMT)
commitb1973c252c2eec757eaa067afaf593c2cc5ea8db (patch)
treeed78f6f23d427c0dd0ee589c308d1b5cbb297a77
parentec67d187ee9a86aaf1108643832f69ad3bc0e369 (diff)
downloadcpython-b1973c252c2eec757eaa067afaf593c2cc5ea8db.zip
cpython-b1973c252c2eec757eaa067afaf593c2cc5ea8db.tar.gz
cpython-b1973c252c2eec757eaa067afaf593c2cc5ea8db.tar.bz2
Issue #8865: Concurrent invocation of select.poll.poll() now raises a
RuntimeError exception. Patch by Christian Schubert.
-rw-r--r--Lib/test/test_poll.py42
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/selectmodule.c13
4 files changed, 57 insertions, 2 deletions
diff --git a/Lib/test/test_poll.py b/Lib/test/test_poll.py
index f2d1795..f98a280 100644
--- a/Lib/test/test_poll.py
+++ b/Lib/test/test_poll.py
@@ -1,8 +1,16 @@
# Test case for the os.poll() function
-import os, select, random, unittest
+import os
+import random
+import select
import _testcapi
-from test.support import TESTFN, run_unittest
+try:
+ import threading
+except ImportError:
+ threading = None
+import time
+import unittest
+from test.support import TESTFN, run_unittest, reap_threads
try:
select.poll
@@ -160,6 +168,36 @@ class PollTests(unittest.TestCase):
self.assertRaises(OverflowError, pollster.poll, _testcapi.INT_MAX + 1)
self.assertRaises(OverflowError, pollster.poll, _testcapi.UINT_MAX + 1)
+ @unittest.skipUnless(threading, 'Threading required for this test.')
+ @reap_threads
+ def test_threaded_poll(self):
+ r, w = os.pipe()
+ self.addCleanup(os.close, r)
+ self.addCleanup(os.close, w)
+ rfds = []
+ for i in range(10):
+ fd = os.dup(r)
+ self.addCleanup(os.close, fd)
+ rfds.append(fd)
+ pollster = select.poll()
+ for fd in rfds:
+ pollster.register(fd, select.POLLIN)
+
+ t = threading.Thread(target=pollster.poll)
+ t.start()
+ try:
+ time.sleep(0.5)
+ # trigger ufds array reallocation
+ for fd in rfds:
+ pollster.unregister(fd)
+ pollster.register(w, select.POLLOUT)
+ self.assertRaises(RuntimeError, pollster.poll)
+ finally:
+ # and make the call to poll() from the thread return
+ os.write(w, b'spam')
+ t.join()
+
+
def test_main():
run_unittest(PollTests)
diff --git a/Misc/ACKS b/Misc/ACKS
index 349c70e..78d3b47 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1097,6 +1097,7 @@ Arvin Schnell
Scott Schram
Robin Schreiber
Chad J. Schroeder
+Christian Schubert
Sam Schulenburg
Stefan Schwarzer
Dietmar Schwertberger
diff --git a/Misc/NEWS b/Misc/NEWS
index 27fa09d..416f3d3 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -66,6 +66,9 @@ Core and Builtins
Library
-------
+- Issue #8865: Concurrent invocation of select.poll.poll() now raises a
+ RuntimeError exception. Patch by Christian Schubert.
+
- Issue #13461: Fix a crash in the TextIOWrapper.tell method on 64-bit
platforms. Patch by Yogesh Chaudhari.
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
index 2ca2d41..92d0e67 100644
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -332,6 +332,7 @@ typedef struct {
int ufd_uptodate;
int ufd_len;
struct pollfd *ufds;
+ int poll_running;
} pollObject;
static PyTypeObject poll_Type;
@@ -528,16 +529,27 @@ poll_poll(pollObject *self, PyObject *args)
return NULL;
}
+ /* Avoid concurrent poll() invocation, issue 8865 */
+ if (self->poll_running) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "concurrent poll() invocation");
+ return NULL;
+ }
+
/* Ensure the ufd array is up to date */
if (!self->ufd_uptodate)
if (update_ufd_array(self) == 0)
return NULL;
+ self->poll_running = 1;
+
/* call poll() */
Py_BEGIN_ALLOW_THREADS
poll_result = poll(self->ufds, self->ufd_len, timeout);
Py_END_ALLOW_THREADS
+ self->poll_running = 0;
+
if (poll_result < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
@@ -614,6 +626,7 @@ newPollObject(void)
array pointed to by ufds matches the contents of the dictionary. */
self->ufd_uptodate = 0;
self->ufds = NULL;
+ self->poll_running = 0;
self->dict = PyDict_New();
if (self->dict == NULL) {
Py_DECREF(self);