private void DoRandomDPORAddRaces(Stack stack, int lastAccessIndex, int i, TidEntry step) { // Note: Only sends are reversible // so we will only get here if step.OpType is a send // so the if below is redundant. // But I am leaving this here in case we end up with more reversible types. // The following assert will then fail and more thought will be needed. // The if below is probably sufficient though, so the assert can probably just be removed. Contract.Assert(step.OpType == OperationType.Send); // Easy case (non-sends). if (step.OpType != OperationType.Send) { Races.Add(new Race(lastAccessIndex, i)); return; } // Harder case (sends). // In random DPOR, we don't just want the last race; // we want all races. // In random DPOR, we don't join the VCs of sends, // so a send will potentially race with many prior sends to the // same mailbox. // We scan the stack looking for concurrent sends. // We only need to start scanning from the first send to this mailbox. int firstSend = TargetIdToFirstSend[step.TargetId]; Contract.Assert( firstSend > 0, "We should only get here if a send races with a prior send, but it does not."); for (int j = firstSend; j < i; j++) { var entry = GetSelectedTidEntry(stack, j); if (entry.OpType != OperationType.Send || entry.Id == step.Id || HB(entry.Id, j, i)) { continue; } Races.Add(new Race(j, i)); } }
/// <summary> /// Utility method to show the selected thread. /// </summary> /// <param name="contract">IContract</param> public string ShowSelected(IContract contract) { int selectedIndex = TryGetSelected(contract); if (selectedIndex < 0) { return("-"); } TidEntry selected = List[selectedIndex]; int priorSend = selected.OpType == OperationType.Receive ? selected.SendStepIndex : -1; return($"({selected.Id}, {selected.OpType}, {selected.TargetType}, {selected.TargetId}, {priorSend})"); }
private void AddBacktrack(Stack stack, bool doRandomDPOR, int lastAccessIndex, int i, TidEntry step) { var aTidEntries = GetThreadsAt(stack, lastAccessIndex); var a = GetSelectedTidEntry(stack, lastAccessIndex); if (HB(a.Id, lastAccessIndex, i) || !Reversible(stack, lastAccessIndex, i)) { return; } if (doRandomDPOR) { DoRandomDPORAddRaces(stack, lastAccessIndex, i, step); return; } // PSEUDOCODE: Find candidates. // There is a race between `a` and `b`. // Must find first steps after `a` that do not HA `a` // (except maybe b.tid) and do not HA each other. // candidates = {} // if b.tid is enabled before a: // add b.tid to candidates // lookingFor = set of enabled threads before a - a.tid - b.tid. // let vc = [0,0,...] // vc[a.tid] = a; // for k = aIndex+1 to bIndex: // if lookingFor does not contain k.tid: // continue // remove k.tid from lookingFor // doesHaAnother = false // foreach t in tids: // if vc[t] hb k: // doesHaAnother = true // break // vc[k.tid] = k // if !doesHaAnother: // add k.tid to candidates // if lookingFor is empty: // break var candidateThreadIds = new HashSet <int>(); if (aTidEntries.List.Count > step.Id && (aTidEntries.List[step.Id].Enabled || aTidEntries.List[step.Id].OpType == OperationType.Yield)) { candidateThreadIds.Add(step.Id); } var lookingFor = new HashSet <int>(); for (int j = 0; j < aTidEntries.List.Count; ++j) { if (j != a.Id && j != step.Id && (aTidEntries.List[j].Enabled || aTidEntries.List[j].OpType == OperationType.Yield)) { lookingFor.Add(j); } } int[] vc = new int[NumThreads]; vc[a.Id] = lastAccessIndex; if (lookingFor.Count > 0) { for (int k = lastAccessIndex + 1; k < i; ++k) { var kEntry = GetSelectedTidEntry(stack, k); if (!lookingFor.Contains(kEntry.Id)) { continue; } lookingFor.Remove(kEntry.Id); bool doesHaAnother = false; for (int t = 0; t < NumThreads; ++t) { if (vc[t] > 0 && vc[t] <= ForVCGetClock(k, t)) { doesHaAnother = true; break; } } if (!doesHaAnother) { candidateThreadIds.Add(kEntry.Id); } if (lookingFor.Count == 0) { break; } } } // Make sure at least one candidate is found Contract.Assert(candidateThreadIds.Count > 0, "DPOR: There were no candidate backtrack points."); // Is one already backtracked? foreach (var tid in candidateThreadIds) { if (aTidEntries.List[tid].Backtrack) { return; } } // None are backtracked, so we have to pick one. // Try to pick one that is slept first. // Start from thread b.tid: { int sleptThread = step.Id; for (int k = 0; k < NumThreads; ++k) { if (candidateThreadIds.Contains(sleptThread) && aTidEntries.List[sleptThread].Sleep) { aTidEntries.List[sleptThread].Backtrack = true; return; } ++sleptThread; if (sleptThread >= NumThreads) { sleptThread = 0; } } } // None are slept. // Avoid picking threads that are disabled (due to yield hack) // Start from thread b.tid: { int backtrackThread = step.Id; for (int k = 0; k < NumThreads; ++k) { if (candidateThreadIds.Contains(backtrackThread) && aTidEntries.List[backtrackThread].Enabled) { aTidEntries.List[backtrackThread].Backtrack = true; return; } ++backtrackThread; if (backtrackThread >= NumThreads) { backtrackThread = 0; } } } // None are slept and enabled. // Start from thread b.tid: { int backtrackThread = step.Id; for (int k = 0; k < NumThreads; ++k) { if (candidateThreadIds.Contains(backtrackThread)) { aTidEntries.List[backtrackThread].Backtrack = true; return; } ++backtrackThread; if (backtrackThread >= NumThreads) { backtrackThread = 0; } } } Contract.Assert(false, "DPOR: Did not manage to add backtrack point."); }
/// <summary> /// The main entry point to the DPOR algorithm. /// </summary> /// <param name="stack">Should contain a terminal schedule.</param> /// <param name="rand">If non-null, then a randomized DPOR algorithm will be used.</param> public void DoDPOR(Stack stack, IRandomNumberGenerator rand) { UpdateFieldsAndRealocateDatastructuresIfNeeded(stack); // Indexes start at 1. for (int i = 1; i < NumSteps; ++i) { TidEntry step = GetSelectedTidEntry(stack, i); if (ThreadIdToLastOpIndex[step.Id] > 0) { FromVCSetVC(ThreadIdToLastOpIndex[step.Id], i); } else { ClearVC(i); } ForVCSetClockToValue(i, step.Id, i); ThreadIdToLastOpIndex[step.Id] = i; int targetId = step.TargetId; if (step.OpType == OperationType.Create && i + 1 < NumSteps) { targetId = GetThreadsAt(stack, i).List.Count; } if (targetId < 0) { continue; } int lastAccessIndex = 0; switch (step.OpType) { case OperationType.Start: case OperationType.Stop: case OperationType.Create: case OperationType.Join: { lastAccessIndex = TargetIdToLastCreateStartEnd[targetId]; TargetIdToLastCreateStartEnd[targetId] = i; break; } case OperationType.Send: { lastAccessIndex = TargetIdToLastSend[targetId]; TargetIdToLastSend[targetId] = i; if (TargetIdToFirstSend[targetId] == 0) { TargetIdToFirstSend[targetId] = i; } break; } case OperationType.Receive: { lastAccessIndex = step.SendStepIndex; break; } case OperationType.WaitForQuiescence: for (int j = 0; j < ThreadIdToLastOpIndex.Length; j++) { if (j == step.Id || ThreadIdToLastOpIndex[j] == 0) { continue; } ForVCJoinFromVC(i, ThreadIdToLastOpIndex[j]); } // Continue. No backtrack. continue; case OperationType.Yield: // Nothing. break; default: throw new ArgumentOutOfRangeException(); } if (lastAccessIndex > 0) { AddBacktrack(stack, rand != null, lastAccessIndex, i, step); // Random DPOR skips joining for sends. if (!(rand != null && step.OpType == OperationType.Send)) { ForVCJoinFromVC(i, lastAccessIndex); } } } if (rand != null) { DoRandomRaceReverse(stack, rand); } }