@@ -225,6 +225,7 @@ typedef struct {
225225
226226#ifdef WITH_THREAD
227227 PyThread_type_lock lock ;
228+ volatile long owner ;
228229#endif
229230
230231 Py_ssize_t buffer_size ;
@@ -260,17 +261,34 @@ typedef struct {
260261/* These macros protect the buffered object against concurrent operations. */
261262
262263#ifdef WITH_THREAD
263- #define ENTER_BUFFERED (self ) \
264- if (!PyThread_acquire_lock(self->lock, 0)) { \
265- Py_BEGIN_ALLOW_THREADS \
266- PyThread_acquire_lock(self->lock, 1); \
267- Py_END_ALLOW_THREADS \
264+
265+ static int
266+ _enter_buffered_busy (buffered * self )
267+ {
268+ if (self -> owner == PyThread_get_thread_ident ()) {
269+ PyErr_Format (PyExc_RuntimeError ,
270+ "reentrant call inside %R" , self );
271+ return 0 ;
268272 }
273+ Py_BEGIN_ALLOW_THREADS
274+ PyThread_acquire_lock (self -> lock , 1 );
275+ Py_END_ALLOW_THREADS
276+ return 1 ;
277+ }
278+
279+ #define ENTER_BUFFERED (self ) \
280+ ( (PyThread_acquire_lock(self->lock, 0) ? \
281+ 1 : _enter_buffered_busy(self)) \
282+ && (self->owner = PyThread_get_thread_ident(), 1) )
269283
270284#define LEAVE_BUFFERED (self ) \
271- PyThread_release_lock(self->lock);
285+ do { \
286+ self->owner = 0; \
287+ PyThread_release_lock(self->lock); \
288+ } while(0);
289+
272290#else
273- #define ENTER_BUFFERED (self )
291+ #define ENTER_BUFFERED (self ) 1
274292#define LEAVE_BUFFERED (self )
275293#endif
276294
@@ -444,7 +462,8 @@ buffered_close(buffered *self, PyObject *args)
444462 int r ;
445463
446464 CHECK_INITIALIZED (self )
447- ENTER_BUFFERED (self )
465+ if (!ENTER_BUFFERED (self ))
466+ return NULL ;
448467
449468 r = buffered_closed (self );
450469 if (r < 0 )
@@ -465,7 +484,8 @@ buffered_close(buffered *self, PyObject *args)
465484 /* flush() will most probably re-take the lock, so drop it first */
466485 LEAVE_BUFFERED (self )
467486 res = PyObject_CallMethodObjArgs ((PyObject * )self , _PyIO_str_flush , NULL );
468- ENTER_BUFFERED (self )
487+ if (!ENTER_BUFFERED (self ))
488+ return NULL ;
469489 if (res == NULL ) {
470490 goto end ;
471491 }
@@ -679,6 +699,7 @@ _buffered_init(buffered *self)
679699 PyErr_SetString (PyExc_RuntimeError , "can't allocate read lock" );
680700 return -1 ;
681701 }
702+ self -> owner = 0 ;
682703#endif
683704 /* Find out whether buffer_size is a power of 2 */
684705 /* XXX is this optimization useful? */
@@ -705,7 +726,8 @@ buffered_flush(buffered *self, PyObject *args)
705726 CHECK_INITIALIZED (self )
706727 CHECK_CLOSED (self , "flush of closed file" )
707728
708- ENTER_BUFFERED (self )
729+ if (!ENTER_BUFFERED (self ))
730+ return NULL ;
709731 res = _bufferedwriter_flush_unlocked (self , 0 );
710732 if (res != NULL && self -> readable ) {
711733 /* Rewind the raw stream so that its position corresponds to
@@ -732,7 +754,8 @@ buffered_peek(buffered *self, PyObject *args)
732754 return NULL ;
733755 }
734756
735- ENTER_BUFFERED (self )
757+ if (!ENTER_BUFFERED (self ))
758+ return NULL ;
736759
737760 if (self -> writable ) {
738761 res = _bufferedwriter_flush_unlocked (self , 1 );
@@ -767,15 +790,17 @@ buffered_read(buffered *self, PyObject *args)
767790
768791 if (n == -1 ) {
769792 /* The number of bytes is unspecified, read until the end of stream */
770- ENTER_BUFFERED (self )
793+ if (!ENTER_BUFFERED (self ))
794+ return NULL ;
771795 res = _bufferedreader_read_all (self );
772796 LEAVE_BUFFERED (self )
773797 }
774798 else {
775799 res = _bufferedreader_read_fast (self , n );
776800 if (res == Py_None ) {
777801 Py_DECREF (res );
778- ENTER_BUFFERED (self )
802+ if (!ENTER_BUFFERED (self ))
803+ return NULL ;
779804 res = _bufferedreader_read_generic (self , n );
780805 LEAVE_BUFFERED (self )
781806 }
@@ -803,7 +828,8 @@ buffered_read1(buffered *self, PyObject *args)
803828 if (n == 0 )
804829 return PyBytes_FromStringAndSize (NULL , 0 );
805830
806- ENTER_BUFFERED (self )
831+ if (!ENTER_BUFFERED (self ))
832+ return NULL ;
807833
808834 if (self -> writable ) {
809835 res = _bufferedwriter_flush_unlocked (self , 1 );
@@ -859,7 +885,8 @@ buffered_readinto(buffered *self, PyObject *args)
859885
860886 /* TODO: use raw.readinto() instead! */
861887 if (self -> writable ) {
862- ENTER_BUFFERED (self )
888+ if (!ENTER_BUFFERED (self ))
889+ return NULL ;
863890 res = _bufferedwriter_flush_unlocked (self , 0 );
864891 LEAVE_BUFFERED (self )
865892 if (res == NULL)
@@ -903,7 +930,8 @@ _buffered_readline(buffered *self, Py_ssize_t limit)
903930 goto end_unlocked ;
904931 }
905932
906- ENTER_BUFFERED (self )
933+ if (!ENTER_BUFFERED (self ))
934+ goto end_unlocked ;
907935
908936 /* Now we try to get some more from the raw stream */
909937 if (self -> writable ) {
@@ -1053,7 +1081,8 @@ buffered_seek(buffered *self, PyObject *args)
10531081 }
10541082 }
10551083
1056- ENTER_BUFFERED (self )
1084+ if (!ENTER_BUFFERED (self ))
1085+ return NULL ;
10571086
10581087 /* Fallback: invoke raw seek() method and clear buffer */
10591088 if (self -> writable ) {
@@ -1091,7 +1120,8 @@ buffered_truncate(buffered *self, PyObject *args)
10911120 return NULL ;
10921121 }
10931122
1094- ENTER_BUFFERED (self )
1123+ if (!ENTER_BUFFERED (self ))
1124+ return NULL ;
10951125
10961126 if (self -> writable ) {
10971127 res = _bufferedwriter_flush_unlocked (self , 0 );
@@ -1748,7 +1778,10 @@ bufferedwriter_write(buffered *self, PyObject *args)
17481778 return NULL ;
17491779 }
17501780
1751- ENTER_BUFFERED (self )
1781+ if (!ENTER_BUFFERED (self )) {
1782+ PyBuffer_Release (& buf );
1783+ return NULL ;
1784+ }
17521785
17531786 /* Fast path: the data to write can be fully buffered. */
17541787 if (!VALID_READ_BUFFER (self ) && !VALID_WRITE_BUFFER (self )) {
0 commit comments