[Python-checkins] r68048 - in sandbox/trunk/io-c: _bufferedio.c _iomodule.h io.c

antoine.pitrou python-checkins at python.org
Tue Dec 30 00:27:20 CET 2008


Author: antoine.pitrou
Date: Tue Dec 30 00:27:19 2008
New Revision: 68048

Log:
Many changes:
- rewrite BufferedWriter with a fixed-size buffer (*).
  This breaks two tests which are based on implementation details of the
  previous version. The tests should be rewritten.
- add some interned strings for method calls.
- fix testThreads for BufferedReader.
- various other things.

(*) benchmarks:

- before:

** Streaming binary output **

[ 20KB.bin] write one byte/char at a time...          0.398 MB/s
[400KB.bin] write 20 bytes/chars at a time...          7.86 MB/s
[400KB.bin] write 4096 bytes/chars at a time...         594 MB/s
[ 10MB.bin] write 1e6 bytes/chars at a time...          400 MB/s

** Binary overwrite **

[ 20KB.bin] modify one byte/char at a time...         0.417 MB/s
[400KB.bin] modify 20 bytes/chars at a time...         7.91 MB/s
[400KB.bin] modify 4096 bytes/chars at a time...        342 MB/s
[400KB.bin] modify one byte/char every two...         0.127 MB/s
[400KB.bin] modify 1000 bytes/chars every 2000...       104 MB/s

- after:

** Streaming binary output **

[ 20KB.bin] write one byte/char at a time...          0.608 MB/s
[400KB.bin] write 20 bytes/chars at a time...          11.3 MB/s
[400KB.bin] write 4096 bytes/chars at a time...         740 MB/s
[ 10MB.bin] write 1e6 bytes/chars at a time...         1580 MB/s

** Binary overwrite **

[ 20KB.bin] modify one byte/char at a time...           0.6 MB/s
[400KB.bin] modify 20 bytes/chars at a time...         10.9 MB/s
[400KB.bin] modify 4096 bytes/chars at a time...        317 MB/s
[400KB.bin] modify one byte/char every two...         0.161 MB/s
[400KB.bin] modify 1000 bytes/chars every 2000...       132 MB/s





Modified:
   sandbox/trunk/io-c/_bufferedio.c
   sandbox/trunk/io-c/_iomodule.h
   sandbox/trunk/io-c/io.c

Modified: sandbox/trunk/io-c/_bufferedio.c
==============================================================================
--- sandbox/trunk/io-c/_bufferedio.c	(original)
+++ sandbox/trunk/io-c/_bufferedio.c	Tue Dec 30 00:27:19 2008
@@ -112,7 +112,12 @@
     Py_ssize_t read_pos;
     PyThread_type_lock read_lock;
 
-    PyObject *write_buf;
+    /* A static buffer of size `buffer_size` */
+    unsigned char *write_buf;
+    /* Just after the last byte actually written */
+    Py_ssize_t write_pos;
+    /* Just after the last buffered byte */
+    Py_ssize_t write_end;
     PyThread_type_lock write_lock;
 
     Py_ssize_t buffer_size;
@@ -122,6 +127,25 @@
     PyObject *weakreflist;
 } BufferedObject;
 
+/* These macros protect the BufferedObject against concurrent operations. */
+
+#define ENTER_BUFFERED_READER(self) \
+    Py_BEGIN_ALLOW_THREADS \
+    PyThread_acquire_lock(self->read_lock, 1); \
+    Py_END_ALLOW_THREADS \
+
+#define LEAVE_BUFFERED_READER(self) \
+    PyThread_release_lock(self->read_lock);
+
+#define ENTER_BUFFERED_WRITER(self) \
+    Py_BEGIN_ALLOW_THREADS \
+    PyThread_acquire_lock(self->write_lock, 1); \
+    Py_END_ALLOW_THREADS \
+
+#define LEAVE_BUFFERED_WRITER(self) \
+    PyThread_release_lock(self->write_lock);
+
+
 static void
 BufferedObject_dealloc(BufferedObject *self)
 {
@@ -129,7 +153,18 @@
         PyObject_ClearWeakRefs((PyObject *)self);
     Py_CLEAR(self->raw);
     Py_CLEAR(self->read_buf);
-    Py_CLEAR(self->write_buf);
+    if (self->write_buf) {
+        PyMem_Free(self->write_buf);
+        self->write_buf = NULL;
+    }
+    if (self->read_lock) {
+        PyThread_free_lock(self->read_lock);
+        self->read_lock = NULL;
+    }
+    if (self->write_lock) {
+        PyThread_free_lock(self->write_lock);
+        self->read_lock = NULL;
+    }
     Py_CLEAR(self->dict);
 }
 
@@ -156,7 +191,7 @@
      * and a flush may be necessary to synch both views of the current
      *  file state.
      */
-    res = PyObject_CallMethod(self->raw, "flush", NULL);
+    res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_flush, NULL);
     if (res == NULL)
         return NULL;
     Py_DECREF(res);
@@ -180,16 +215,16 @@
 static PyObject *
 BufferedIOMixin_flush(BufferedObject *self, PyObject *args)
 {
-    return PyObject_CallMethod(self->raw, "flush", NULL);
+    return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_flush, NULL);
 }
 
 static int
 BufferedIOMixin_closed(BufferedObject *self)
 {
     int closed;
-    PyObject *res = PyObject_GetAttrString(self->raw, "closed");
+    PyObject *res = PyObject_GetAttr(self->raw, _PyIO_str_closed);
     if (res == NULL)
-        return 0;
+        return -1;
     closed = PyObject_IsTrue(res);
     Py_DECREF(res);
     return closed;
@@ -209,7 +244,7 @@
     if (BufferedIOMixin_closed(self))
         Py_RETURN_NONE;
 
-    res = PyObject_CallMethod((PyObject *)self, "flush", NULL);
+    res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL);
     if (res == NULL) {
         /* If flush() fails, just give up */
         if (PyErr_ExceptionMatches(PyExc_IOError))
@@ -275,7 +310,7 @@
 PyDoc_STRVAR(BufferedReader_doc,
              "Create a new buffered reader using the given readable raw IO object.");
 
-static int _BufferedReader_reset_read_buf(BufferedObject *self)
+static int _BufferedReader_reset_buf(BufferedObject *self)
 {
     PyObject *oldbuf = self->read_buf;
 
@@ -311,7 +346,7 @@
     self->buffer_size = buffer_size;
     self->read_buf = NULL;
 
-    if( _BufferedReader_reset_read_buf(self) < 0)
+    if (_BufferedReader_reset_buf(self) < 0)
         return -1;
 
     self->read_lock = PyThread_allocate_lock();
@@ -338,7 +373,7 @@
             return NULL;
 
                 Py_INCREF(buf);
-        if (_BufferedReader_reset_read_buf(self) < 0)
+        if (_BufferedReader_reset_buf(self) < 0)
             return NULL;
 
         /* Strip the consumed bytes */
@@ -422,8 +457,10 @@
 
     data = NULL;
 
-    if (current_size)
+    if (current_size) {
         data = PyBytes_FromStringAndSize(PyBytes_AS_STRING(buf) + pos, current_size);
+        self->read_pos += current_size;
+    }
 
     while (1) {
         Py_ssize_t wanted;
@@ -437,10 +474,10 @@
             Py_DECREF(data);
         }
 
-                if (current_size >= n)
-                        break;
+        if (current_size >= n)
+            break;
 
-                wanted = n;
+        wanted = n;
         if (wanted < self->buffer_size)
             wanted = self->buffer_size;
 
@@ -455,33 +492,27 @@
 
         if (data == Py_None || Py_SIZE(data) == 0) {
             /* EOF occurred or read() would block. */
-
             if (current_size == 0) {
                 Py_DECREF(chunks);
-
-                if( _BufferedReader_reset_read_buf(self) < 0) {
+                if (_BufferedReader_reset_buf(self) < 0) {
                     Py_DECREF(data);
                     return NULL;
                 }
-
                 return data;
             }
             else {
-                            Py_DECREF(data);
+                Py_DECREF(data);
                 break;
             }
         }
-
-                current_size += Py_SIZE(data);
+        current_size += Py_SIZE(data);
     }
 
     sep = PyBytes_FromStringAndSize(NULL, 0);
-
     if (sep == NULL) {
         Py_DECREF(chunks);
         return NULL;
     }
-
     res =_PyBytes_Join(sep, chunks);
     Py_DECREF(sep);
     Py_DECREF(chunks);
@@ -526,13 +557,11 @@
         return NULL;
     }
 
-    Py_BEGIN_ALLOW_THREADS
-    PyThread_acquire_lock(self->read_lock, 1);
-    Py_END_ALLOW_THREADS
+    ENTER_BUFFERED_READER(self)
 
     res = _BufferedReader_read_unlocked(self, n);
 
-    PyThread_release_lock(self->read_lock);
+    LEAVE_BUFFERED_READER(self)
 
     return res;
 }
@@ -591,13 +620,11 @@
         return NULL;
     }
 
-        Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->read_lock, 1);
-        Py_END_ALLOW_THREADS
+    ENTER_BUFFERED_READER(self)
 
     res = _BufferedReader_peek_unlocked(self, n);
 
-    PyThread_release_lock(self->read_lock);
+    LEAVE_BUFFERED_READER(self)
 
     return res;
 }
@@ -615,9 +642,7 @@
     if (n <= 0)
         return PyBytes_FromStringAndSize(NULL, 0);
 
-        Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->read_lock, 1);
-        Py_END_ALLOW_THREADS
+    ENTER_BUFFERED_READER(self)
 
     res = _BufferedReader_peek_unlocked(self, 1);
     if(res == NULL)
@@ -630,9 +655,8 @@
 
     res = _BufferedReader_read_unlocked(self, n);
 
-  end:
-    PyThread_release_lock(self->read_lock);
-
+end:
+    LEAVE_BUFFERED_READER(self)
     return res;
 }
 
@@ -647,9 +671,7 @@
         return NULL;
     }
 
-    Py_BEGIN_ALLOW_THREADS
-    PyThread_acquire_lock(self->read_lock, 1);
-    Py_END_ALLOW_THREADS
+    ENTER_BUFFERED_READER(self)
 
     if (whence == 1) {
         pos -= Py_SIZE(self->read_buf) - self->read_pos;
@@ -657,13 +679,13 @@
 
     res = PyObject_CallMethod(self->raw, "seek", "ni", pos, whence);
     if (res == NULL)
-        return NULL;
+        goto end;
 
-    if (_BufferedReader_reset_read_buf(self) < 0)
+    if (_BufferedReader_reset_buf(self) < 0)
         Py_CLEAR(res);
 
-    PyThread_release_lock(self->read_lock);
-
+end:
+    LEAVE_BUFFERED_READER(self)
     return res;
 }
 
@@ -776,8 +798,17 @@
     );
 
 static int
+_BufferedWriter_reset_buf(BufferedObject *self)
+{
+    self->write_pos = 0;
+    self->write_end = 0;
+    return 0;
+}
+
+static int
 BufferedWriter_init(BufferedObject *self, PyObject *args, PyObject *kwds)
 {
+    /* TODO: max_buffer_size is unused, remove it */
     char *kwlist[] = {"raw", "buffer_size", "max_buffer_size", NULL};
     Py_ssize_t buffer_size = DEFAULT_BUFFER_SIZE;
     Py_ssize_t max_buffer_size = -1;
@@ -799,8 +830,13 @@
     self->buffer_size = buffer_size;
     self->max_buffer_size = max_buffer_size;
 
-        self->write_buf = PyByteArray_FromStringAndSize(NULL, 0);
-    if (self->write_buf == NULL)
+    Py_CLEAR(self->write_buf);
+    self->write_buf = PyMem_Malloc(self->buffer_size);
+    if (self->write_buf == NULL) {
+        PyErr_NoMemory();
+        return -1;
+    }
+    if (_BufferedWriter_reset_buf(self) < 0)
         return -1;
 
     self->write_lock = PyThread_allocate_lock();
@@ -812,78 +848,91 @@
     return 0;
 }
 
-static PyObject *
-_BufferedWriter_flush_unlocked(BufferedObject *self)
+/* Returns the address of the `written` member if a BlockingIOError was
+   raised, NULL otherwise. The error is always re-raised. */
+static Py_ssize_t *
+_Buffered_check_blocking_error(void)
 {
-    Py_ssize_t written = 0;
+    PyObject *t, *v, *tb;
+    PyBlockingIOErrorObject *err;
 
-    if (BufferedIOMixin_closed(self)) {
-        PyErr_SetString(PyExc_ValueError, "flush of closed file");
+    PyErr_Fetch(&t, &v, &tb);
+    if (v == NULL || !PyErr_GivenExceptionMatches(v, PyExc_BlockingIOError)) {
+        PyErr_Restore(t, v, tb);
         return NULL;
     }
+    err = (PyBlockingIOErrorObject *) v;
+    /* TODO: sanity check (err->written >= 0) */
+    PyErr_Restore(t, v, tb);
+    return &err->written;
+}
 
-    while (Py_SIZE(self->write_buf) > 0) {
-        PyObject *slice, *res;
-        Py_ssize_t w;
-        PyObject *n = PyObject_CallMethod(self->raw, "write", "O", self->write_buf);
-
-        if (n == NULL) {
-            PyObject *type, *value, *traceback;
-            PyErr_Fetch(&type, &value, &traceback);
-
-            if (value == NULL ||
-                !PyErr_GivenExceptionMatches(value, PyExc_BlockingIOError)) {
-                PyErr_Restore(type, value, traceback);
-                return NULL;
-            }
-
-            w = ((PyBlockingIOErrorObject *)value)->written;
-
-            /* del self->write_buf[:w] */
-            slice = _PySlice_FromIndices(0, w);
-            if (slice == NULL)
-                return NULL;
-            res = PyObject_CallMethod(self->write_buf, "__delitem__", "O", slice);
-            Py_DECREF(slice);
-            if (res == NULL)
-                return NULL;
-            Py_DECREF(res);
+static Py_ssize_t
+_BufferedWriter_raw_write(BufferedObject *self, unsigned char *start, Py_ssize_t len)
+{
+    Py_buffer buf;
+    PyObject *memobj, *res;
+    Py_ssize_t n;
+    /* NOTE: the buffer needn't be released as its object is NULL. */
+    if (PyBuffer_FillInfo(&buf, NULL, start, len, 1, PyBUF_CONTIG_RO) == -1)
+        return -1;
+    memobj = PyMemoryView_FromBuffer(&buf);
+    if (memobj == NULL)
+        return -1;
+    res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_write, memobj, NULL);
+    Py_DECREF(memobj);
+    if (res == NULL)
+        return -1;
+    n = PyNumber_AsSsize_t(res, PyExc_ValueError);
+    Py_DECREF(res);
+    /* TODO: sanity check (0 <= n <= len) */
+    return n;
+}
 
-            written += w;
-            ((PyBlockingIOErrorObject *)value)->written = written;
+static PyObject *
+_BufferedWriter_flush_unlocked(BufferedObject *self)
+{
+    Py_ssize_t written = 0, n;
+    PyObject *res = NULL;
 
-            PyErr_Restore(type, value, traceback);
-            return NULL;
+    while (self->write_pos < self->write_end) {
+        /* Export a buffer of write_buf[write_pos:write_end].
+           NOTE: the buffer needn't be released as its object is NULL. */
+        n = _BufferedWriter_raw_write(self,
+            self->write_buf + self->write_pos,
+            self->write_end - self->write_pos);
+        if (n == -1 && PyErr_Occurred()) {
+            Py_ssize_t *w = _Buffered_check_blocking_error();
+            if (w == NULL)
+                goto error;
+            *w += written;
+            self->write_pos += n;
+            written += n;
+            /* Already re-raised */
+            goto error;
         }
-
-        /* del self->write_buf[:w] */
-        w = PyNumber_AsSsize_t(n, PyExc_ValueError);
-        Py_DECREF(n);
-        if (w == -1 && PyErr_Occurred())
-            return NULL;
-
-        slice = _PySlice_FromIndices(0, w);
-        if (slice == NULL)
-            return NULL;
-        res = PyObject_CallMethod(self->write_buf, "__delitem__", "O", slice);
-        Py_DECREF(slice);
-        if (res == NULL)
-            return NULL;
-        Py_DECREF(res);
-
-        written += w;
+        /* TODO: sanity check with a macro */
+        self->write_pos += n;
+        written += n;
     }
+    if (_BufferedWriter_reset_buf(self) < 0)
+        goto error;
 
     Py_RETURN_NONE;
+
+error:
+    Py_XDECREF(res);
+    return NULL;
 }
 
 static PyObject *
 BufferedWriter_write(BufferedObject *self, PyObject *args)
 {
-    PyObject *res;
+    PyObject *res = NULL;
     Py_buffer buf;
-    Py_ssize_t before, written;
+    Py_ssize_t written, avail, remaining, n;
 
+    /* TODO: check everywhere that write_buf is allocated */
     if (!PyArg_ParseTuple(args, "y*:write", &buf)) {
         return NULL;
     }
@@ -894,80 +943,87 @@
         return NULL;
     }
 
-        Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->write_lock, 1);
-        Py_END_ALLOW_THREADS
-
-    /* XXX we can implement some more tricks to try and avoid
-     * partial writes
-     */
-
-    if (Py_SIZE(self->write_buf) > self->buffer_size) {
-        /* We're full, so let's pre-flush the buffer*/
-        res = _BufferedWriter_flush_unlocked(self);
-        if (res == NULL) {
-            /* We can't accept anything else. */
-            PyObject *type, *value, *traceback;
-            PyErr_Fetch(&type, &value, &traceback);
-
-            if (value != NULL &&
-                PyErr_GivenExceptionMatches(value, PyExc_BlockingIOError)) {
-                ((PyBlockingIOErrorObject *)value)->written = 0;
-            }
-
-            PyErr_Restore(type, value, traceback);
-            goto fail;
-        }
+    /* Fast path: the data to write can be fully buffered */
+    avail = self->buffer_size - self->write_end;
+    if (buf.len <= avail) {
+        memcpy(self->write_buf + self->write_end, buf.buf, buf.len);
+        self->write_end += buf.len;
+        written = buf.len;
+        goto end;
     }
 
-    written = buf.len;
+    ENTER_BUFFERED_WRITER(self)
 
-    before = Py_SIZE(self->write_buf);
-    if (PyByteArray_Resize(self->write_buf, before + written) < 0) {
+    /* First write the current buffer */
+    res = _BufferedWriter_flush_unlocked(self);
+    if (res == NULL) {
+        Py_ssize_t *w = _Buffered_check_blocking_error();
+        if (w == NULL)
+            goto error;
+        /* Make some place by shifting the buffer. */
+        memmove(self->write_buf, self->write_buf + self->write_pos,
+                self->write_end - self->write_pos);
+        self->write_end -= self->write_pos;
+        self->write_pos = 0;
+        avail = self->buffer_size - self->write_end;
+        if (buf.len <= avail) {
+            /* Everything can be buffered */
+            PyErr_Clear();
+            memcpy(self->write_buf + self->write_end, buf.buf, buf.len);
+            self->write_pos += buf.len;
+            written = buf.len;
+            goto end;
+        }
+        /* Buffer as much as possible. */
+        memcpy(self->write_buf + self->write_end, buf.buf, avail);
+        self->write_pos += avail;
+        /* Already re-raised */
+        *w = avail;
         res = NULL;
-        goto fail;
+        goto error;
     }
-    memcpy(PyByteArray_AS_STRING(self->write_buf) + before, buf.buf, written);
+    Py_CLEAR(res);
 
-    if (Py_SIZE(self->write_buf) > self->buffer_size) {
-        res = _BufferedWriter_flush_unlocked(self);
-        if (res == NULL) {
-            PyObject *type, *value, *traceback;
-            PyErr_Fetch(&type, &value, &traceback);
-
-            if (value != NULL &&
-                PyErr_GivenExceptionMatches(value, PyExc_BlockingIOError)) {
-
-                Py_ssize_t overage =
-                    Py_SIZE(self->write_buf) - self->max_buffer_size;
-
-                if (overage > 0) {
-                    /* We've hit max_buffer_size. We have to accept a
-                     * partial write and cut back our buffer.
-                     */
-                    if (PyByteArray_Resize(self->write_buf, self->max_buffer_size) < 0)
-                        goto fail;
-                    ((PyBlockingIOErrorObject *)value)->written = overage;
-                }
-                else
-                {
-                    Py_CLEAR(type);
-                    Py_CLEAR(value);
-                    Py_CLEAR(traceback);
-                    goto end;
-                }
+    /* Then write buf itself. At this point write_buf has been emptied. */
+    remaining = buf.len;
+    written = 0;
+    while (remaining > self->buffer_size) {
+        n = _BufferedWriter_raw_write(
+            self, buf.buf + written, buf.len - written);
+        if (n == -1) {
+            Py_ssize_t *w = _Buffered_check_blocking_error();
+            if (w == NULL)
+                goto error;
+            written += *w;
+            remaining -= *w;
+            if (remaining > self->buffer_size) {
+                /* Can't buffer everything, still buffer as much as possible */
+                memcpy(self->write_buf, buf.buf + written, remaining);
+                self->write_end = self->buffer_size;
+                *w += self->buffer_size;
+                /* Already re-raised */
+                goto error;
             }
-
-            PyErr_Restore(type, value, traceback);
-            goto fail;
+            PyErr_Clear();
+            break;
         }
+        written += n;
+        remaining -= n;
     }
+    if (remaining > 0) {
+        memcpy(self->write_buf, buf.buf + written, remaining);
+        written += remaining;
+    }
+    self->write_pos = 0;
+    /* TODO: sanity check (remaining >= 0) */
+    self->write_end = remaining;
 
-  end:
+end:
     res = PyLong_FromSsize_t(written);
 
-  fail:
-    PyThread_release_lock(self->write_lock);
+error:
+    LEAVE_BUFFERED_WRITER(self)
+error_unlocked:
     PyBuffer_Release(&buf);
     return res;
 }
@@ -975,16 +1031,21 @@
 static PyObject *
 BufferedWriter_truncate(BufferedObject *self, PyObject *args)
 {
-    PyObject *pos = Py_None;
+    PyObject *pos = NULL;
     PyObject *res;
 
     if (!PyArg_ParseTuple(args, "|O:truncate", &pos)) {
         return NULL;
     }
+    /* will be parsed again by BufferedIOMixin_truncate */
+    Py_XDECREF(pos);
+
+    if (BufferedIOMixin_closed(self)) {
+        PyErr_SetString(PyExc_ValueError, "truncate of closed file");
+        return NULL;
+    }
 
-    Py_BEGIN_ALLOW_THREADS
-    PyThread_acquire_lock(self->write_lock, 1);
-    Py_END_ALLOW_THREADS
+    ENTER_BUFFERED_WRITER(self)
 
     res = _BufferedWriter_flush_unlocked(self);
     if (res == NULL)
@@ -994,7 +1055,7 @@
     res = BufferedIOMixin_truncate(self, args);
 
   end:
-    PyThread_release_lock(self->write_lock);
+    LEAVE_BUFFERED_WRITER(self)
     return res;
 }
 
@@ -1003,13 +1064,16 @@
 {
     PyObject *res;
 
-        Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->write_lock, 1);
-        Py_END_ALLOW_THREADS
+    if (BufferedIOMixin_closed(self)) {
+        PyErr_SetString(PyExc_ValueError, "flush of closed file");
+        return NULL;
+    }
+
+    ENTER_BUFFERED_WRITER(self)
 
     res = _BufferedWriter_flush_unlocked(self);
 
-    PyThread_release_lock(self->write_lock);
+    LEAVE_BUFFERED_WRITER(self)
 
     return res;
 }
@@ -1020,8 +1084,7 @@
     PyObject *op1, *op2, *res;
 
     op1 = PyObject_CallMethod(self->raw, "tell", NULL);
-
-    op2 = PyLong_FromSsize_t(Py_SIZE(self->write_buf));
+    op2 = PyLong_FromSsize_t(self->write_end - self->write_pos);
     if (op2 == NULL) {
         Py_DECREF(op1);
         return NULL;
@@ -1043,10 +1106,12 @@
     if (!PyArg_ParseTuple(args, "n|i:seek", &pos, &whence)) {
         return NULL;
     }
+    if (BufferedIOMixin_closed(self)) {
+        PyErr_SetString(PyExc_ValueError, "seek of closed file");
+        return NULL;
+    }
 
-        Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->write_lock, 1);
-        Py_END_ALLOW_THREADS
+    ENTER_BUFFERED_WRITER(self)
 
     res = _BufferedWriter_flush_unlocked(self);
     if (res == NULL)
@@ -1055,7 +1120,7 @@
     res = PyObject_CallMethod(self->raw, "seek", "ni", pos, whence);
 
   end:
-    PyThread_release_lock(self->write_lock);
+    LEAVE_BUFFERED_WRITER(self)
     return res;
 }
 
@@ -1396,7 +1461,7 @@
     self->buffer_size = buffer_size;
     self->max_buffer_size = max_buffer_size;
 
-    if( _BufferedReader_reset_read_buf(self) < 0)
+    if( _BufferedReader_reset_buf(self) < 0)
         return -1;
 
     self->read_lock = PyThread_allocate_lock();
@@ -1405,8 +1470,13 @@
         return -1;
     }
 
-        self->write_buf = PyByteArray_FromStringAndSize(NULL, 0);
-    if (self->write_buf == NULL)
+    Py_CLEAR(self->write_buf);
+    self->write_buf = PyMem_Malloc(self->buffer_size);
+    if (self->write_buf == NULL) {
+        PyErr_NoMemory();
+        return -1;
+    }
+    if (_BufferedWriter_reset_buf(self) < 0)
         return -1;
 
     self->write_lock = PyThread_allocate_lock();
@@ -1421,7 +1491,7 @@
 static PyObject *
 BufferedRandom_tell(BufferedObject *self, PyObject *args)
 {
-    if (Py_SIZE(self->write_buf))
+    if (self->write_end > self->write_pos)
         return BufferedWriter_tell(self, args);
     else
         return BufferedReader_tell(self, args);
@@ -1450,14 +1520,13 @@
     if (res == NULL)
         return NULL;
 
-        Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->read_lock, 1);
-        Py_END_ALLOW_THREADS
+    ENTER_BUFFERED_READER(self)
 
-    if( _BufferedReader_reset_read_buf(self) < 0)
+    if (_BufferedReader_reset_buf(self) < 0
+        || _BufferedWriter_reset_buf(self) < 0)
         Py_CLEAR(res);
 
-    PyThread_release_lock(self->read_lock);
+    LEAVE_BUFFERED_READER(self)
     return res;
 }
 
@@ -1551,19 +1620,17 @@
         PyObject *res;
         /* Undo readahead */
 
-        Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->read_lock, 1);
-        Py_END_ALLOW_THREADS
-
+        ENTER_BUFFERED_READER(self)
+    
         res = PyObject_CallMethod(self->raw, "seek", "ni",
                                   self->read_pos - Py_SIZE(self->read_buf), 1);
         Py_XDECREF(res);
         if (res != NULL) {
-            if( _BufferedReader_reset_read_buf(self) < 0)
+            if (_BufferedReader_reset_buf(self) < 0)
                 res = NULL;
         }
 
-        PyThread_release_lock(self->read_lock);
+        LEAVE_BUFFERED_READER(self)
 
         if (res == NULL)
             return NULL;

Modified: sandbox/trunk/io-c/_iomodule.h
==============================================================================
--- sandbox/trunk/io-c/_iomodule.h	(original)
+++ sandbox/trunk/io-c/_iomodule.h	Tue Dec 30 00:27:19 2008
@@ -48,3 +48,9 @@
 } PyBlockingIOErrorObject;
 PyObject *PyExc_BlockingIOError;
 
+/* Implementation details */
+
+extern PyObject *_PyIO_str_closed;
+extern PyObject *_PyIO_str_flush;
+extern PyObject *_PyIO_str_read;
+extern PyObject *_PyIO_str_write;

Modified: sandbox/trunk/io-c/io.c
==============================================================================
--- sandbox/trunk/io-c/io.c	(original)
+++ sandbox/trunk/io-c/io.c	Tue Dec 30 00:27:19 2008
@@ -4,6 +4,13 @@
 
 PyObject *PyIOExc_UnsupportedOperation;
 
+/* Various interned strings */
+
+PyObject *_PyIO_str_closed;
+PyObject *_PyIO_str_flush;
+PyObject *_PyIO_str_read;
+PyObject *_PyIO_str_write;
+
 
 PyDoc_STRVAR(module_doc,
 "The io module provides the Python interfaces to stream handling. The\n"
@@ -599,6 +606,16 @@
     Py_INCREF(&PyIncrementalNewlineDecoder_Type);
     PyModule_AddObject(m, "IncrementalNewlineDecoder", (PyObject *) &PyIncrementalNewlineDecoder_Type);
 
+    /* Interned strings */
+    if (!(_PyIO_str_closed = PyUnicode_InternFromString("closed")))
+        goto fail;
+    if (!(_PyIO_str_flush = PyUnicode_InternFromString("flush")))
+        goto fail;
+    if (!(_PyIO_str_read = PyUnicode_InternFromString("read")))
+        goto fail;
+    if (!(_PyIO_str_write = PyUnicode_InternFromString("write")))
+        goto fail;
+
     return m;
 
   fail:


More information about the Python-checkins mailing list