mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
* spelling: add Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: attributes Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: bridging Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: deserialization Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: initialize Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: invariants Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: lazily Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: occurred Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: offset Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: optimization Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: our Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: process Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: substitution Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: the operation Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: the Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> Co-authored-by: Josh Soref <jsoref@users.noreply.github.com>
209 lines
7.1 KiB
C++
Executable File
209 lines
7.1 KiB
C++
Executable File
//===--- TaskQueue.inc ------------------------------------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2019 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 an implementation of TaskQueue for Windows platform.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/Basic/TaskQueue.h"
|
|
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Signals.h"
|
|
|
|
#define NOMINMAX
|
|
#include <Windows.h>
|
|
#include <psapi.h>
|
|
|
|
#include "Default/Task.inc"
|
|
|
|
using namespace llvm::sys;
|
|
|
|
namespace {
|
|
|
|
class TaskMonitor {
|
|
std::queue<std::unique_ptr<Task>> &TasksToBeExecuted;
|
|
llvm::SmallVector<std::unique_ptr<Task>, 32> TasksBeingExecuted;
|
|
const unsigned MaxNumberOfParallelTasks;
|
|
|
|
public:
|
|
struct Callbacks {
|
|
const TaskQueue::TaskBeganCallback TaskBegan;
|
|
const TaskQueue::TaskFinishedCallback TaskFinished;
|
|
const TaskQueue::TaskSignalledCallback TaskSignalled;
|
|
};
|
|
|
|
private:
|
|
Callbacks Cbs;
|
|
|
|
bool startUpSomeTasks() {
|
|
while (!TasksToBeExecuted.empty() &&
|
|
TasksBeingExecuted.size() < MaxNumberOfParallelTasks) {
|
|
std::unique_ptr<Task> T(std::move(TasksToBeExecuted.front()));
|
|
TasksToBeExecuted.pop();
|
|
if (T->execute())
|
|
return true;
|
|
if (Cbs.TaskBegan)
|
|
Cbs.TaskBegan(T->PI.Pid, T->Context);
|
|
TasksBeingExecuted.push_back(std::move(T));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool waitForFinishedTask() {
|
|
if (TasksBeingExecuted.empty())
|
|
return false;
|
|
llvm::SmallVector<HANDLE, 32> Handles;
|
|
for (auto &TBE : TasksBeingExecuted)
|
|
Handles.push_back((HANDLE)TBE->PI.Process);
|
|
|
|
DWORD Finished =
|
|
WaitForMultipleObjects(Handles.size(), Handles.data(), FALSE, INFINITE);
|
|
if (Finished < WAIT_OBJECT_0 || Finished >= WAIT_OBJECT_0 + Handles.size())
|
|
return true;
|
|
size_t Index = Finished - WAIT_OBJECT_0;
|
|
if (Index + 1 != TasksBeingExecuted.size())
|
|
std::swap(TasksBeingExecuted[Index], TasksBeingExecuted.back());
|
|
|
|
std::unique_ptr<Task> T(std::move(TasksBeingExecuted.back()));
|
|
TasksBeingExecuted.pop_back();
|
|
|
|
DWORD Status = 0;
|
|
BOOL RC = GetExitCodeProcess(T->PI.Process, &Status);
|
|
DWORD Err = GetLastError();
|
|
if (Err != ERROR_INVALID_HANDLE)
|
|
CloseHandle(T->PI.Process);
|
|
if (!RC) {
|
|
SetLastError(Err);
|
|
// -2 indicates a crash or timeout as opposed to failure to execute.
|
|
// See the comments on llvm::sys::Wait for more details.
|
|
T->PI.ReturnCode = -2;
|
|
}
|
|
// Convert the return code in the same way as llvm::sys::Wait does.
|
|
if (Status != 0) {
|
|
// Pass 10(Warning) and 11(Error) to the callee as negative value.
|
|
// For more information on status codes, see
|
|
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/87fba13e-bf06-450e-83b1-9241dc81e781
|
|
if ((Status & 0xBFFF0000U) == 0x80000000U)
|
|
T->PI.ReturnCode = static_cast<int>(Status);
|
|
else if (Status & 0xFF)
|
|
T->PI.ReturnCode = Status & 0x7FFFFFFF;
|
|
else
|
|
T->PI.ReturnCode = 1;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
bool Crashed = T->PI.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);
|
|
bool ErrorOccurred = false;
|
|
if (Crashed) {
|
|
if (Cbs.TaskSignalled) {
|
|
TaskFinishedResponse Response =
|
|
Cbs.TaskSignalled(T->PI.Pid, "", StdoutContents, StderrContents,
|
|
T->Context, T->PI.ReturnCode, TPI);
|
|
ErrorOccurred = (Response == TaskFinishedResponse::StopExecution);
|
|
} else {
|
|
// If we don't have a Signalled callback, unconditionally stop.
|
|
ErrorOccurred = true;
|
|
}
|
|
} else {
|
|
// Wait() returned a normal return code, so just indicate that the task
|
|
// finished.
|
|
if (Cbs.TaskFinished) {
|
|
TaskFinishedResponse Response =
|
|
Cbs.TaskFinished(T->PI.Pid, T->PI.ReturnCode, StdoutContents,
|
|
StderrContents, TPI, T->Context);
|
|
ErrorOccurred = (Response == TaskFinishedResponse::StopExecution);
|
|
} else if (Crashed) {
|
|
ErrorOccurred = true;
|
|
}
|
|
}
|
|
llvm::sys::fs::remove(T->StdoutPath);
|
|
if (T->SeparateErrors)
|
|
llvm::sys::fs::remove(T->StderrPath);
|
|
return ErrorOccurred;
|
|
}
|
|
|
|
public:
|
|
TaskMonitor(std::queue<std::unique_ptr<Task>> &TasksToBeExecuted,
|
|
unsigned NumberOfParallelTasks, Callbacks Cbs)
|
|
: TasksToBeExecuted(TasksToBeExecuted),
|
|
MaxNumberOfParallelTasks(std::max(NumberOfParallelTasks, 1u)),
|
|
Cbs(std::move(Cbs)) {}
|
|
|
|
/// Run the tasks to be executed.
|
|
/// \return true on error.
|
|
bool executeTasks() {
|
|
bool ErrorOccurred = false;
|
|
while ((!ErrorOccurred && !TasksToBeExecuted.empty()) ||
|
|
!TasksBeingExecuted.empty()) {
|
|
if (!ErrorOccurred)
|
|
ErrorOccurred |= startUpSomeTasks();
|
|
ErrorOccurred |= waitForFinishedTask();
|
|
}
|
|
return ErrorOccurred;
|
|
}
|
|
};
|
|
|
|
} // end namespace
|
|
|
|
bool TaskQueue::supportsBufferingOutput() { return true; }
|
|
|
|
bool TaskQueue::supportsParallelExecution() { return true; }
|
|
|
|
unsigned TaskQueue::getNumberOfParallelTasks() const {
|
|
return NumberOfParallelTasks;
|
|
}
|
|
|
|
void TaskQueue::addTask(const char *ExecPath, ArrayRef<const char *> Args,
|
|
ArrayRef<const char *> Env, void *Context,
|
|
bool SeparateErrors) {
|
|
auto T = std::make_unique<Task>(ExecPath, Args, Env, Context, SeparateErrors);
|
|
QueuedTasks.push(std::move(T));
|
|
}
|
|
|
|
bool TaskQueue::execute(TaskBeganCallback Began, TaskFinishedCallback Finished,
|
|
TaskSignalledCallback Signalled) {
|
|
TaskMonitor::Callbacks Cbs{std::move(Began), std::move(Finished),
|
|
std::move(Signalled)};
|
|
TaskMonitor TM(QueuedTasks, NumberOfParallelTasks, std::move(Cbs));
|
|
return TM.executeTasks();
|
|
}
|