/// <summary> /// (asynchronously) block until there is a process available on the local queue, the rack queue /// or the cluster queue, then return that process. If ShutDown is called, this returns null /// immediately /// </summary> private async Task <Process> GetProcess() { // make a new waiter object to block on all the available queues until a Process is available var waiter = new ProcessWaiter(this); // get the actual blocker Task out of the waiter var blocker = waiter.Initialize(); logger.Log("Computer " + name + " trying to find process on local queue"); // try to match with an available Process in the local queue. If AddWaiter returns false, there // wasn't one, but by passing in waiter, we ensure that blocker will be unblocked if one // turns up. If Peek returns true there was already a waiting Process to be paired with, // blocker has been unblocked, and the await below will fall through immediately and return // the process; in this case don't bother to add the waiter to the rack and cluster queues. if (!localQueue.AddWaiter(waiter)) { logger.Log("Computer " + name + " trying to find process on rack queue"); // there was no local process, so try to match with an available Process in the rack queue. // If Peek returns false, there wasn't one, but by passing in waiter, we ensure that blocker // will be unblocked if one turns up. If Peek returns true then blocker is already unblocked // (because there was a Process in the rack queue, or by the localQueue we just put it in above) // and matched with a waiting process, and the await below will fall through immediately. if (!rackQueue.AddWaiter(waiter)) { logger.Log("Computer " + name + " trying to find process on cluster queue"); // there was no local or rack process, so try to match with an available Process in the // cluster queue. clusterQueue.AddWaiter(waiter); } } logger.Log("Computer " + name + " waiting for matched process"); // we want to wait either for waiter to be matched with a Process in one of the three queues, or // for ShutDown to be called, so make an array of tasks and wait for the first one to be unblocked. TaskCompletionSource <Process> thisWaiter = GetAsyncFinishWaiter(); var unblocked = await Task.WhenAny(blocker, thisWaiter.Task); RemoveAsyncFinishWaiter(thisWaiter); if (unblocked.Result != null) { logger.Log("Computer " + name + " matched process " + blocker.Result.Id); } else { logger.Log("Computer " + name + " unblocked by shutdown"); } return(unblocked.Result); }
/// <summary> /// add a waiting computer. If there is an unclaimed process waiting, the /// process will be assigned to the computer and the computer's Task will be /// unblocked. Returns true if the waiter has been matched (by this call or /// another asynchronous event in the meantime). Returns false if the computer /// still needs to be matched. /// </summary> /// <param name="waiter">holder for the waiting computer</param> /// <returns></returns> public bool AddWaiter(ProcessWaiter waiter) { // process will exit the lock holding the value of the newly-claimed process, if // any. process may be non-null exiting the lock even if there was no // unclaimed process; the claimed flag below disambiguates Process process = null; // claimed will exit the lock set to true if and only if there was an unclaimed // waiter, in which case waiter will be set to the value of the waiter bool claimed = false; // lock ordering discipline is Queue first, then waiter, then process lock (this) { if (!active) { // the queue has been shut down so don't accept another waiter lock (waiter) { if (waiter.Unclaimed) { waiter.Claim(); // this will make us Dispatch the waiter with a null process below // which is correct since the queue has been shut down, and will cause // the waiting computer's commandloop to exit if it hasn't already claimed = true; } } } // even if there are processes, they may have been claimed by other computers // already, so use a loop here while (active && processQueue.Count > 0 && !claimed) { // get the next available process; don't dequeue it yet because // we have to wait until we acquire the locks below to figure out // if it's going to be matched to anything process = processQueue.Peek(); // lock ordering discipline is Queue first, then waiter, then process lock (waiter) { if (waiter.Unclaimed) { // lock ordering discipline is Queue first, then waiter, then process lock (process) { // another queue might have turned up and claimed the Process // while we were dithering; matching a process to a computer // must be done while both are locked if (process.Unclaimed) { // remove the process from the queue and match it to the computer processQueue.Dequeue(); var computer = waiter.Claim(); process.Claim(computer); // break out of the loop claimed = true; } else { // the process that we Peek()ed above was already claimed so discard it // and go around the loop again processQueue.Dequeue(); } } } else { // there's no point in continuing to look for a process for the waiter // since it has been claimed by someone else. // The process we Peek()ed is left in the queue for the next // waiter to match return true; } } } // there were no unclaimed processes, so add the waiter to the queue of // waiting computers if (!claimed) { waiterQueue.Enqueue(waiter); } } // exit the lock before triggering the wakeup of the computer if (claimed) { // this pairs the process with the computer waiter.Dispatch(process); return true; } else { return false; } }
/// <summary> /// add a waiting computer. If there is an unclaimed process waiting, the /// process will be assigned to the computer and the computer's Task will be /// unblocked. Returns true if the waiter has been matched (by this call or /// another asynchronous event in the meantime). Returns false if the computer /// still needs to be matched. /// </summary> /// <param name="waiter">holder for the waiting computer</param> /// <returns></returns> public bool AddWaiter(ProcessWaiter waiter) { // process will exit the lock holding the value of the newly-claimed process, if // any. process may be non-null exiting the lock even if there was no // unclaimed process; the claimed flag below disambiguates Process process = null; // claimed will exit the lock set to true if and only if there was an unclaimed // waiter, in which case waiter will be set to the value of the waiter bool claimed = false; // lock ordering discipline is Queue first, then waiter, then process lock (this) { if (!active) { // the queue has been shut down so don't accept another waiter lock (waiter) { if (waiter.Unclaimed) { waiter.Claim(); // this will make us Dispatch the waiter with a null process below // which is correct since the queue has been shut down, and will cause // the waiting computer's commandloop to exit if it hasn't already claimed = true; } } } // even if there are processes, they may have been claimed by other computers // already, so use a loop here while (active && processQueue.Count > 0 && !claimed) { // get the next available process; don't dequeue it yet because // we have to wait until we acquire the locks below to figure out // if it's going to be matched to anything process = processQueue.Peek(); // lock ordering discipline is Queue first, then waiter, then process lock (waiter) { if (waiter.Unclaimed) { // lock ordering discipline is Queue first, then waiter, then process lock (process) { // another queue might have turned up and claimed the Process // while we were dithering; matching a process to a computer // must be done while both are locked if (process.Unclaimed) { // remove the process from the queue and match it to the computer processQueue.Dequeue(); var computer = waiter.Claim(); process.Claim(computer); // break out of the loop claimed = true; } else { // the process that we Peek()ed above was already claimed so discard it // and go around the loop again processQueue.Dequeue(); } } } else { // there's no point in continuing to look for a process for the waiter // since it has been claimed by someone else. // The process we Peek()ed is left in the queue for the next // waiter to match return(true); } } } // there were no unclaimed processes, so add the waiter to the queue of // waiting computers if (!claimed) { waiterQueue.Enqueue(waiter); } } // exit the lock before triggering the wakeup of the computer if (claimed) { // this pairs the process with the computer waiter.Dispatch(process); return(true); } else { return(false); } }
/// <summary> /// add a schedulable process. If there is an unclaimed computer waiting, the /// process will be assigned to the computer and the computer's Task will be /// unblocked. Returns true if the process has been matched (by this call or /// another asynchronous event in the meantime). Returns false if the process /// still needs to be matched. /// </summary> public bool AddProcess(Process process) { // waiter will exit the lock holding the value of the newly-claimed waiter, if // any. waiter may be non-null exiting the lock even if there was no // unclaimed waiter; the claimed flag below disambiguates ProcessWaiter waiter = null; // claimed will exit the lock set to true if and only if there was an unclaimed // waiter, in which case waiter will be set to the value of the waiter bool claimed = false; // lock ordering discipline is Queue first, then waiter, then process lock (this) { // if we are shutting down, return immediately if (!active) { return(false); } // even if there are waiters, they may have been claimed by processes // already, so use a loop here while (waiterQueue.Count > 0 && !claimed) { // get the next available waiter; don't dequeue it yet because // we have to wait until we acquire the locks below to figure out // if it's going to be matched to anything waiter = waiterQueue.Peek(); // lock ordering discipline is Queue first, then waiter, then process lock (waiter) { if (waiter.Unclaimed) { // lock ordering discipline is Queue first, then waiter, then process lock (process) { // another queue might have turned up and claimed the Process // while we were dithering; matching a process to a computer // must be done while both are locked if (process.Unclaimed) { // remove the waiter from the queue and match it to the process waiterQueue.Dequeue(); var computer = waiter.Claim(); process.Claim(computer); // break out of the loop claimed = true; } else { // there's no point in continuing to try to add the process // since it has been claimed by someone else. // The waiter we Peek()ed is left in the queue for the next // process to match return(true); } } } else { // the waiter that we Peek()ed above was already claimed so discard it // and go around the loop again waiterQueue.Dequeue(); } } } // there were no unclaimed waiters, so add the process to the queue of // schedulable items while we're holding the queue lock if (!claimed) { lock (process) { // let the process know it has been added to another queue process.IncrementQueueCount(); } processQueue.Enqueue(process); } } // exit the lock before triggering the wakeup of the computer if (claimed) { // this pairs the process with the computer waiter.Dispatch(process); return(true); } else { return(false); } }