[Python-Dev] writelines() not thread-safe
Guido van Rossum
guido@python.org
Thu, 09 Mar 2000 20:13:51 -0500
Christian Tismer just did an exhaustive search for thread unsafe use
of Python operations, and found two weaknesses. One is
posix.listdir(), which I had already found; the other is
file.writelines(). Here's a program that demonstrates the bug;
basically, while writelines is walking down the list, another thread
could truncate the list, causing PyList_GetItem() to fail or a string
object to be deallocated while writelines is using it. On my SOlaris
7 system it typically crashes in the first or second iteration.
It's easy to fix: just don't use release the interpreter lock (get rid
of Py_BEGIN_ALLOW_THREADS c.s.). This would however prevent other
threads from doing any work while this thread may be blocked for I/O.
An alternative solution is to put Py_BEGIN_ALLOW_THREADS and
Py_END_ALLOW_THREADS just around the fwrite() call. This is safe, but
would require a lot of lock operations and would probably slow things
down too much.
Ideas?
--Guido van Rossum (home page: http://www.python.org/~guido/)
import os
import sys
import thread
import random
import time
import tempfile
def good_guy(fp, list):
t0 = time.time()
fp.seek(0)
fp.writelines(list)
t1 = time.time()
print fp.tell(), "bytes written"
return t1-t0
def bad_guy(dt, list):
time.sleep(random.random() * dt)
del list[:]
def main():
infn = "/usr/dict/words"
if sys.argv[1:]:
infn = sys.argv[1]
print "reading %s..." % infn
fp = open(infn)
list = fp.readlines()
fp.close()
print "read %d lines" % len(list)
tfn = tempfile.mktemp()
fp = None
try:
fp = open(tfn, "w")
print "calibrating..."
dt = 0.0
n = 3
for i in range(n):
dt = dt + good_guy(fp, list)
dt = dt / n # average time it took to write the list to disk
print "dt =", round(dt, 3)
i = 0
while 1:
i = i+1
print "test", i
copy = map(lambda x: x[1:], list)
thread.start_new_thread(bad_guy, (dt, copy))
good_guy(fp, copy)
finally:
if fp:
fp.close()
try:
os.unlink(tfn)
except os.error:
pass
main()