/// <summary> /// Invokes the pulse operation. /// </summary> private void Pulse(PulseOperation pulseOperation) { if (pulseOperation is PulseOperation.Next) { if (this.WaitQueue.Count > 0) { // System.Threading.Monitor has FIFO semantics. var waitingOp = this.WaitQueue[0]; this.WaitQueue.RemoveAt(0); this.ReadyQueue.Add(waitingOp); IO.Debug.WriteLine("<Coyote> Operation '{0}' is pulsed by task '{1}'.", waitingOp.Id, SystemTask.CurrentId); } } else { foreach (var waitingOp in this.WaitQueue) { this.ReadyQueue.Add(waitingOp); IO.Debug.WriteLine("<Coyote> Operation '{0}' is pulsed by task '{1}'.", waitingOp.Id, SystemTask.CurrentId); } this.WaitQueue.Clear(); } }
/// <summary> /// Schedules a pulse operation that will either execute immediately or be scheduled /// to execute after the current owner releases the lock. This nondeterministic action /// is controlled by the runtime to simulate scenarios where the pulse is delayed by /// the operation system. /// </summary> private void SchedulePulse(PulseOperation pulseOperation) { var op = this.Resource.Runtime.GetExecutingOperation(); if (this.Owner != op) { throw new SystemSynchronizationLockException(); } // Pulse has a delay in the operating system, we can simulate that here // by scheduling the pulse operation to be executed nondeterministically. this.PulseQueue.Enqueue(pulseOperation); if (this.PulseQueue.Count is 1) { // Create a task for draining the queue. To optimize the testing performance, // we create and maintain a single task to perform this role. Task.Run(this.DrainPulseQueue); } }