Add cyclic dependence detection.
If a depends on b which depends on a, redo would just freeze. Now it aborts with a somewhat helpful error message. [Updated by apenwarr for coding style and to add a test.]
This commit is contained in:
parent
63f9dcb640
commit
7dd63efb37
8 changed files with 32 additions and 1 deletions
|
|
@ -206,6 +206,7 @@ class BuildJob:
|
||||||
os.environ['REDO_PWD'] = state.relpath(newp, vars.STARTDIR)
|
os.environ['REDO_PWD'] = state.relpath(newp, vars.STARTDIR)
|
||||||
os.environ['REDO_TARGET'] = self.basename + self.ext
|
os.environ['REDO_TARGET'] = self.basename + self.ext
|
||||||
os.environ['REDO_DEPTH'] = vars.DEPTH + ' '
|
os.environ['REDO_DEPTH'] = vars.DEPTH + ' '
|
||||||
|
vars.add_lock(str(self.lock.fid))
|
||||||
if dn:
|
if dn:
|
||||||
os.chdir(dn)
|
os.chdir(dn)
|
||||||
os.dup2(self.f.fileno(), 1)
|
os.dup2(self.f.fileno(), 1)
|
||||||
|
|
@ -378,7 +379,13 @@ def main(targets, shouldbuildfunc):
|
||||||
# be released; but we should never run get_token() while
|
# be released; but we should never run get_token() while
|
||||||
# holding a lock, or we could cause deadlocks.
|
# holding a lock, or we could cause deadlocks.
|
||||||
jwack.release_mine()
|
jwack.release_mine()
|
||||||
lock.waitlock()
|
try:
|
||||||
|
lock.waitlock()
|
||||||
|
except state.CyclicDependencyError:
|
||||||
|
err('cyclic dependency while building %s\n' % _nice(t))
|
||||||
|
jwack.get_token(t)
|
||||||
|
retcode[0] = 208
|
||||||
|
return retcode[0]
|
||||||
lock.unlock()
|
lock.unlock()
|
||||||
jwack.get_token(t)
|
jwack.get_token(t)
|
||||||
lock.trylock()
|
lock.trylock()
|
||||||
|
|
|
||||||
6
state.py
6
state.py
|
|
@ -11,6 +11,9 @@ STAMP_DIR='dir' # the stamp of a directory; mtime is unhelpful
|
||||||
STAMP_MISSING='0' # the stamp of a nonexistent file
|
STAMP_MISSING='0' # the stamp of a nonexistent file
|
||||||
|
|
||||||
|
|
||||||
|
class CyclicDependencyError(Exception): pass
|
||||||
|
|
||||||
|
|
||||||
def _connect(dbfile):
|
def _connect(dbfile):
|
||||||
_db = sqlite3.connect(dbfile, timeout=TIMEOUT)
|
_db = sqlite3.connect(dbfile, timeout=TIMEOUT)
|
||||||
_db.execute("pragma synchronous = off")
|
_db.execute("pragma synchronous = off")
|
||||||
|
|
@ -347,6 +350,9 @@ class Lock:
|
||||||
|
|
||||||
def waitlock(self):
|
def waitlock(self):
|
||||||
assert(not self.owned)
|
assert(not self.owned)
|
||||||
|
if str(self.fid) in vars.get_locks():
|
||||||
|
# Lock already held by parent: cyclic dependence
|
||||||
|
raise CyclicDependencyError
|
||||||
fcntl.lockf(self.lockfile, fcntl.LOCK_EX, 0, 0)
|
fcntl.lockf(self.lockfile, fcntl.LOCK_EX, 0, 0)
|
||||||
self.owned = True
|
self.owned = True
|
||||||
|
|
||||||
|
|
|
||||||
1
t/355-deps-cyclic/a.do
Normal file
1
t/355-deps-cyclic/a.do
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
redo-ifchange b
|
||||||
2
t/355-deps-cyclic/all.do
Normal file
2
t/355-deps-cyclic/all.do
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
. ../skip-if-minimal-do.sh
|
||||||
|
! redo a >/dev/null 2>&1 || exit 204
|
||||||
1
t/355-deps-cyclic/b.do
Normal file
1
t/355-deps-cyclic/b.do
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
redo-ifchange a
|
||||||
1
t/355-deps-cyclic/clean.do
Normal file
1
t/355-deps-cyclic/clean.do
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
rm -f *~ a b
|
||||||
11
vars.py
11
vars.py
|
|
@ -28,3 +28,14 @@ os.environ['REDO_UNLOCKED'] = '' # not inheritable by subprocesses
|
||||||
|
|
||||||
NO_OOB = os.environ.get('REDO_NO_OOB', '') and 1 or 0
|
NO_OOB = os.environ.get('REDO_NO_OOB', '') and 1 or 0
|
||||||
os.environ['REDO_NO_OOB'] = '' # not inheritable by subprocesses
|
os.environ['REDO_NO_OOB'] = '' # not inheritable by subprocesses
|
||||||
|
|
||||||
|
|
||||||
|
def get_locks():
|
||||||
|
"""Get the list of held locks."""
|
||||||
|
return os.environ.get('REDO_LOCKS', '').split(':')
|
||||||
|
|
||||||
|
def add_lock(name):
|
||||||
|
"""Add a lock to the list of held locks."""
|
||||||
|
locks = set(get_locks())
|
||||||
|
locks.add(name)
|
||||||
|
os.environ['REDO_LOCKS'] = ':'.join(list(locks))
|
||||||
|
|
|
||||||
|
|
@ -34,3 +34,5 @@ def init(targets):
|
||||||
|
|
||||||
import state
|
import state
|
||||||
state.init()
|
state.init()
|
||||||
|
|
||||||
|
os.environ['REDO_LOCKS'] = os.environ.get('REDO_LOCKS', '')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue