/// <summary> /// Notify that a new task has been created for the given machine. /// </summary> /// <param name="id">TaskId</param> /// <param name="machine">Machine</param> internal virtual void NotifyNewTaskCreated(int id, AbstractMachine machine) { var machineInfo = new MachineInfo(id, machine); IO.Debug($"<ScheduleDebug> Created task '{machineInfo.Id}' for machine " + $"'{machineInfo.Machine.Id}'."); if (this.TaskMap.Count == 0) { machineInfo.IsActive = true; } this.TaskMap.TryAdd(id, machineInfo); }
/// <summary> /// Returns the prioritized machine. /// </summary> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>MachineInfo</returns> private MachineInfo GetPrioritizedMachine(List <MachineInfo> choices, MachineInfo current) { if (this.PrioritizedMachines.Count == 0) { this.PrioritizedMachines.Add(current.Machine.Id); } foreach (var mi in choices.Where(mi => !this.PrioritizedMachines.Contains(mi.Machine.Id))) { var mIndex = this.Random.Next(this.PrioritizedMachines.Count) + 1; this.PrioritizedMachines.Insert(mIndex, mi.Machine.Id); Debug.WriteLine($"<PCTLog> Detected new machine '{mi.Machine.Id}' at index '{mIndex}'."); } if (this.PriorityChangePoints.Contains(this.ExploredSteps)) { if (choices.Count == 1) { this.MovePriorityChangePointForward(); } else { var priority = this.GetHighestPriorityEnabledMachine(choices); this.PrioritizedMachines.Remove(priority); this.PrioritizedMachines.Add(priority); Debug.WriteLine($"<PCTLog> Machine '{priority}' changes to lowest priority."); } } var prioritizedMachine = this.GetHighestPriorityEnabledMachine(choices); Debug.WriteLine($"<PCTLog> Prioritized machine '{prioritizedMachine}'."); Debug.Write("<PCTLog> Priority list: "); for (int idx = 0; idx < this.PrioritizedMachines.Count; idx++) { if (idx < this.PrioritizedMachines.Count - 1) { Debug.Write($"'{this.PrioritizedMachines[idx]}', "); } else { Debug.Write($"'{this.PrioritizedMachines[idx]}({1})'.\n"); } } var prioritizedMachineInfo = choices.First(mi => mi.Machine.Id.Equals(prioritizedMachine)); return(prioritizedMachineInfo); }
/// <summary> /// Tries to register any new operations. /// </summary> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> private void TryRegisterNewOperations(IEnumerable <MachineInfo> choices, MachineInfo current) { if (this.Operations.Count == 0) { this.Operations.Add(current.Machine.OperationId); } var operationIds = choices.Select(mi => mi.Machine.OperationId).Distinct(); foreach (var id in operationIds.Where(id => !this.Operations.Contains(id))) { var opIndex = this.Random.Next(this.Operations.Count) + 1; this.Operations.Insert(opIndex, id); Debug.WriteLine("<OperationDebug> Detected new operation '{0}' at index '{1}'.", id, opIndex); } }
/// <summary> /// Determines whether the specified System.Object is equal /// to the current System.Object. /// </summary> /// <param name="obj">Object</param> /// <returns>Boolean</returns> public override bool Equals(object obj) { if (obj == null) { return(false); } MachineInfo mid = obj as MachineInfo; if (mid == null) { return(false); } return(this.Id == mid.Id); }
/// <summary> /// Notify that a new task has been created for the given machine. /// </summary> /// <param name="id">TaskId</param> /// <param name="machine">Machine</param> internal override void NotifyNewTaskCreated(int id, AbstractMachine machine) { var machineInfo = new MachineInfo(id, machine); IO.Debug($"<ScheduleDebug> Created task '{machineInfo.Id}' for machine " + $"'{machineInfo.Machine.Id}'."); if (base.TaskMap.Count == 0) { machineInfo.IsActive = true; } base.TaskMap.TryAdd(id, machineInfo); if (machine is TaskMachine) { this.WrappedTaskMap.TryAdd((machine as TaskMachine).WrappedTask.Id, machineInfo); } }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable <MachineInfo> choices, MachineInfo current) { var availableMachines = choices.Where( mi => mi.IsEnabled && !mi.IsBlocked && !mi.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { availableMachines = choices.Where(m => m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { next = null; return(false); } } next = this.GetPrioritizedMachine(availableMachines, current); this.ExploredSteps++; return(true); }
/// <summary> /// Notify that a new task has been created for the given machine. /// </summary> /// <param name="id">TaskId</param> /// <param name="machine">Machine</param> internal override void NotifyNewTaskCreated(int id, AbstractMachine machine) { var machineInfo = new MachineInfo(id, machine); IO.Debug($"<ScheduleDebug> Created task '{machineInfo.Id}' for machine " + $"'{machineInfo.Machine.Id}'."); if (base.MachineInfos.Count == 0) { machineInfo.IsActive = true; } base.MachineInfos.Add(machineInfo); base.TaskMap.TryAdd(id, machineInfo); if (machine is TaskMachine) { this.WrappedTaskMap.TryAdd((machine as TaskMachine).WrappedTask.Id, machineInfo); } }
/// <summary> /// Returns the next operation to schedule. /// </summary> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>OperationId</returns> protected override int GetNextOperation(List <MachineInfo> choices, MachineInfo current) { var operationIds = choices.Select(val => val.Machine.OperationId).Distinct(); if (this.PriorityChangePoints.Contains(this.ExploredSteps)) { if (operationIds.Count() == 1) { this.MovePriorityChangePointForward(); } else { var priority = this.GetHighestPriorityEnabledOperationId(choices); base.Operations.Remove(priority); base.Operations.Add(priority); Debug.WriteLine("<OperationLog> Operation '{0}' changes to lowest priority.", priority); } } return(this.GetHighestPriorityEnabledOperationId(choices)); }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public virtual bool TryGetNext(out MachineInfo next, IEnumerable <MachineInfo> choices, MachineInfo current) { var machines = choices.OrderBy(mi => mi.Machine.Id.Value).ToList(); var currentMachineIdx = machines.IndexOf(current); var orderedMachines = machines.GetRange(currentMachineIdx, machines.Count - currentMachineIdx); if (currentMachineIdx != 0) { orderedMachines.AddRange(machines.GetRange(0, currentMachineIdx)); } var availableMachines = orderedMachines.Where( mi => mi.IsEnabled && !mi.IsBlocked && !mi.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { availableMachines = choices.Where(m => m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { next = null; return(false); } } int idx = 0; while (this.RemainingDelays.Count > 0 && this.ExploredSteps == this.RemainingDelays[0]) { idx = (idx + 1) % availableMachines.Count; this.RemainingDelays.RemoveAt(0); IO.PrintLine("<DelayLog> Inserted delay, '{0}' remaining.", this.RemainingDelays.Count); } next = availableMachines[idx]; this.ExploredSteps++; return(true); }
/// <summary> /// Notify that the task has started. /// </summary> internal override void NotifyTaskStarted() { int?id = Task.CurrentId; if (id == null) { return; } MachineInfo machineInfo = null; if (!(base.TaskMap.TryGetValue((int)id, out machineInfo) || this.WrappedTaskMap.TryGetValue((int)id, out machineInfo))) { IO.Debug($"<ScheduleDebug> Unable to start task '{id}'."); base.Stop(); } IO.Debug($"<ScheduleDebug> Started task '{machineInfo.Id}' of machine " + $"'{machineInfo.Machine.Id}'."); lock (machineInfo) { machineInfo.HasStarted = true; System.Threading.Monitor.PulseAll(machineInfo); while (!machineInfo.IsActive) { IO.Debug($"<ScheduleDebug> Sleep task '{machineInfo.Id}' of machine " + $"'{machineInfo.Machine.Id}'."); System.Threading.Monitor.Wait(machineInfo); IO.Debug($"<ScheduleDebug> Wake up task '{machineInfo.Id}' of machine " + $"'{machineInfo.Machine.Id}'."); } if (!machineInfo.IsEnabled) { throw new OperationCanceledException(); } } }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable <MachineInfo> choices, MachineInfo current) { var availableMachines = choices.Where( m => m.IsEnabled && !m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { availableMachines = choices.Where(m => m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { next = null; return(false); } } if (this.ExploredSteps >= this.ScheduleTrace.Count) { Error.ReportAndExit("Trace is not reproducible: execution is longer than trace."); } ScheduleStep nextStep = this.ScheduleTrace[this.ExploredSteps]; if (nextStep.Type != ScheduleStepType.SchedulingChoice) { Error.ReportAndExit("Trace is not reproducible: next step is not a scheduling choice."); } next = availableMachines.FirstOrDefault(m => m.Machine.Id.Type.Equals( nextStep.ScheduledMachineId.Type) && m.Machine.Id.Value == nextStep.ScheduledMachineId.Value); if (next == null) { Error.ReportAndExit("Trace is not reproducible: cannot detect machine with type " + $"'{nextStep.ScheduledMachineId.Type}' and id '{nextStep.ScheduledMachineId.Value}'."); } this.ExploredSteps++; return(true); }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable <MachineInfo> choices, MachineInfo current) { var availableMachines = choices.Where( m => m.IsEnabled && !m.IsBlocked && !m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { availableMachines = choices.Where(m => m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { next = null; return(false); } } int idx = this.Random.Next(availableMachines.Count); next = availableMachines[idx]; this.ExploredSteps++; return(true); }
/// <summary> /// Returns the prioritized machines. /// </summary> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> private List <MachineInfo> GetPrioritizedMachines(List <MachineInfo> choices, MachineInfo current) { choices = choices.OrderBy(mi => mi.Machine.Id.Value).ToList(); choices = choices.OrderBy(mi => mi.Machine.OperationId).ToList(); MachineInfo priorityMachine = current; var prioritizedMachines = new List <MachineInfo>(); if (current.Machine.OperationId == this.PrioritizedOperationId) { var currentMachineIdx = choices.IndexOf(current); prioritizedMachines = choices.GetRange(currentMachineIdx, choices.Count - currentMachineIdx); if (currentMachineIdx != 0) { prioritizedMachines.AddRange(choices.GetRange(0, currentMachineIdx)); } } else { priorityMachine = choices.First(mi => mi.Machine.OperationId == this.PrioritizedOperationId); var priorityMachineIdx = choices.IndexOf(priorityMachine); prioritizedMachines = choices.GetRange(priorityMachineIdx, choices.Count - priorityMachineIdx); if (priorityMachineIdx != 0) { prioritizedMachines.AddRange(choices.GetRange(0, priorityMachineIdx)); } } prioritizedMachines = prioritizedMachines.Where( mi => mi.IsEnabled && !mi.IsWaitingToReceive).ToList(); if (prioritizedMachines.Count == 0) { return(prioritizedMachines); } return(prioritizedMachines); }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable <MachineInfo> choices, MachineInfo current) { var availableMachines = choices.Where( m => m.IsEnabled && !m.IsBlocked && !m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { availableMachines = choices.Where(m => m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { next = null; return(false); } } SChoice nextChoice = null; List <SChoice> scs = null; if (this.SchIndex < this.ScheduleStack.Count) { scs = this.ScheduleStack[this.SchIndex]; } else { scs = new List <SChoice>(); foreach (var task in availableMachines) { scs.Add(new SChoice(task.Machine.Id.Value)); } this.ScheduleStack.Add(scs); } nextChoice = scs.FirstOrDefault(val => !val.IsDone); if (nextChoice == null) { next = null; return(false); } if (this.SchIndex > 0) { var previousChoice = this.ScheduleStack[this.SchIndex - 1].Last(val => val.IsDone); previousChoice.IsDone = false; } next = availableMachines.Find(task => task.Machine.Id.Value == nextChoice.Id); nextChoice.IsDone = true; this.SchIndex++; if (next == null) { return(false); } this.ExploredSteps++; return(true); }
/// <summary> /// Schedules the next machine to execute. /// </summary> internal override void Schedule() { int?id = Task.CurrentId; if (id == null || id == base.Runtime.RootTaskId) { return; } if (this.BugFound || !base.IsSchedulerRunning) { base.Stop(); } // Checks if the scheduling steps bound has been reached. this.CheckIfSchedulingStepsBoundIsReached(true); foreach (var task in base.TaskMap.Values.Where(val => val.IsBlocked)) { foreach (var userTask in this.UserTasks.Where(val => val.IsCompleted)) { if (task.BlockingUnwrappedTasks.Contains(userTask)) { task.BlockingUnwrappedTasks.Remove(userTask); if (!task.WaitAll) { task.BlockingWrappedTasks.Clear(); task.BlockingUnwrappedTasks.Clear(); task.IsBlocked = false; break; } } } if (task.WaitAll && task.BlockingWrappedTasks.Count == 0 && task.BlockingUnwrappedTasks.Count == 0) { task.WaitAll = false; task.IsBlocked = false; } if (!task.IsBlocked) { IO.Debug($"<ScheduleDebug> Unblocked task '{task.Id}' of machine " + $"'{task.Machine.Id}'."); } } MachineInfo machineInfo = null; if (!(base.TaskMap.TryGetValue((int)id, out machineInfo) || this.WrappedTaskMap.TryGetValue((int)id, out machineInfo))) { IO.Debug($"<ScheduleDebug> Unable to schedule task '{id}'."); base.Stop(); } MachineInfo next = null; if (!this.Strategy.TryGetNext(out next, this.TaskMap.Values, machineInfo)) { IO.Debug("<ScheduleDebug> Schedule explored."); this.HasFullyExploredSchedule = true; base.Stop(); } base.Runtime.ScheduleTrace.AddSchedulingChoice(next.Machine.Id); // Checks the liveness monitors for potential liveness bugs. base.Runtime.LivenessChecker.CheckLivenessAtShedulingStep(); if (base.Runtime.Configuration.CacheProgramState && base.Runtime.Configuration.SafetyPrefixBound <= this.ExploredSteps) { base.Runtime.StateCache.CaptureState(base.Runtime.ScheduleTrace.Peek()); } IO.Debug($"<ScheduleDebug> Schedule task '{next.Id}' of machine " + $"'{next.Machine.Id}'."); if (next.IsWaitingToReceive) { string message = IO.Format("Livelock detected. Machine " + $"'{next.Machine.Id}' is waiting for an event, " + "but no other machine is enabled."); base.Runtime.BugFinder.NotifyAssertionFailure(message, true); } if (machineInfo != next) { machineInfo.IsActive = false; lock (next) { next.IsActive = true; System.Threading.Monitor.PulseAll(next); } lock (machineInfo) { if (machineInfo.IsCompleted) { return; } while (!machineInfo.IsActive) { IO.Debug($"<ScheduleDebug> Sleep task '{machineInfo.Id}' of machine " + $"'{machineInfo.Machine.Id}'."); System.Threading.Monitor.Wait(machineInfo); IO.Debug($"<ScheduleDebug> Wake up task '{machineInfo.Id}' of machine " + $"'{machineInfo.Machine.Id}'."); } if (!machineInfo.IsEnabled) { throw new OperationCanceledException(); } } } }
/// <summary> /// Returns the next operation to schedule. /// </summary> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>OperationId</returns> protected override int GetNextOperation(List<MachineInfo> choices, MachineInfo current) { int idx = this.Random.Next(choices.Count); var machineWithNextOperation = choices[idx]; return machineWithNextOperation.Machine.OperationId; //var enabledOperations = base.Operations.Where(val => choices.Any( // m => m.Machine.OperationId == val)).ToList(); //int opIdx = base.Random.Next(enabledOperations.Count); //return enabledOperations[opIdx]; }
/// <summary> /// Schedules the next machine to execute. /// </summary> internal void Schedule() { int?taskId = Task.CurrentId; // If the caller is the root task, then return. if (taskId != null && taskId == this.Runtime.RootTaskId) { return; } if (!this.IsSchedulerRunning) { this.Stop(); } // Checks if synchronisation not controlled by P# was used. this.CheckIfExternalSynchronizationIsUsed(); // Checks if the scheduling steps bound has been reached. this.CheckIfSchedulingStepsBoundIsReached(); MachineInfo machineInfo = this.TaskMap[(int)taskId]; MachineInfo next = null; var choices = this.TaskMap.Values.OrderBy(mi => mi.Machine.Id.Value); if (!this.Strategy.TryGetNext(out next, choices, machineInfo)) { Debug.WriteLine("<ScheduleDebug> Schedule explored."); this.HasFullyExploredSchedule = true; this.Stop(); } this.ScheduledMachine = next; this.Runtime.ScheduleTrace.AddSchedulingChoice(next.Machine.Id); next.Machine.ProgramCounter = 0; if (this.Runtime.Configuration.CacheProgramState && this.Runtime.Configuration.SafetyPrefixBound <= this.ExploredSteps) { this.Runtime.StateCache.CaptureState(this.Runtime.ScheduleTrace.Peek()); } // Checks the liveness monitors for potential liveness bugs. this.Runtime.LivenessChecker.CheckLivenessAtShedulingStep(); Debug.WriteLine($"<ScheduleDebug> Schedule task '{next.Id}' of machine " + $"'{next.Machine.Id}'."); if (next.IsWaitingToReceive) { string message = IO.Utilities.Format("Livelock detected. Machine " + $"'{next.Machine.Id}' is waiting for an event, " + "but no other machine is enabled."); this.Runtime.Scheduler.NotifyAssertionFailure(message, true); } if (machineInfo != next) { machineInfo.IsActive = false; lock (next) { next.IsActive = true; System.Threading.Monitor.PulseAll(next); } lock (machineInfo) { if (machineInfo.IsCompleted) { return; } while (!machineInfo.IsActive) { Debug.WriteLine($"<ScheduleDebug> Sleep task '{machineInfo.Id}' of machine " + $"'{machineInfo.Machine.Id}'."); System.Threading.Monitor.Wait(machineInfo); Debug.WriteLine($"<ScheduleDebug> Wake up task '{machineInfo.Id}' of machine " + $"'{machineInfo.Machine.Id}'."); } if (!machineInfo.IsEnabled) { throw new ExecutionCanceledException(); } } } }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable<MachineInfo> choices, MachineInfo current) { var availableMachines = choices.Where( mi => mi.IsEnabled && !mi.IsBlocked && !mi.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { availableMachines = choices.Where(m => m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { next = null; return false; } } next = this.GetPrioritizedMachine(availableMachines, current); this.ExploredSteps++; return true; }
/// <summary> /// Returns the next operation to schedule. /// </summary> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>OperationId</returns> protected abstract int GetNextOperation(List <MachineInfo> choices, MachineInfo current);
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable <MachineInfo> choices, MachineInfo current) { if (this.HasCurrentOperationCompleted(choices, current)) { this.Operations.Remove(current.Machine.OperationId); Debug.WriteLine("<OperationDebug> Removes operation '{0}'.", current.Machine.OperationId); } var availableMachines = choices.Where( mi => mi.IsEnabled && !mi.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { availableMachines = choices.Where(m => m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { next = null; return(false); } } this.TryRegisterNewOperations(availableMachines, current); var nextOperation = this.GetNextOperation(availableMachines, current); Debug.WriteLine("<OperationDebug> Chosen operation '{0}'.", nextOperation); Debug.Write("<OperationDebug> Operation list: "); for (int opIdx = 0; opIdx < this.Operations.Count; opIdx++) { if (opIdx < this.Operations.Count - 1) { Debug.Write("'{0}', ", this.Operations[opIdx]); } else { Debug.Write("'{0}'.\n", this.Operations[opIdx]); } } if (this.Configuration.DynamicEventQueuePrioritization) { var machineChoices = availableMachines.Where(mi => mi.Machine is Machine). Select(m => m.Machine as Machine); foreach (var choice in machineChoices) { choice.SetQueueOperationPriority(nextOperation); } } next = this.GetNextMachineWithOperation(availableMachines, nextOperation); this.ExploredSteps++; return(true); }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable<MachineInfo> choices, MachineInfo current) { if (this.HasCurrentOperationCompleted(choices, current)) { this.Operations.Remove(current.Machine.OperationId); IO.Debug("<OperationDebug> Removes operation '{0}'.", current.Machine.OperationId); } var availableMachines = choices.Where( mi => mi.IsEnabled && !mi.IsBlocked && !mi.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { availableMachines = choices.Where(m => m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { next = null; return false; } } this.TryRegisterNewOperations(availableMachines, current); var nextOperation = this.GetNextOperation(availableMachines, current); IO.Debug("<OperationDebug> Chosen operation '{0}'.", nextOperation); if (IO.Debugging) { IO.Print("<OperationDebug> Operation list: "); for (int opIdx = 0; opIdx < this.Operations.Count; opIdx++) { if (opIdx < this.Operations.Count - 1) { IO.Print("'{0}', ", this.Operations[opIdx]); } else { IO.Print("'{0}'.\n", this.Operations[opIdx]); } } } if (this.Configuration.DynamicEventQueuePrioritization) { var machineChoices = availableMachines.Where(mi => mi.Machine is Machine). Select(m => m.Machine as Machine); foreach (var choice in machineChoices) { choice.SetQueueOperationPriority(nextOperation); } } next = this.GetNextMachineWithOperation(availableMachines, nextOperation); this.ExploredSteps++; return true; }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public virtual bool TryGetNext(out MachineInfo next, IEnumerable<MachineInfo> choices, MachineInfo current) { var machines = choices.OrderBy(mi => mi.Machine.Id.Value).ToList(); var currentMachineIdx = machines.IndexOf(current); var orderedMachines = machines.GetRange(currentMachineIdx, machines.Count - currentMachineIdx); if (currentMachineIdx != 0) { orderedMachines.AddRange(machines.GetRange(0, currentMachineIdx)); } var availableMachines = orderedMachines.Where( mi => mi.IsEnabled && !mi.IsBlocked && !mi.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { availableMachines = choices.Where(m => m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { next = null; return false; } } int idx = 0; while (this.RemainingDelays.Count > 0 && this.ExploredSteps == this.RemainingDelays[0]) { idx = (idx + 1) % availableMachines.Count; this.RemainingDelays.RemoveAt(0); IO.PrintLine("<DelayLog> Inserted delay, '{0}' remaining.", this.RemainingDelays.Count); } next = availableMachines[idx]; this.ExploredSteps++; return true; }
/// <summary> /// Returns true if the current operation has completed. /// </summary> /// <param name="choices">List of machine infos</param> /// <param name="current">MachineInfo</param> /// <returns>Boolean</returns> private bool HasCurrentOperationCompleted(IEnumerable<MachineInfo> choices, MachineInfo current) { foreach (var choice in choices.Where(mi => !mi.IsCompleted)) { if (choice.Machine.OperationId == current.Machine.OperationId || choice.Machine.IsOperationPending(current.Machine.OperationId)) { return false; } } return true; }
/// <summary> /// Returns the next operation to schedule. /// </summary> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>OperationId</returns> protected abstract int GetNextOperation(List<MachineInfo> choices, MachineInfo current);
/// <summary> /// Tries to register any new operations. /// </summary> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> private void TryRegisterNewOperations(IEnumerable<MachineInfo> choices, MachineInfo current) { if (this.Operations.Count == 0) { this.Operations.Add(current.Machine.OperationId); } var operationIds = choices.Select(mi => mi.Machine.OperationId).Distinct(); foreach (var id in operationIds.Where(id => !this.Operations.Contains(id))) { var opIndex = this.Random.Next(this.Operations.Count) + 1; this.Operations.Insert(opIndex, id); IO.Debug("<OperationDebug> Detected new operation '{0}' at index '{1}'.", id, opIndex); } }
/// <summary> /// Returns the next operation to schedule. /// </summary> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>OperationId</returns> protected override int GetNextOperation(List<MachineInfo> choices, MachineInfo current) { var operationIds = choices.Select(val => val.Machine.OperationId).Distinct(); if (this.PriorityChangePoints.Contains(this.ExploredSteps)) { if (operationIds.Count() == 1) { this.MovePriorityChangePointForward(); } else { var priority = this.GetHighestPriorityEnabledOperationId(choices); base.Operations.Remove(priority); base.Operations.Add(priority); IO.PrintLine("<OperationLog> Operation '{0}' changes to lowest priority.", priority); } } return this.GetHighestPriorityEnabledOperationId(choices); }
/// <summary> /// Returns the prioritized machine. /// </summary> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>MachineInfo</returns> private MachineInfo GetPrioritizedMachine(List<MachineInfo> choices, MachineInfo current) { if (this.PrioritizedMachines.Count == 0) { this.PrioritizedMachines.Add(current.Machine.Id); } foreach (var mi in choices.Where(mi => !this.PrioritizedMachines.Contains(mi.Machine.Id))) { var mIndex = this.Random.Next(this.PrioritizedMachines.Count) + 1; this.PrioritizedMachines.Insert(mIndex, mi.Machine.Id); IO.Debug($"<PCTDebug> Detected new machine '{mi.Machine.Id}' at index '{mIndex}'."); } if (this.PriorityChangePoints.Contains(this.ExploredSteps)) { if (choices.Count == 1) { this.MovePriorityChangePointForward(); } else { var priority = this.GetHighestPriorityEnabledMachine(choices); this.PrioritizedMachines.Remove(priority); this.PrioritizedMachines.Add(priority); IO.PrintLine($"<PCTLog> Machine '{priority}' changes to lowest priority."); } } var prioritizedMachine = this.GetHighestPriorityEnabledMachine(choices); IO.Debug($"<PCTDebug> Prioritized machine '{prioritizedMachine}'."); if (IO.Debugging) { IO.Print("<PCTDebug> Priority list: "); for (int idx = 0; idx < this.PrioritizedMachines.Count; idx++) { if (idx < this.PrioritizedMachines.Count - 1) { IO.Print($"'{this.PrioritizedMachines[idx]}', "); } else { IO.Print($"'{this.PrioritizedMachines[idx]}({1})'.\n"); } } } var prioritizedMachineInfo = choices.First(mi => mi.Machine.Id.Equals(prioritizedMachine)); return prioritizedMachineInfo; }
/// <summary> /// Notify that a new task has been created for the given machine. /// </summary> /// <param name="id">TaskId</param> /// <param name="machine">Machine</param> internal virtual void NotifyNewTaskCreated(int id, AbstractMachine machine) { var machineInfo = new MachineInfo(id, machine); IO.Debug($"<ScheduleDebug> Created task '{machineInfo.Id}' for machine " + $"'{machineInfo.Machine.Id}'."); if (this.MachineInfos.Count == 0) { machineInfo.IsActive = true; } this.MachineInfos.Add(machineInfo); this.TaskMap.TryAdd(id, machineInfo); }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable<MachineInfo> choices, MachineInfo current) { if (this.BoundedDFS.HasReachedMaxSchedulingSteps()) { return this.Random.TryGetNext(out next, choices, current); } { return this.BoundedDFS.TryGetNext(out next, choices, current); } }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable <MachineInfo> choices, MachineInfo current) { if (this.BoundedDFS.HasReachedMaxSchedulingSteps()) { return(this.Random.TryGetNext(out next, choices, current)); } { return(this.BoundedDFS.TryGetNext(out next, choices, current)); } }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable<MachineInfo> choices, MachineInfo current) { var availableMachines = choices.Where( m => m.IsEnabled && !m.IsBlocked && !m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { availableMachines = choices.Where(m => m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { next = null; return false; } } if (this.ExploredSteps >= this.ScheduleTrace.Count) { IO.Error.ReportAndExit("Trace is not reproducible: execution is longer than trace."); } ScheduleStep nextStep = this.ScheduleTrace[this.ExploredSteps]; if (nextStep.Type != ScheduleStepType.SchedulingChoice) { IO.Error.ReportAndExit("Trace is not reproducible: next step is not a scheduling choice."); } next = availableMachines.FirstOrDefault(m => m.Machine.Id.Type.Equals( nextStep.ScheduledMachineType) && m.Machine.Id.Value == nextStep.ScheduledMachineId); if (next == null) { IO.Error.ReportAndExit("Trace is not reproducible: cannot detect machine with type " + $"'{nextStep.ScheduledMachineType}' and id '{nextStep.ScheduledMachineId}'."); } this.ExploredSteps++; return true; }
/// <summary> /// Returns the prioritized machines. /// </summary> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> private List<MachineInfo> GetPrioritizedMachines(List<MachineInfo> choices, MachineInfo current) { choices = choices.OrderBy(mi => mi.Machine.Id.Value).ToList(); choices = choices.OrderBy(mi => mi.Machine.OperationId).ToList(); MachineInfo priorityMachine = current; var prioritizedMachines = new List<MachineInfo>(); if (current.Machine.OperationId == this.PrioritizedOperationId) { var currentMachineIdx = choices.IndexOf(current); prioritizedMachines = choices.GetRange(currentMachineIdx, choices.Count - currentMachineIdx); if (currentMachineIdx != 0) { prioritizedMachines.AddRange(choices.GetRange(0, currentMachineIdx)); } } else { priorityMachine = choices.First(mi => mi.Machine.OperationId == this.PrioritizedOperationId); var priorityMachineIdx = choices.IndexOf(priorityMachine); prioritizedMachines = choices.GetRange(priorityMachineIdx, choices.Count - priorityMachineIdx); if (priorityMachineIdx != 0) { prioritizedMachines.AddRange(choices.GetRange(0, priorityMachineIdx)); } } prioritizedMachines = prioritizedMachines.Where( mi => mi.IsEnabled && !mi.IsBlocked && !mi.IsWaitingToReceive).ToList(); if (prioritizedMachines.Count == 0) { return prioritizedMachines; } return prioritizedMachines; }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable<MachineInfo> choices, MachineInfo current) { var availableMachines = choices.Where( m => m.IsEnabled && !m.IsBlocked && !m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { availableMachines = choices.Where(m => m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { next = null; return false; } } SChoice nextChoice = null; List<SChoice> scs = null; if (this.SchIndex < this.ScheduleStack.Count) { scs = this.ScheduleStack[this.SchIndex]; } else { scs = new List<SChoice>(); foreach (var task in availableMachines) { scs.Add(new SChoice(task.Machine.Id.Value)); } this.ScheduleStack.Add(scs); } nextChoice = scs.FirstOrDefault(val => !val.IsDone); if (nextChoice == null) { next = null; return false; } if (this.SchIndex > 0) { var previousChoice = this.ScheduleStack[this.SchIndex - 1].Last(val => val.IsDone); previousChoice.IsDone = false; } next = availableMachines.Find(task => task.Machine.Id.Value == nextChoice.Id); nextChoice.IsDone = true; this.SchIndex++; if (next == null) { return false; } this.ExploredSteps++; return true; }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable<MachineInfo> choices, MachineInfo current) { next = null; List<MachineInfo> availableMachines; if (this.Configuration.BoundOperations) { availableMachines = this.GetPrioritizedMachines(choices.ToList(), current); } else { choices = choices.OrderBy(machine => machine.Machine.Id.Value).ToList(); availableMachines = choices.Where( m => m.IsEnabled && !m.IsBlocked && !m.IsWaitingToReceive).ToList(); } if (availableMachines.Count == 0) { IO.PrintLine(">> No available machines to schedule ..."); return false; } this.ExploredSteps++; var parsed = false; while (!parsed) { if (this.InputCache.Count >= this.ExploredSteps) { var step = this.InputCache[this.ExploredSteps - 1]; int idx = 0; if (step.Length > 0) { idx = Convert.ToInt32(step); } else { this.InputCache[this.ExploredSteps - 1] = "0"; } next = availableMachines[idx]; parsed = true; break; } IO.PrintLine(">> Available machines to schedule ..."); for (int idx = 0; idx < availableMachines.Count; idx++) { var m = availableMachines[idx]; if (this.Configuration.BoundOperations) { IO.PrintLine($">> [{idx}] '{m.Machine.Id}' with " + $"operation id '{m.Machine.OperationId}'"); } else { IO.PrintLine($">> [{idx}] '{m.Machine.Id}'"); } } IO.PrintLine($">> Choose machine to schedule [step '{this.ExploredSteps}']"); var input = IO.GetLine(); if (input.Equals("replay")) { if (!this.Replay()) { continue; } this.Configuration.SchedulingIterations++; this.ConfigureNextIteration(); return false; } else if (input.Equals("jump")) { this.Jump(); continue; } else if (input.Equals("reset")) { this.Configuration.SchedulingIterations++; this.Reset(); return false; } else if (input.Length > 0) { try { var idx = Convert.ToInt32(input); if (idx < 0) { IO.PrintLine(">> Expected positive integer, please retry ..."); continue; } next = availableMachines[idx]; if (next == null) { IO.PrintLine(">> Unexpected id, please retry ..."); continue; } if (this.Configuration.BoundOperations) { this.PrioritizedOperationId = next.Machine.OperationId; } } catch (FormatException) { IO.PrintLine(">> Wrong format, please retry ..."); continue; } } else { next = availableMachines[0]; } this.InputCache.Add(input); parsed = true; } return true; }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> bool ISchedulingStrategy.TryGetNext(out MachineInfo next, IEnumerable<MachineInfo> choices, MachineInfo current) { var availableMachines = choices.Where( m => m.IsEnabled && !m.IsBlocked && !m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { availableMachines = choices.Where(m => m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { next = null; return false; } } if (this.Runtime.Configuration.EnableCycleReplayingStrategy) { ScheduleStep nextStep = this.PotentialCycle[this.CurrentCycleIndex].Item1; if (nextStep.Type != ScheduleStepType.SchedulingChoice) { IO.Debug("<LivenessDebug> Trace is not reproducible: next step is not a scheduling choice."); this.EscapeCycle(); return this.BugFindingSchedulingStrategy.TryGetNext(out next, choices, current); } IO.Debug($"<LivenessDebug> Replaying '{nextStep.Index}' '{nextStep.ScheduledMachine.Id}'."); next = availableMachines.FirstOrDefault(m => m.Machine.Id.Type.Equals( nextStep.ScheduledMachineType) && m.Machine.Id.Value == nextStep.ScheduledMachineId); if (next == null) { IO.Debug("<LivenessDebug> Trace is not reproducible: cannot detect machine with type " + $"'{nextStep.ScheduledMachineType}' and id '{nextStep.ScheduledMachineId}'."); this.EscapeCycle(); return this.BugFindingSchedulingStrategy.TryGetNext(out next, choices, current); } this.CurrentCycleIndex++; if (this.CurrentCycleIndex == this.PotentialCycle.Count) { this.CurrentCycleIndex = 0; } } else { int idx = this.Random.Next(availableMachines.Count); next = availableMachines[idx]; } return true; }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable<MachineInfo> choices, MachineInfo current) { var availableMachines = choices.Where( m => m.IsEnabled && !m.IsBlocked && !m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { availableMachines = choices.Where(m => m.IsWaitingToReceive).ToList(); if (availableMachines.Count == 0) { next = null; return false; } } int idx = this.Random.Next(availableMachines.Count); next = availableMachines[idx]; this.ExploredSteps++; return true; }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable <MachineInfo> choices, MachineInfo current) { if (this.PrefixStrategy.GetExploredSteps() > this.SafetyPrefixDepth) { return(this.SuffixStrategy.TryGetNext(out next, choices, current)); } else { return(this.PrefixStrategy.TryGetNext(out next, choices, current)); } }
/// <summary> /// Returns the next machine to schedule. /// </summary> /// <param name="next">Next</param> /// <param name="choices">Choices</param> /// <param name="current">Curent</param> /// <returns>Boolean</returns> public bool TryGetNext(out MachineInfo next, IEnumerable <MachineInfo> choices, MachineInfo current) { next = null; List <MachineInfo> availableMachines; if (this.Configuration.BoundOperations) { availableMachines = this.GetPrioritizedMachines(choices.ToList(), current); } else { choices = choices.OrderBy(machine => machine.Machine.Id.Value).ToList(); availableMachines = choices.Where( m => m.IsEnabled && !m.IsWaitingToReceive).ToList(); } if (availableMachines.Count == 0) { this.Logger.WriteLine(">> No available machines to schedule ..."); return(false); } this.ExploredSteps++; var parsed = false; while (!parsed) { if (this.InputCache.Count >= this.ExploredSteps) { var step = this.InputCache[this.ExploredSteps - 1]; int idx = 0; if (step.Length > 0) { idx = Convert.ToInt32(step); } else { this.InputCache[this.ExploredSteps - 1] = "0"; } next = availableMachines[idx]; parsed = true; break; } this.Logger.WriteLine(">> Available machines to schedule ..."); for (int idx = 0; idx < availableMachines.Count; idx++) { var m = availableMachines[idx]; if (this.Configuration.BoundOperations) { this.Logger.WriteLine($">> [{idx}] '{m.Machine.Id}' with " + $"operation id '{m.Machine.OperationId}'"); } else { this.Logger.WriteLine($">> [{idx}] '{m.Machine.Id}'"); } } this.Logger.WriteLine($">> Choose machine to schedule [step '{this.ExploredSteps}']"); var input = Console.ReadLine(); if (input.Equals("replay")) { if (!this.Replay()) { continue; } this.Configuration.SchedulingIterations++; this.ConfigureNextIteration(); return(false); } else if (input.Equals("jump")) { this.Jump(); continue; } else if (input.Equals("reset")) { this.Configuration.SchedulingIterations++; this.Reset(); return(false); } else if (input.Length > 0) { try { var idx = Convert.ToInt32(input); if (idx < 0) { this.Logger.WriteLine(">> Expected positive integer, please retry ..."); continue; } next = availableMachines[idx]; if (next == null) { this.Logger.WriteLine(">> Unexpected id, please retry ..."); continue; } if (this.Configuration.BoundOperations) { this.PrioritizedOperationId = next.Machine.OperationId; } } catch (FormatException) { this.Logger.WriteLine(">> Wrong format, please retry ..."); continue; } } else { next = availableMachines[0]; } this.InputCache.Add(input); parsed = true; } return(true); }
/// <summary> /// Schedules the next machine to execute. /// </summary> internal virtual void Schedule() { int?id = Task.CurrentId; if (id == null || id == this.Runtime.RootTaskId) { return; } if (this.BugFound || !this.IsSchedulerRunning) { this.Stop(); } // Checks if the scheduling steps bound has been reached. this.CheckIfSchedulingStepsBoundIsReached(false); MachineInfo machineInfo = null; if (!this.TaskMap.TryGetValue((int)id, out machineInfo)) { IO.Debug($"<ScheduleDebug> Unable to schedule task '{id}'."); this.Stop(); } MachineInfo next = null; if (!this.Strategy.TryGetNext(out next, this.TaskMap.Values, machineInfo)) { IO.Debug("<ScheduleDebug> Schedule explored."); this.HasFullyExploredSchedule = true; this.Stop(); } this.Runtime.ScheduleTrace.AddSchedulingChoice(next.Machine.Id); if (this.Runtime.Configuration.CacheProgramState && this.Runtime.Configuration.SafetyPrefixBound <= this.ExploredSteps) { this.Runtime.StateCache.CaptureState(this.Runtime.ScheduleTrace.Peek()); } // Checks the liveness monitors for potential liveness bugs. this.Runtime.LivenessChecker.CheckLivenessAtShedulingStep(); IO.Debug($"<ScheduleDebug> Schedule task '{next.Id}' of machine " + $"'{next.Machine.Id}'."); if (next.IsWaitingToReceive) { string message = IO.Format("Livelock detected. Machine " + $"'{next.Machine.Id}' is waiting for an event, " + "but no other machine is enabled."); this.Runtime.BugFinder.NotifyAssertionFailure(message, true); } if (machineInfo != next) { machineInfo.IsActive = false; lock (next) { next.IsActive = true; System.Threading.Monitor.PulseAll(next); } lock (machineInfo) { if (machineInfo.IsCompleted) { return; } while (!machineInfo.IsActive) { IO.Debug($"<ScheduleDebug> Sleep task '{machineInfo.Id}' of machine " + $"'{machineInfo.Machine.Id}'."); System.Threading.Monitor.Wait(machineInfo); IO.Debug($"<ScheduleDebug> Wake up task '{machineInfo.Id}' of machine " + $"'{machineInfo.Machine.Id}'."); } if (!machineInfo.IsEnabled) { throw new OperationCanceledException(); } } } }