//===--- TaskQueue.inc - Default serial TaskQueue ---------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// /// /// \file /// This file contains a platform-agnostic implementation of TaskQueue /// using the functions from llvm/Support/Program.h. /// /// \note The default implementation of TaskQueue does not support parallel /// execution, nor does it support output buffering. As a result, /// platform-specific implementations should be preferred. /// //===----------------------------------------------------------------------===// #include "swift/Basic/TaskQueue.h" #include "swift/Basic/LLVM.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Signals.h" #if defined(_WIN32) #define NOMINMAX #include #include #endif #include "Task.inc" using namespace llvm::sys; bool TaskQueue::supportsBufferingOutput() { // The default implementation supports buffering output. return true; } bool TaskQueue::supportsParallelExecution() { // The default implementation does not support parallel execution. return false; } unsigned TaskQueue::getNumberOfParallelTasks() const { // The default implementation does not support parallel execution. return 1; } void TaskQueue::addTask(const char *ExecPath, ArrayRef Args, ArrayRef Env, void *Context, bool SeparateErrors) { auto T = make_unique(ExecPath, Args, Env, Context, SeparateErrors); QueuedTasks.push(std::move(T)); } bool TaskQueue::execute(TaskBeganCallback Began, TaskFinishedCallback Finished, TaskSignalledCallback Signalled) { bool ContinueExecution = true; // This implementation of TaskQueue doesn't support parallel execution. // We need to reference NumberOfParallelTasks to avoid warnings, though. (void)NumberOfParallelTasks; while (!QueuedTasks.empty() && ContinueExecution) { std::unique_ptr T(QueuedTasks.front().release()); QueuedTasks.pop(); bool ExecutionFailed = T->execute(); if (ExecutionFailed) { return true; } if (Began) { Began(T->PI.Pid, T->Context); } std::string ErrMsg; T->PI = Wait(T->PI, 0, true, &ErrMsg); int ReturnCode = T->PI.ReturnCode; auto StdoutBuffer = llvm::MemoryBuffer::getFile(T->StdoutPath); StringRef StdoutContents = StdoutBuffer.get()->getBuffer(); StringRef StderrContents; if (T->SeparateErrors) { auto StderrBuffer = llvm::MemoryBuffer::getFile(T->StderrPath); StderrContents = StderrBuffer.get()->getBuffer(); } #if defined(_WIN32) // Wait() sets the upper two bits of the return code to indicate warnings // (10) and errors (11). // // This isn't a true signal on Windows, but we'll treat it as such so that // we clean up after it properly bool Crashed = ReturnCode & 0xC0000000; FILETIME CreationTime; FILETIME ExitTime; FILETIME UtimeTicks; FILETIME StimeTicks; PROCESS_MEMORY_COUNTERS Counters = {}; GetProcessTimes(T->PI.Process, &CreationTime, &ExitTime, &StimeTicks, &UtimeTicks); // Each tick is 100ns uint64_t Utime = ((uint64_t)UtimeTicks.dwHighDateTime << 32 | UtimeTicks.dwLowDateTime) / 10; uint64_t Stime = ((uint64_t)StimeTicks.dwHighDateTime << 32 | StimeTicks.dwLowDateTime) / 10; GetProcessMemoryInfo(T->PI.Process, &Counters, sizeof(Counters)); TaskProcessInformation TPI(T->PI.Pid, Utime, Stime, Counters.PeakWorkingSetSize); #else // Wait() returning a return code of -2 indicates the process received // a signal during execution. bool Crashed = ReturnCode == -2; TaskProcessInformation TPI(T->PI.Pid); #endif if (Crashed) { if (Signalled) { TaskFinishedResponse Response = Signalled(T->PI.Pid, ErrMsg, StdoutContents, StderrContents, T->Context, ReturnCode, TPI); ContinueExecution = Response != TaskFinishedResponse::StopExecution; } else { // If we don't have a Signalled callback, unconditionally stop. ContinueExecution = false; } } else { // Wait() returned a normal return code, so just indicate that the task // finished. if (Finished) { TaskFinishedResponse Response = Finished(T->PI.Pid, T->PI.ReturnCode, StdoutContents, StderrContents, TPI, T->Context); ContinueExecution = Response != TaskFinishedResponse::StopExecution; } else if (T->PI.ReturnCode != 0) { ContinueExecution = false; } } llvm::sys::fs::remove(T->StdoutPath); if (T->SeparateErrors) llvm::sys::fs::remove(T->StderrPath); } return !ContinueExecution; }