package kodkod.examples.alloy; import java.util.ArrayList; import java.util.List; import kodkod.ast.Decls; import kodkod.ast.Expression; import kodkod.ast.Formula; import kodkod.ast.Relation; import kodkod.ast.Variable; import kodkod.engine.Solution; import kodkod.engine.Solver; import kodkod.engine.satlab.SATFactory; import kodkod.instance.Bounds; import kodkod.instance.TupleFactory; import kodkod.instance.TupleSet; import kodkod.instance.Universe; /** * Kodkod encoding of models/examples/algorithms/dijkstra.als. * @author Emina Torlak */ public final class Dijkstra { private final Relation Process, Mutex, State, holds, waits; private final Relation sfirst, slast, sord, mfirst, mlast, mord; /** * Creates an instance of Dijkstra example. */ public Dijkstra() { Process = Relation.unary("Process"); Mutex = Relation.unary("Mutex"); State = Relation.unary("State"); holds = Relation.ternary("holds"); waits = Relation.ternary("waits"); sfirst = Relation.unary("sfirst"); slast = Relation.unary("slast"); sord = Relation.binary("sord"); mfirst = Relation.unary("mfirst"); mlast = Relation.unary("mlast"); mord = Relation.binary("mord"); } /** * Returns the declaration constraints. * @return * <pre> * sig Process {} * sig Mutex {} * sig State { holds, waits: Process -> Mutex } * </pre> */ public Formula declarations() { final Formula f1 = sord.totalOrder(State, sfirst, slast); final Formula f2 = mord.totalOrder(Mutex, mfirst, mlast); final Formula f3 = holds.in(State.product(Process).product(Mutex)); final Formula f4 = waits.in(State.product(Process).product(Mutex)); return Formula.and(f1, f2, f3, f4); } /** * Returns the initial predicate for state s. * @return * <pre> * pred State.Initial () { no this.holds + this.waits } * </pre> */ public Formula initial(Expression s) { return s.join(holds).union(s.join(waits)).no(); } /** * Returns the free predicate for state s and mutex m. * @return * <pre> * pred State.IsFree (m: Mutex) { * // no process holds this mutex * no m.~(this.holds) * } * </pre> */ public Formula isFree(Expression s, Expression m) { return m.join((s.join(holds)).transpose()).no(); } /** * Returns the isStalled predicate for state s and process p. * @return * <pre> * pred State.IsStalled (p: Process) { some p.(this.waits) } * </pre> */ public Formula isStalled(Expression s, Expression p) { return p.join(s.join(waits)).some(); } /** * Returns the GrabMutex predicate for states s1, s2, process p and mutex m. * @return * <pre> * pred State.GrabMutex (p: Process, m: Mutex, s': State) { * // a process can only act if it is not * // waiting for a mutex * !this::IsStalled(p) * // can only grab a mutex we do not yet hold * m !in p.(this.holds) * this::IsFree (m) => { * // if the mutex is free, we now hold it, * // and do not become stalled * p.(s'.holds) = p.(this.holds) + m * no p.(s'.waits) * } else { * // if the mutex was not free, * // we still hold the same mutexes we held, * // and are now waiting on the mutex * // that we tried to grab. * p.(s'.holds) = p.(this.holds) * p.(s'.waits) = m * } * all otherProc: Process - p | { * otherProc.(s'.holds) = otherProc.(this.holds) * otherProc.(s'.waits) = otherProc.(this.waits) * } * } * </pre> */ public Formula grabMutex(Expression s1, Expression s2, Expression p, Expression m) { final Formula f1 = isStalled(s1,p).not().and(m.in(p.join(s1.join(holds))).not()); final Formula isFree = isFree(s1,m); final Formula f2 = p.join(s2.join(holds)).eq(p.join(s1.join(holds)).union(m)); final Formula f3 = p.join(s2.join(waits)).no(); final Formula f4 = isFree.implies(f2.and(f3)); final Formula f5 = p.join(s2.join(holds)).eq(p.join(s1.join(holds))); final Formula f6 = p.join(s2.join(waits)).eq(m); final Formula f7 = isFree.not().implies(f5.and(f6)); final Variable otherProc = Variable.unary("otherProc"); final Formula f8 = otherProc.join(s2.join(holds)).eq(otherProc.join(s1.join(holds))); final Formula f9 = otherProc.join(s2.join(waits)).eq(otherProc.join(s1.join(waits))); final Formula f10 = f8.and(f9).forAll(otherProc.oneOf(Process.difference(p))); return Formula.and(f1, f4, f7, f10); } /** * Returns the GrabMutex predicate for states s1, s2, process p and mutex m. * @return * <pre> * pred State.ReleaseMutex (p: Process, m: Mutex, s': State) { * !this::IsStalled(p) * m in p.(this.holds) * p.(s'.holds) = p.(this.holds) - m * no p.(s'.waits) * no m.~(this.waits) => { * no m.~(s'.holds) * no m.~(s'.waits) * } else { * some lucky: m.~(this.waits) | { * m.~(s'.waits) = m.~(this.waits) - lucky * m.~(s'.holds) = lucky * } * } * all mu: Mutex - m { * mu.~(s'.waits) = mu.~(this.waits) * mu.~(s'.holds)= mu.~(this.holds) * } * } * </pre> */ public Formula releaseMutex(Expression s1, Expression s2, Expression p, Expression m) { final Formula f1 = isStalled(s1,p).not().and(m.in(p.join(s1.join(holds)))); final Formula f2 = p.join(s2.join(holds)).eq(p.join(s1.join(holds)).difference(m)); final Formula f3 = p.join(s2.join(waits)).no(); final Expression cexpr = m.join((s1.join(waits)).transpose()); final Formula f4 = m.join(s2.join(holds).transpose()).no(); final Formula f5 = m.join(s2.join(waits).transpose()).no(); final Formula f6 = cexpr.no().implies(f4.and(f5)); final Variable lucky = Variable.unary("lucky"); final Formula f7 = m.join(s2.join(waits).transpose()).eq(m.join(s1.join(waits).transpose()).difference(lucky)); final Formula f8 = m.join(s2.join(holds).transpose()).eq(lucky); final Formula f9 = f7.and(f8).forSome(lucky.oneOf(m.join(s1.join(waits).transpose()))); final Formula f10 = cexpr.some().implies(f9); final Variable mu = Variable.unary("mu"); final Formula f11 = mu.join(s2.join(waits).transpose()).eq(mu.join(s1.join(waits).transpose())); final Formula f12 = mu.join(s2.join(holds).transpose()).eq(mu.join(s1.join(holds).transpose())); final Formula f13 = f11.and(f12).forAll(mu.oneOf(Mutex.difference(m))); return Formula.and(f1, f2, f3, f6, f10, f13); } /** * Returns the GrabOrRelease predicate. * @return * <pre> * pred GrabOrRelease () { * Initial(so/first()) && * ( * all pre: State - so/last () | let post = so/next (pre) | * (post.holds = pre.holds && post.waits = pre.waits) * || * (some p: Process, m: Mutex | pre::GrabMutex (p, m, post)) * || * (some p: Process, m: Mutex | pre::ReleaseMutex (p, m, post)) * * ) * } * </pre> */ public Formula grabOrRelease() { final Variable pre = Variable.unary("pre"); final Expression post = pre.join(sord); final Formula f1 = post.join(holds).eq(pre.join(holds)); final Formula f2 = post.join(waits).eq(pre.join(waits)); final Variable p = Variable.unary("p"); final Variable m = Variable.unary("m"); final Decls d = p.oneOf(Process).and(m.oneOf(Mutex)); final Formula f3 = grabMutex(pre, post, p, m).forSome(d); final Formula f4 = releaseMutex(pre, post, p, m).forSome(d); return initial(sfirst).and(((f1.and(f2)).or(f3).or(f4)).forAll(pre.oneOf(State.difference(slast)))); } /** * Returns the Deadlock predicate. * @return * <pre> * pred Deadlock () { * some s: State | all p: Process | some p.(s.waits) * } * </pre> */ public Formula deadlock() { final Variable s = Variable.unary("s"); final Variable p = Variable.unary("p"); return p.join(s.join(waits)).some().forAll(p.oneOf(Process)).forSome(s.oneOf(State)); } /** * Returns the GrabbedInOrder predicate. * @return * <pre> * pred GrabbedInOrder ( ) { * all pre: State - so/last() | * let post = so/next(pre) | * let had = Process.(pre.holds), have = Process.(post.holds) | * let grabbed = have - had | * some grabbed => grabbed in mo/nexts(had) * } * </pre> */ public Formula grabbedInOrder() { final Variable pre = Variable.unary("pre"); final Expression post = pre.join(sord); final Expression had = Process.join(pre.join(holds)); final Expression have = Process.join(post.join(holds)); final Expression grabbed = have.difference(had); return grabbed.some().implies(grabbed.in(had.join(mord.closure()))).forAll(pre.oneOf(State.difference(slast))); } /** * Returns the DijkstraPreventsDeadlocks assertion. * @return * <pre> * assert DijkstraPreventsDeadlocks { * some Process && GrabOrRelease() && GrabbedInOrder() => ! Deadlock() * } * </pre> */ public Formula checkDijkstraPreventsDeadlocks() { return Formula.and(declarations(), Process.some(), grabOrRelease(), grabbedInOrder(), deadlock()); } /** * Returns the showDijkstra predicate. * @return he showDijkstra predicate */ public Formula showDijkstra() { return declarations().and(grabOrRelease()).and(deadlock()).and(waits.some()); } /** * Returns the bounds that allocate the given number of atoms to each type. * @return bounds */ public Bounds bounds(int scope) { return bounds(scope, scope, scope); } /** * Returns the bounds corresponding to the given scopes. * @return bounds */ public Bounds bounds(int states, int processes, int mutexes) { final List<String> atoms = new ArrayList<String>(states + processes + mutexes); for(int i = 0; i < states; i++) { atoms.add("State"+i); } for(int i = 0; i < processes; i++) { atoms.add("Process"+i); } for(int i = 0; i < mutexes; i++) { atoms.add("Mutex"+i); } final Universe u = new Universe(atoms); final TupleFactory f = u.factory(); final Bounds b = new Bounds(u); final TupleSet sb = f.range(f.tuple("State0"), f.tuple("State"+(states-1))); final TupleSet pb = f.range(f.tuple("Process0"), f.tuple("Process"+(processes-1))); final TupleSet mb = f.range(f.tuple("Mutex0"), f.tuple("Mutex"+(mutexes-1))); b.bound(State, sb); b.bound(holds, sb.product(pb).product(mb)); b.bound(waits, sb.product(pb).product(mb)); b.bound(sfirst, sb); b.bound(slast, sb); b.bound(sord, sb.product(sb)); b.bound(Process, pb); b.bound(Mutex, mb); b.bound(mfirst, mb); b.bound(mlast, mb); b.bound(mord, mb.product(mb)); return b; } private static void usage() { System.out.println("Usage: java examples.Dijkstra [# states] [# processes] [# mutexes]"); System.exit(1); } /** * Usage: java examples.Dijkstra [# states] [# processes] [# mutexes] */ public static void main(String[] args) { if (args.length < 3) usage(); final Dijkstra model = new Dijkstra(); final Solver solver = new Solver(); solver.options().setSolver(SATFactory.MiniSat); try { final Formula noDeadlocks = model.checkDijkstraPreventsDeadlocks(); final int states = Integer.parseInt(args[0]); final int processes = Integer.parseInt(args[1]); final int mutexes = Integer.parseInt(args[2]); final Bounds bounds = model.bounds(states, processes, mutexes); System.out.println("*****check DijkstraPreventsDeadlocks for " + states + " State, " + processes + " Process, " + mutexes + " Mutex*****"); System.out.println(noDeadlocks); // System.out.println(bounds); Solution sol1 = solver.solve(noDeadlocks, bounds); System.out.println(sol1); // System.out.println(solver.solve(model.grabOrRelease().and(model.declarations()). // and(model.waits.some()).and(model.deadlock()), bounds)); } catch (NumberFormatException nfe) { usage(); } } }