summaryrefslogtreecommitdiffstats
path: root/Modules/_io/bufferedio.c
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2010-12-03 18:41:39 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2010-12-03 18:41:39 (GMT)
commitf3b68b3f981b8baa81e3e838ab921d2e4362ae33 (patch)
tree37d2eea64775edd9f74f251e4cc8a780e98cbe2d /Modules/_io/bufferedio.c
parent38e117d20a1914e9506b3f901c7170eadccefe54 (diff)
downloadcpython-f3b68b3f981b8baa81e3e838ab921d2e4362ae33.zip
cpython-f3b68b3f981b8baa81e3e838ab921d2e4362ae33.tar.gz
cpython-f3b68b3f981b8baa81e3e838ab921d2e4362ae33.tar.bz2
Issue #10478: Reentrant calls inside buffered IO objects (for example by
way of a signal handler) now raise a RuntimeError instead of freezing the current process.
Diffstat (limited to 'Modules/_io/bufferedio.c')
-rw-r--r--Modules/_io/bufferedio.c71
1 files changed, 52 insertions, 19 deletions
diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c
index 7661a00..f7e34a8 100644
--- a/Modules/_io/bufferedio.c
+++ b/Modules/_io/bufferedio.c
@@ -225,6 +225,7 @@ typedef struct {
#ifdef WITH_THREAD
PyThread_type_lock lock;
+ volatile long owner;
#endif
Py_ssize_t buffer_size;
@@ -260,17 +261,34 @@ typedef struct {
/* These macros protect the buffered object against concurrent operations. */
#ifdef WITH_THREAD
-#define ENTER_BUFFERED(self) \
- if (!PyThread_acquire_lock(self->lock, 0)) { \
- Py_BEGIN_ALLOW_THREADS \
- PyThread_acquire_lock(self->lock, 1); \
- Py_END_ALLOW_THREADS \
+
+static int
+_enter_buffered_busy(buffered *self)
+{
+ if (self->owner == PyThread_get_thread_ident()) {
+ PyErr_Format(PyExc_RuntimeError,
+ "reentrant call inside %R", self);
+ return 0;
}
+ Py_BEGIN_ALLOW_THREADS
+ PyThread_acquire_lock(self->lock, 1);
+ Py_END_ALLOW_THREADS
+ return 1;
+}
+
+#define ENTER_BUFFERED(self) \
+ ( (PyThread_acquire_lock(self->lock, 0) ? \
+ 1 : _enter_buffered_busy(self)) \
+ && (self->owner = PyThread_get_thread_ident(), 1) )
#define LEAVE_BUFFERED(self) \
- PyThread_release_lock(self->lock);
+ do { \
+ self->owner = 0; \
+ PyThread_release_lock(self->lock); \
+ } while(0);
+
#else
-#define ENTER_BUFFERED(self)
+#define ENTER_BUFFERED(self) 1
#define LEAVE_BUFFERED(self)
#endif
@@ -444,7 +462,8 @@ buffered_close(buffered *self, PyObject *args)
int r;
CHECK_INITIALIZED(self)
- ENTER_BUFFERED(self)
+ if (!ENTER_BUFFERED(self))
+ return NULL;
r = buffered_closed(self);
if (r < 0)
@@ -465,7 +484,8 @@ buffered_close(buffered *self, PyObject *args)
/* flush() will most probably re-take the lock, so drop it first */
LEAVE_BUFFERED(self)
res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL);
- ENTER_BUFFERED(self)
+ if (!ENTER_BUFFERED(self))
+ return NULL;
if (res == NULL) {
goto end;
}
@@ -679,6 +699,7 @@ _buffered_init(buffered *self)
PyErr_SetString(PyExc_RuntimeError, "can't allocate read lock");
return -1;
}
+ self->owner = 0;
#endif
/* Find out whether buffer_size is a power of 2 */
/* XXX is this optimization useful? */
@@ -705,7 +726,8 @@ buffered_flush(buffered *self, PyObject *args)
CHECK_INITIALIZED(self)
CHECK_CLOSED(self, "flush of closed file")
- ENTER_BUFFERED(self)
+ if (!ENTER_BUFFERED(self))
+ return NULL;
res = _bufferedwriter_flush_unlocked(self, 0);
if (res != NULL && self->readable) {
/* Rewind the raw stream so that its position corresponds to
@@ -732,7 +754,8 @@ buffered_peek(buffered *self, PyObject *args)
return NULL;
}
- ENTER_BUFFERED(self)
+ if (!ENTER_BUFFERED(self))
+ return NULL;
if (self->writable) {
res = _bufferedwriter_flush_unlocked(self, 1);
@@ -767,7 +790,8 @@ buffered_read(buffered *self, PyObject *args)
if (n == -1) {
/* The number of bytes is unspecified, read until the end of stream */
- ENTER_BUFFERED(self)
+ if (!ENTER_BUFFERED(self))
+ return NULL;
res = _bufferedreader_read_all(self);
LEAVE_BUFFERED(self)
}
@@ -775,7 +799,8 @@ buffered_read(buffered *self, PyObject *args)
res = _bufferedreader_read_fast(self, n);
if (res == Py_None) {
Py_DECREF(res);
- ENTER_BUFFERED(self)
+ if (!ENTER_BUFFERED(self))
+ return NULL;
res = _bufferedreader_read_generic(self, n);
LEAVE_BUFFERED(self)
}
@@ -803,7 +828,8 @@ buffered_read1(buffered *self, PyObject *args)
if (n == 0)
return PyBytes_FromStringAndSize(NULL, 0);
- ENTER_BUFFERED(self)
+ if (!ENTER_BUFFERED(self))
+ return NULL;
if (self->writable) {
res = _bufferedwriter_flush_unlocked(self, 1);
@@ -859,7 +885,8 @@ buffered_readinto(buffered *self, PyObject *args)
/* TODO: use raw.readinto() instead! */
if (self->writable) {
- ENTER_BUFFERED(self)
+ if (!ENTER_BUFFERED(self))
+ return NULL;
res = _bufferedwriter_flush_unlocked(self, 0);
LEAVE_BUFFERED(self)
if (res == NULL)
@@ -903,7 +930,8 @@ _buffered_readline(buffered *self, Py_ssize_t limit)
goto end_unlocked;
}
- ENTER_BUFFERED(self)
+ if (!ENTER_BUFFERED(self))
+ goto end_unlocked;
/* Now we try to get some more from the raw stream */
if (self->writable) {
@@ -1053,7 +1081,8 @@ buffered_seek(buffered *self, PyObject *args)
}
}
- ENTER_BUFFERED(self)
+ if (!ENTER_BUFFERED(self))
+ return NULL;
/* Fallback: invoke raw seek() method and clear buffer */
if (self->writable) {
@@ -1091,7 +1120,8 @@ buffered_truncate(buffered *self, PyObject *args)
return NULL;
}
- ENTER_BUFFERED(self)
+ if (!ENTER_BUFFERED(self))
+ return NULL;
if (self->writable) {
res = _bufferedwriter_flush_unlocked(self, 0);
@@ -1748,7 +1778,10 @@ bufferedwriter_write(buffered *self, PyObject *args)
return NULL;
}
- ENTER_BUFFERED(self)
+ if (!ENTER_BUFFERED(self)) {
+ PyBuffer_Release(&buf);
+ return NULL;
+ }
/* Fast path: the data to write can be fully buffered. */
if (!VALID_READ_BUFFER(self) && !VALID_WRITE_BUFFER(self)) {