/// <summary> /// Returns or forces the next choice to schedule. /// </summary> private bool GetNextHelper(ref IAsyncOperation next, List <IAsyncOperation> ops, IAsyncOperation current) { int currentSchedulableId = (int)current.SourceId; // "Yield" and "Waiting for quiescence" hack. if (ops.TrueForAll(op => !op.IsEnabled)) { if (ops.Exists(op => op.Type is AsyncOperationType.Yield)) { foreach (var op in ops) { if (op.Type is AsyncOperationType.Yield) { op.IsEnabled = true; } } } else if (ops.Exists(op => op.Type is AsyncOperationType.WaitForQuiescence)) { foreach (var op in ops) { if (op.Type is AsyncOperationType.WaitForQuiescence) { op.IsEnabled = true; } } } } // Forced choice. if (next != null) { this.AbdandonReplay(false); } bool added = this.Stack.Push(ops); TaskEntryList top = this.Stack.GetTop(); Debug.Assert(next is null || added, "DPOR: Forced choice implies we should have added to stack."); if (added) { if (this.UseSleepSets) { SleepSets.UpdateSleepSets(this.Stack); } if (this.Dpor is null) { top.SetAllEnabledToBeBacktracked(); } else if (this.Dpor.RaceReplaySuffix.Count > 0 && this.Dpor.ReplayRaceIndex < this.Dpor.RaceReplaySuffix.Count) { // Replaying a race: var tidReplay = this.Dpor.RaceReplaySuffix[this.Dpor.ReplayRaceIndex]; // Restore the nondet choices on the top of stack. top.NondetChoices = tidReplay.NondetChoices; // Add the replay tid to the backtrack set. top.List[tidReplay.Id].Backtrack = true; Debug.Assert(top.List[tidReplay.Id].Enabled || top.List[tidReplay.Id].OpType is AsyncOperationType.Yield, "Failed."); ++this.Dpor.ReplayRaceIndex; } else { // TODO: Here is where we can combine with another scheduler: // For now, we just do round-robin when doing DPOR and random when doing random DPOR. // If our choice is forced by parent scheduler: if (next != null) { top.AddToBacktrack((int)next.SourceId); } else if (this.Rand is null) { top.AddFirstEnabledNotSleptToBacktrack(currentSchedulableId); } else { top.AddRandomEnabledNotSleptToBacktrack(this.Rand); } } } else if (this.Rand != null) { // When doing random DPOR: we are replaying a schedule prefix so rewind the nondet choices now. top.RewindNondetChoicesForReplay(); } int nextTid = this.Stack.GetSelectedOrFirstBacktrackNotSlept(currentSchedulableId); if (nextTid < 0) { next = null; // TODO: if nextTidIndex == DPORAlgorithm.SLEEP_SET_BLOCKED then let caller know that this is the case. // I.e. this is not deadlock. return(false); } if (top.TryGetSelected() != nextTid) { top.SetSelected(nextTid); } Debug.Assert(nextTid < ops.Count, "nextTid >= choices.Count"); next = ops[nextTid]; // TODO: Part of yield hack. if (!next.IsEnabled && next.Type is AsyncOperationType.Yield) { // Uncomment to avoid waking a yielding task. // next = null; // TODO: let caller know that this is not deadlock. // return false; next.IsEnabled = true; } Debug.Assert(next.IsEnabled, "Not enabled."); return(true); }
/// <summary> /// Returns or forces the next choice to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> private bool GetNextHelper( ref ISchedulable next, List <ISchedulable> choices, ISchedulable current) { int currentSchedulableId = (int)current.Id; // "Yield" and "Waiting for quiescence" hack. if (choices.TrueForAll(info => !info.IsEnabled)) { if (choices.Exists(info => info.NextOperationType == OperationType.Yield)) { foreach (var actorInfo in choices) { if (actorInfo.NextOperationType == OperationType.Yield) { actorInfo.IsEnabled = true; } } } else if (choices.Exists( info => info.NextOperationType == OperationType.WaitForQuiescence)) { foreach (var actorInfo in choices) { if (actorInfo.NextOperationType == OperationType.WaitForQuiescence) { actorInfo.IsEnabled = true; } } } } // Forced choice. if (next != null) { AbdandonReplay(false); } bool added = Stack.Push(choices, currentSchedulableId); TidEntryList top = Stack.GetTop(); Contract.Assert(next == null || added, "DPOR: Forced choice implies we should have added to stack."); if (added) { if (UseSleepSets) { SleepSets.UpdateSleepSets(Stack, Contract); } if (Dpor == null) { top.SetAllEnabledToBeBacktracked(Contract); } else if (Dpor.RaceReplaySuffix.Count > 0 && Dpor.ReplayRaceIndex < Dpor.RaceReplaySuffix.Count) { // Replaying a race: var tidReplay = Dpor.RaceReplaySuffix[Dpor.ReplayRaceIndex]; // Restore the nondet choices on the top of stack. top.NondetChoices = tidReplay.NondetChoices; // Add the replay tid to the backtrack set. top.List[tidReplay.Id].Backtrack = true; Contract.Assert( top.List[tidReplay.Id].Enabled || top.List[tidReplay.Id].OpType == OperationType.Yield); ++Dpor.ReplayRaceIndex; } else { // TODO: Here is where we can combine with another scheduler: // For now, we just do round-robin when doing DPOR and random when doing random DPOR. // If our choice is forced by parent scheduler: if (next != null) { top.AddToBacktrack((int)next.Id, Contract); } else if (Rand == null) { top.AddFirstEnabledNotSleptToBacktrack(currentSchedulableId, Contract); } else { top.AddRandomEnabledNotSleptToBacktrack(Rand); } } } else if (Rand != null) { // When doing random DPOR: we are replaying a schedule prefix so rewind the nondet choices now. top.RewindNondetChoicesForReplay(); } int nextTid = Stack.GetSelectedOrFirstBacktrackNotSlept(currentSchedulableId); if (nextTid < 0) { next = null; // TODO: if nextTidIndex == DPORAlgorithm.SLEEP_SET_BLOCKED then let caller know that this is the case. // I.e. this is not deadlock. return(false); } if (top.TryGetSelected(Contract) != nextTid) { top.SetSelected(nextTid, Contract); } Contract.Assert(nextTid < choices.Count); next = choices[nextTid]; // TODO: Part of yield hack. if (!next.IsEnabled && next.NextOperationType == OperationType.Yield) { // // Uncomment to avoid waking a yielding thread. // next = null; // // TODO: let caller know that this is not deadlock. // return false; next.IsEnabled = true; } Contract.Assert(next.IsEnabled); return(true); }