/// <summary>
        /// Runs the execution engine, which eternally processes execution
        /// requests posted to the main queue.
        /// </summary>
        public void Run()
        {
            this.keepSwimming = true;

            while (this.keepSwimming)
            {
                // Pull request off queue.
                Console.WriteLine("Waiting for new work request.");
                CloudExecutionRequest executionRequest = this.mainQueue.GetRequest(block: true);
                if (!this.keepSwimming)
                {
                    // Exit without deleting request from queue.
                    // Request will become visible again after timeout expires.
                    break;
                }

                // TODO: For now, we immediately delete the requests we take.
                // Eventually we'll want to leave the request invisible until
                // some timeout expires so that other engines can pick it up
                // if we crash.
                this.mainQueue.DeleteRequest(executionRequest);

                Console.WriteLine("Received request with ID {0}.", executionRequest.Identifier);

                // Make a progress report to soothe nervous users.
                CloudExecutionReport statusUpdate = new CloudExecutionReport(
                    executionRequest.Identifier,
                    CloudExecutionReport.StatusCode.InProgress,
                    0,
                    null,
                    null,
                    0,
                    null);

                // Submit progress report.
                this.mainQueue.SubmitReport(statusUpdate, executionRequest.ReportQueue);

                // Do the requested operation.
                CloudExecutionReport report = null;
                switch (executionRequest.RequestedOperation)
                {
                case CloudExecutionRequest.Operation.RunExecutable:
                    report = this.RunAnExecutable(executionRequest);
                    break;

                case CloudExecutionRequest.Operation.CommitSuicide:
                    this.keepSwimming = false;
                    goto case CloudExecutionRequest.Operation.None;

                case CloudExecutionRequest.Operation.DeleteQueue:
                    this.mainQueue.DeleteQueue(executionRequest.ReportQueue);
                    goto case CloudExecutionRequest.Operation.None;

                case CloudExecutionRequest.Operation.None:
                    report = new CloudExecutionReport(
                        executionRequest.Identifier,
                        CloudExecutionReport.StatusCode.Completed);
                    break;

                default:
                    // ToDo: This should probably be treated as an error.
                    goto case CloudExecutionRequest.Operation.None;
                }

                // Submit report.
                this.mainQueue.SubmitReport(report, executionRequest.ReportQueue);
            }
        }
        /// <summary>
        /// Run the requested executable and produce a report of the results.
        /// </summary>
        /// <param name="executionRequest">The execution request.</param>
        /// <returns>A report of the results.</returns>
        private CloudExecutionReport RunAnExecutable(CloudExecutionRequest executionRequest)
        {
            // REVIEW: How/whether to use this.
            BuildObject diagnosticsBase = new BuildObject(Path.Combine("nuobj", "diagnostics", "process"));

            // Prep working directory with input files and output dirs.
            // TODO: The below will throw cache exceptions if something
            // isn't there (they should all be though).  Need to catch
            // these and fail the execution request when this happens.
            WorkingDirectory workingDirectory = new WorkingDirectory(this.virtualIronRoot);

            foreach (BuildObjectValuePointer inputFile in executionRequest.InputFileMappings)
            {
                // REVIEW: How to determine cache container here.
                ItemCacheContainer container = ItemCacheContainer.Sources;
                if (this.multiplexedItemCache.GetItemSize(container, inputFile.ObjectHash) == -1)
                {
                    container = ItemCacheContainer.Objects;
                }

                // TODO: Move path/directory manipulation code into
                // WorkingDirectory and/or ItemCache.
                string inputFilePath = workingDirectory.PathTo(inputFile.RelativePath);
                Directory.CreateDirectory(Path.GetDirectoryName(inputFilePath));  // REVIEW: Still neeeded?
                this.multiplexedItemCache.FetchItemToFile(
                    container,
                    inputFile.ObjectHash,
                    inputFilePath);
            }

            foreach (BuildObject outputFile in executionRequest.OutputFiles)
            {
                workingDirectory.CreateDirectoryFor(outputFile);
            }

            // Run executable.
            ProcessInvoker pinv = new ProcessInvoker(
                workingDirectory,
                executionRequest.Executable,
                new string[] { executionRequest.Arguments },
                diagnosticsBase,
                null,  // This is captureStdout.  TODO: Should cleanup how this is used in ProcessInvoker.
                null); // This is dbgText.  REVIEW: How/whether to use this.

            // When ProcessInvoker's constructor returns, the process has
            // finished running.
            Console.WriteLine("Request {0} completed in {1} seconds.", executionRequest.Identifier, pinv.CpuTime);

            // Store output files in the (cloud) item cache, and create a
            // list of the mappings.
            List <BuildObjectValuePointer> outputFileMappings = new List <BuildObjectValuePointer>();

            foreach (BuildObject outFile in executionRequest.OutputFiles)
            {
                if (File.Exists(workingDirectory.PathTo(outFile)))
                {
                    string fileHash = Util.hashFilesystemPath(workingDirectory.PathTo(outFile));
                    Util.Assert(!string.IsNullOrEmpty(fileHash));

                    // Note we explicitly write to the cloud cache here.
                    this.cloudCache.StoreItemFromFile(ItemCacheContainer.Objects, fileHash, workingDirectory.PathTo(outFile));
                    outputFileMappings.Add(new BuildObjectValuePointer(fileHash, outFile.getRelativePath()));
                }
            }

            // Collect the results into a report.
            CloudExecutionReport report = new CloudExecutionReport(
                executionRequest.Identifier,
                CloudExecutionReport.StatusCode.Completed,
                pinv.ExitCode,
                pinv.GetStdout(),
                pinv.GetStderr(),
                pinv.CpuTime,
                outputFileMappings);

            return(report);
        }