Fix a race condition on the CGI script.
* wrap/python/ajax/spot.in: Create all cache files in a temporary directory, and only rename this directory at the end. This way if two processes are processing the same request, they won't attempt to populate the same directory (and only one of the first of two renames will succeed, but that is OK).
This commit is contained in:
parent
547715463a
commit
62914059f7
2 changed files with 37 additions and 10 deletions
10
ChangeLog
10
ChangeLog
|
|
@ -1,3 +1,13 @@
|
||||||
|
2012-02-15 Alexandre Duret-Lutz <adl@lrde.epita.fr>
|
||||||
|
|
||||||
|
Fix a race condition on the CGI script.
|
||||||
|
|
||||||
|
* wrap/python/ajax/spot.in: Create all cache files in a temporary
|
||||||
|
directory, and only rename this directory at the end. This way if
|
||||||
|
two processes are processing the same request, they won't attempt
|
||||||
|
to populate the same directory (and only one of the first of two
|
||||||
|
renames will succeed, but that is OK).
|
||||||
|
|
||||||
2012-01-24 Alexandre Duret-Lutz <adl@lrde.epita.fr>
|
2012-01-24 Alexandre Duret-Lutz <adl@lrde.epita.fr>
|
||||||
|
|
||||||
Fix a segfault reported by Etienne Renault using dve2check.
|
Fix a segfault reported by Etienne Renault using dve2check.
|
||||||
|
|
|
||||||
|
|
@ -37,15 +37,15 @@ qs = os.getenv('QUERY_STRING')
|
||||||
if qs:
|
if qs:
|
||||||
import hashlib
|
import hashlib
|
||||||
# We (optimistically) assume no collision from sha1(qs)
|
# We (optimistically) assume no collision from sha1(qs)
|
||||||
imgprefix = imgdir + '/' + hashlib.sha1(qs).hexdigest()
|
cachedir = imgdir + '/' + hashlib.sha1(qs).hexdigest()
|
||||||
cachename = imgprefix + '/html'
|
cachename = cachedir + '/html'
|
||||||
try:
|
try:
|
||||||
# Is this a request we have already processed?
|
# Is this a request we have already processed?
|
||||||
cache = open(cachename, "r", 0)
|
cache = open(cachename, "r", 0)
|
||||||
print cache.read()
|
print cache.read()
|
||||||
# Touch the directory containing the files we used, so
|
# Touch the directory containing the files we used, so
|
||||||
# it that it survives the browser's cache.
|
# it that it survives the browser's cache.
|
||||||
os.utime(imgprefix, None)
|
os.utime(cachedir, None)
|
||||||
exit(0)
|
exit(0)
|
||||||
except IOError:
|
except IOError:
|
||||||
# We failed to open the file.
|
# We failed to open the file.
|
||||||
|
|
@ -90,6 +90,11 @@ import signal
|
||||||
import time
|
import time
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
|
# We do not output in cachedir directely, in case two
|
||||||
|
# CGI scripts process the same request concurrently.
|
||||||
|
tmpdir = cachedir + '-' + str(os.getpid())
|
||||||
|
cachename = tmpdir + '/html'
|
||||||
|
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
# Reopen stdout without buffering
|
# Reopen stdout without buffering
|
||||||
sys.stdout = os.fdopen(sys.stdout.fileno(), "w", 0)
|
sys.stdout = os.fdopen(sys.stdout.fileno(), "w", 0)
|
||||||
|
|
@ -98,11 +103,9 @@ sys.stdout = os.fdopen(sys.stdout.fileno(), "w", 0)
|
||||||
# even errors from subprocesses get printed).
|
# even errors from subprocesses get printed).
|
||||||
os.dup2(sys.stdout.fileno(), sys.stderr.fileno())
|
os.dup2(sys.stdout.fileno(), sys.stderr.fileno())
|
||||||
|
|
||||||
# Create a cache directory if one does not already exist.
|
# Create the temporary cache directory
|
||||||
try:
|
os.mkdir(tmpdir, 0755)
|
||||||
os.mkdir(imgprefix, 0755)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
# Redirect stdout to the cache file, at a low level
|
# Redirect stdout to the cache file, at a low level
|
||||||
# for similar reason.
|
# for similar reason.
|
||||||
fd = os.open(cachename, os.O_CREAT | os.O_WRONLY, 0644)
|
fd = os.open(cachename, os.O_CREAT | os.O_WRONLY, 0644)
|
||||||
|
|
@ -113,6 +116,20 @@ def finish(kill = False):
|
||||||
os.dup2(sys.stderr.fileno(), sys.stdout.fileno())
|
os.dup2(sys.stderr.fileno(), sys.stdout.fileno())
|
||||||
cache = open(cachename, "r", 0)
|
cache = open(cachename, "r", 0)
|
||||||
print cache.read()
|
print cache.read()
|
||||||
|
|
||||||
|
# Rename tmpdir to its permanent name for caching purpose.
|
||||||
|
# os.rename will fail if cachedir already exist. Since we tested
|
||||||
|
# that initially, it can only happen when two CGI script are
|
||||||
|
# processing the same request concurrently. In that case the
|
||||||
|
# other result is as good as ours, so we just ignore the error.
|
||||||
|
# (We don't bother removing the temporary directory -- it will be
|
||||||
|
# removed by the next cache prune and cannot be created again in
|
||||||
|
# the meantime.)
|
||||||
|
try:
|
||||||
|
os.rename(tmpdir, cachedir)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
if kill:
|
if kill:
|
||||||
os.kill(0, signal.SIGTERM)
|
os.kill(0, signal.SIGTERM)
|
||||||
# Should we prune the cache?
|
# Should we prune the cache?
|
||||||
|
|
@ -187,7 +204,7 @@ def render_dot(basename):
|
||||||
reset_alarm()
|
reset_alarm()
|
||||||
# Create an unused hardlink that point to the output picture
|
# Create an unused hardlink that point to the output picture
|
||||||
# just to remember how many cache entries are sharing it.
|
# just to remember how many cache entries are sharing it.
|
||||||
os.link(outname, imgprefix + "/" + ext)
|
os.link(outname, tmpdir + "/" + ext)
|
||||||
b = cgi.escape(basename)
|
b = cgi.escape(basename)
|
||||||
if svg_output:
|
if svg_output:
|
||||||
print ('<object type="image/svg+xml" data="' + b + '.svg">'
|
print ('<object type="image/svg+xml" data="' + b + '.svg">'
|
||||||
|
|
@ -210,7 +227,7 @@ def render_dot_maybe(dotsrc, dont_run_dot):
|
||||||
dotout.close()
|
dotout.close()
|
||||||
# Create an unused hardlink that points to the output picture
|
# Create an unused hardlink that points to the output picture
|
||||||
# just to remember how many cache entries are sharing it.
|
# just to remember how many cache entries are sharing it.
|
||||||
os.link(dotname, imgprefix + "/txt")
|
os.link(dotname, tmpdir + "/txt")
|
||||||
|
|
||||||
if dont_run_dot:
|
if dont_run_dot:
|
||||||
print ('<p>' + dont_run_dot + ''' to be rendered on-line. However
|
print ('<p>' + dont_run_dot + ''' to be rendered on-line. However
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue