From 2b71e662cb1f30e193291005146d3d1be350e2d3 Mon Sep 17 00:00:00 2001 From: Avery Pennarun Date: Thu, 18 Nov 2010 22:43:39 -0800 Subject: [PATCH] Replace lockfile stuff with fifos instead. Now people waiting for a lock can wait for the fifo to be ready, which means it's instant instead of polled. Very pretty. Probably doesn't work on Windows though. --- redo.py | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/redo.py b/redo.py index 1cf4b54..20b73dd 100755 --- a/redo.py +++ b/redo.py @@ -156,21 +156,31 @@ def build(t): mkdirp('%s/.redo' % vars.BASE) lockname = sname('lock', t) try: - fd = os.open(lockname, os.O_CREAT|os.O_EXCL) + os.mkfifo(lockname, 0600) except OSError, e: if e.errno == errno.EEXIST: log('%s (locked...)\n' % relpath(t, vars.STARTDIR)) os._exit(199) else: raise - os.close(fd) try: try: return _build(t) except BuildError, e: err('%s\n' % e) finally: + fd = None + try: + fd = os.open(lockname, os.O_WRONLY|os.O_NONBLOCK) + except OSError, e: + if e.errno == errno.ENXIO: # no readers open; that's ok + pass + elif e.errno == errno.ENOENT: # 'make clean' might do this + pass + else: + raise unlink(lockname) + if fd != None: os.close(fd) os._exit(1) @@ -194,18 +204,22 @@ def main(): elif pd.rv: err('%s: exit code was %r\n' % (t, pd.rv)) retcode = 1 - while locked: - for t in locked.keys(): - lockname = sname('lock', t) - stampname = sname('stamp', t) - if not os.path.exists(lockname): - relp = relpath(t, vars.STARTDIR) - log('%s (...unlocked!)\n' % relp) - if not os.path.exists(stampname): - err('%s: failed in another thread\n' % relp) - retcode = 2 - del locked[t] - time.sleep(0.2) + for t in locked.keys(): + lockname = sname('lock', t) + stampname = sname('stamp', t) + try: + # open() will finish only when an existing writer does close() + os.close(os.open(lockname, os.O_RDONLY)) + except OSError, e: + if e.errno == errno.ENOENT: + pass # already got unlocked + else: + raise + relp = relpath(t, vars.STARTDIR) + log('%s (...unlocked!)\n' % relp) + if not os.path.exists(stampname): + err('%s: failed in another thread\n' % relp) + retcode = 2 return retcode