From ee70ad1e527e67232c61cf47819a0fc264eca435 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 13 Mar 2000 16:27:06 +0000 Subject: Checking in the new, improve file.writelines() code. This (1) avoids thread unsafety whereby another thread could zap the list while we were using it, and (2) now supports writing arbitrary sequences of strings. --- Objects/fileobject.c | 102 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 24 deletions(-) diff --git a/Objects/fileobject.c b/Objects/fileobject.c index 60c113f..c8b083e 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -890,40 +890,94 @@ file_writelines(f, args) PyFileObject *f; PyObject *args; { - int i, n; +#define CHUNKSIZE 1000 + PyObject *list, *line; + PyObject *result; + int i, j, index, len, nwritten, islist; + if (f->f_fp == NULL) return err_closed(); - if (args == NULL || !PyList_Check(args)) { + if (args == NULL || !PySequence_Check(args)) { PyErr_SetString(PyExc_TypeError, - "writelines() requires list of strings"); + "writelines() requires sequence of strings"); return NULL; } - n = PyList_Size(args); - f->f_softspace = 0; - Py_BEGIN_ALLOW_THREADS - errno = 0; - for (i = 0; i < n; i++) { - PyObject *line = PyList_GetItem(args, i); - int len; - int nwritten; - if (!PyString_Check(line)) { - Py_BLOCK_THREADS - PyErr_SetString(PyExc_TypeError, - "writelines() requires list of strings"); + islist = PyList_Check(args); + + /* Strategy: slurp CHUNKSIZE lines into a private list, + checking that they are all strings, then write that list + without holding the interpreter lock, then come back for more. */ + index = 0; + if (islist) + list = NULL; + else { + list = PyList_New(CHUNKSIZE); + if (list == NULL) return NULL; + } + result = NULL; + + for (;;) { + if (islist) { + Py_XDECREF(list); + list = PyList_GetSlice(args, index, index+CHUNKSIZE); + if (list == NULL) + return NULL; + j = PyList_GET_SIZE(list); } - len = PyString_Size(line); - nwritten = fwrite(PyString_AsString(line), 1, len, f->f_fp); - if (nwritten != len) { - Py_BLOCK_THREADS - PyErr_SetFromErrno(PyExc_IOError); - clearerr(f->f_fp); - return NULL; + else { + for (j = 0; j < CHUNKSIZE; j++) { + line = PySequence_GetItem(args, index+j); + if (line == NULL) { + if (PyErr_ExceptionMatches( + PyExc_IndexError)) + { + PyErr_Clear(); + break; + } + /* Some other error occurred. + XXX We may lose some output. */ + goto error; + } + if (!PyString_Check(line)) { + Py_DECREF(line); + PyErr_SetString(PyExc_TypeError, + "writelines() requires sequences of strings"); + goto error; + } + PyList_SetItem(list, j, line); + } } + if (j == 0) + break; + + Py_BEGIN_ALLOW_THREADS + f->f_softspace = 0; + errno = 0; + for (i = 0; i < j; i++) { + line = PyList_GET_ITEM(list, i); + len = PyString_GET_SIZE(line); + nwritten = fwrite(PyString_AS_STRING(line), + 1, len, f->f_fp); + if (nwritten != len) { + Py_BLOCK_THREADS + PyErr_SetFromErrno(PyExc_IOError); + clearerr(f->f_fp); + goto error; + } + } + Py_END_ALLOW_THREADS + + if (j < CHUNKSIZE) + break; + index += CHUNKSIZE; } - Py_END_ALLOW_THREADS + Py_INCREF(Py_None); - return Py_None; + result = Py_None; + error: + Py_XDECREF(list); + return result; } static PyMethodDef file_methods[] = { -- cgit v0.12