/// <summary> /// Utility method to show the selected task. /// </summary> public string ShowSelected() { int selectedIndex = this.TryGetSelected(); if (selectedIndex < 0) { return("-"); } TaskEntry selected = this.List[selectedIndex]; int priorSend = selected.OpType == AsyncOperationType.Receive ? selected.SendStepIndex : -1; return($"({selected.Id}, {selected.OpType}, {selected.OpTarget}, {selected.TargetId}, {priorSend})"); }
private void DoRandomDPORAddRaces(Stack stack, int lastAccessIndex, int i, TaskEntry 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. Debug.Assert(step.OpType == AsyncOperationType.Send, "DPOR: step.OpType != AsyncOperationType.Send"); // Easy case (non-sends). if (step.OpType != AsyncOperationType.Send) { this.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 = this.TargetIdToFirstSend[step.TargetId]; Debug.Assert(firstSend > 0, "DPOR: 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 = GetSelectedTaskEntry(stack, j); if (entry.OpType != AsyncOperationType.Send || entry.Id == step.Id || this.HB(entry.Id, j, i)) { continue; } this.Races.Add(new Race(j, i)); } }
private void AddBacktrack(Stack stack, bool doRandomDPOR, int lastAccessIndex, int i, TaskEntry step) { var aTidEntries = GetTasksAt(stack, lastAccessIndex); var a = GetSelectedTaskEntry(stack, lastAccessIndex); if (this.HB(a.Id, lastAccessIndex, i) || !Reversible(stack, lastAccessIndex, i)) { return; } if (doRandomDPOR) { this.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 tasks 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 candidateTaskIds = new HashSet <int>(); if (aTidEntries.List.Count > step.Id && (aTidEntries.List[step.Id].Enabled || aTidEntries.List[step.Id].OpType == AsyncOperationType.Yield)) { candidateTaskIds.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 == AsyncOperationType.Yield)) { lookingFor.Add(j); } } int[] vc = new int[this.NumTasks]; vc[a.Id] = lastAccessIndex; if (lookingFor.Count > 0) { for (int k = lastAccessIndex + 1; k < i; ++k) { var kEntry = GetSelectedTaskEntry(stack, k); if (!lookingFor.Contains(kEntry.Id)) { continue; } lookingFor.Remove(kEntry.Id); bool doesHaAnother = false; for (int t = 0; t < this.NumTasks; ++t) { if (vc[t] > 0 && vc[t] <= this.ForVCGetClock(k, t)) { doesHaAnother = true; break; } } if (!doesHaAnother) { candidateTaskIds.Add(kEntry.Id); } if (lookingFor.Count == 0) { break; } } } // Make sure at least one candidate is found. Debug.Assert(candidateTaskIds.Count > 0, "DPOR: There were no candidate backtrack points."); // Is one already backtracked? foreach (var tid in candidateTaskIds) { 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 task b.tid: int sleptTask = step.Id; for (int k = 0; k < this.NumTasks; ++k) { if (candidateTaskIds.Contains(sleptTask) && aTidEntries.List[sleptTask].Sleep) { aTidEntries.List[sleptTask].Backtrack = true; return; } ++sleptTask; if (sleptTask >= this.NumTasks) { sleptTask = 0; } } } { // None are slept. // Avoid picking tasks that are disabled (due to yield hack) // Start from task b.tid: int backtrackTask = step.Id; for (int k = 0; k < this.NumTasks; ++k) { if (candidateTaskIds.Contains(backtrackTask) && aTidEntries.List[backtrackTask].Enabled) { aTidEntries.List[backtrackTask].Backtrack = true; return; } ++backtrackTask; if (backtrackTask >= this.NumTasks) { backtrackTask = 0; } } } { // None are slept and enabled. // Start from task b.tid: int backtrackTask = step.Id; for (int k = 0; k < this.NumTasks; ++k) { if (candidateTaskIds.Contains(backtrackTask)) { aTidEntries.List[backtrackTask].Backtrack = true; return; } ++backtrackTask; if (backtrackTask >= this.NumTasks) { backtrackTask = 0; } } } Debug.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) { this.UpdateFieldsAndRealocateDatastructuresIfNeeded(stack); // Indexes start at 1. for (int i = 1; i < this.NumSteps; ++i) { TaskEntry step = GetSelectedTaskEntry(stack, i); if (this.TaskIdToLastOpIndex[step.Id] > 0) { this.FromVCSetVC(this.TaskIdToLastOpIndex[step.Id], i); } else { this.ClearVC(i); } this.ForVCSetClockToValue(i, step.Id, i); this.TaskIdToLastOpIndex[step.Id] = i; int targetId = step.TargetId; if (step.OpType == AsyncOperationType.Create && i + 1 < this.NumSteps) { targetId = GetTasksAt(stack, i).List.Count; } if (targetId < 0) { continue; } int lastAccessIndex = 0; switch (step.OpType) { case AsyncOperationType.Start: case AsyncOperationType.Stop: case AsyncOperationType.Create: case AsyncOperationType.Join: { lastAccessIndex = this.TargetIdToLastCreateStartEnd[targetId]; this.TargetIdToLastCreateStartEnd[targetId] = i; break; } case AsyncOperationType.Send: { lastAccessIndex = this.TargetIdToLastSend[targetId]; this.TargetIdToLastSend[targetId] = i; if (this.TargetIdToFirstSend[targetId] == 0) { this.TargetIdToFirstSend[targetId] = i; } break; } case AsyncOperationType.Receive: { lastAccessIndex = step.SendStepIndex; break; } case AsyncOperationType.WaitForQuiescence: for (int j = 0; j < this.TaskIdToLastOpIndex.Length; j++) { if (j == step.Id || this.TaskIdToLastOpIndex[j] == 0) { continue; } this.ForVCJoinFromVC(i, this.TaskIdToLastOpIndex[j]); } // Continue. No backtrack. continue; case AsyncOperationType.Yield: // Nothing. break; default: throw new ArgumentOutOfRangeException(); } if (lastAccessIndex > 0) { this.AddBacktrack(stack, rand != null, lastAccessIndex, i, step); // Random DPOR skips joining for sends. if (!(rand != null && step.OpType == AsyncOperationType.Send)) { this.ForVCJoinFromVC(i, lastAccessIndex); } } } if (rand != null) { this.DoRandomRaceReverse(stack, rand); } }