blob: 30b139785ccf104e5511287446565b30e531ee3c [file] [log] [blame]
//===--- TaskQueue.h - Task Execution Work Queue ----------------*- 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_BASIC_TASKQUEUE_H
#define SWIFT_BASIC_TASKQUEUE_H
#include "swift/Basic/JSONSerialization.h"
#include "swift/Basic/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Config/config.h"
#include "llvm/Support/Program.h"
#include <functional>
#include <memory>
#include <queue>
#if defined(HAVE_GETRUSAGE) && !defined(__HAIKU__)
struct rusage;
#endif
namespace swift {
class UnifiedStatsReporter;
namespace sys {
class Task; // forward declared to allow for platform-specific implementations
using ProcessId = llvm::sys::procid_t;
/// Indicates how a TaskQueue should respond to the task finished event.
enum class TaskFinishedResponse {
/// Indicates that execution should continue.
ContinueExecution,
/// Indicates that execution should stop (no new tasks will begin execution,
/// but tasks which are currently executing will be allowed to finish).
StopExecution,
};
/// TaskProcessInformation is bound to a task and contains information about the
/// process that ran this task. This is especially useful to find out which
/// tasks ran in the same process (in multifile-mode or when WMO is activated
/// e.g.). If available, it also contains information about the usage of
/// resources like CPU time or memory the process used in the system. How ever,
/// this could differ from platform to platform and is therefore optional.
/// One process could handle multiple tasks in some modes of the Swift compiler
/// (multifile, WMO). To not break existing tools, the driver does use unique
/// identifiers for the tasks that are not the process identifier. To still be
/// able to reason about tasks that ran in the same process the
/// TaskProcessInformation struct contains information about the actual process
/// of the operating system. The OSPid is the actual process identifier and is
/// therefore not guaranteed to be unique over all tasks. The ProcessUsage
/// contains optional usage information about the operating system process. It
/// could be used by tools that take those information as input for analyzing
/// the Swift compiler on a process-level. It will be `None` if the execution
/// has been skipped or one of the following symbols are not available on the
/// system: `rusage`, `wait4`.
struct TaskProcessInformation {
struct ResourceUsage {
// user time in µs
uint64_t Utime;
// system time in µs
uint64_t Stime;
// maximum resident set size in Bytes
uint64_t Maxrss;
ResourceUsage(uint64_t Utime, uint64_t Stime, uint64_t Maxrss)
: Utime(Utime), Stime(Stime), Maxrss(Maxrss) {}
virtual ~ResourceUsage() = default;
virtual void provideMapping(json::Output &out);
};
private:
// the process identifier of the operating system
ProcessId OSPid;
// usage information about the process, if available
Optional<ResourceUsage> ProcessUsage;
public:
TaskProcessInformation(ProcessId Pid, uint64_t utime, uint64_t stime,
uint64_t maxrss)
: OSPid(Pid), ProcessUsage(ResourceUsage(utime, stime, maxrss)) {}
TaskProcessInformation(ProcessId Pid) : OSPid(Pid), ProcessUsage(None) {}
#if defined(HAVE_GETRUSAGE) && !defined(__HAIKU__)
TaskProcessInformation(ProcessId Pid, struct rusage Usage);
#endif // defined(HAVE_GETRUSAGE) && !defined(__HAIKU__)
Optional<ResourceUsage> getResourceUsage() { return ProcessUsage; }
virtual ~TaskProcessInformation() = default;
virtual void provideMapping(json::Output &out);
};
/// A class encapsulating the execution of multiple tasks in parallel.
class TaskQueue {
/// Tasks which have not begun execution.
std::queue<std::unique_ptr<Task>> QueuedTasks;
/// The number of tasks to execute in parallel.
unsigned NumberOfParallelTasks;
/// Optional place to count I/O and subprocess events.
UnifiedStatsReporter *Stats;
public:
/// Create a new TaskQueue instance.
///
/// \param NumberOfParallelTasks indicates the number of tasks which should
/// be run in parallel. If 0, the TaskQueue will choose the most appropriate
/// number of parallel tasks for the current system.
/// \param USR Optional stats reporter to count I/O and subprocess events.
TaskQueue(unsigned NumberOfParallelTasks = 0,
UnifiedStatsReporter *USR = nullptr);
virtual ~TaskQueue();
// TODO: remove once -Wdocumentation stops warning for \param, \returns on
// std::function (<rdar://problem/15665132>).
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"
/// A callback which will be executed when each task begins execution
///
/// \param Pid the ProcessId of the task which just began execution.
/// \param Context the context which was passed when the task was added
using TaskBeganCallback = std::function<void(ProcessId Pid, void *Context)>;
/// A callback which will be executed after each task finishes
/// execution.
///
/// \param Pid the ProcessId of the task which finished execution.
/// \param ReturnCode the return code of the task which finished execution.
/// \param Output the output from the task which finished execution,
/// if available. (This may not be available on all platforms.)
/// \param Errors the errors from the task which finished execution, if
/// available and SeparateErrors was true. (This may not be available on all
/// platforms.)
/// \param ProcInfo contains information like the operating process identifier
/// and resource usage if available
/// \param Context the context which was passed when the task was added
///
/// \returns true if further execution of tasks should stop,
/// false if execution should continue
using TaskFinishedCallback = std::function<TaskFinishedResponse(
ProcessId Pid, int ReturnCode, StringRef Output, StringRef Errors,
TaskProcessInformation ProcInfo, void *Context)>;
/// A callback which will be executed if a task exited abnormally due
/// to a signal.
///
/// \param Pid the ProcessId of the task which exited abnormally.
/// \param ErrorMsg a string describing why the task exited abnormally. If
/// no reason could be deduced, this may be empty.
/// \param Output the output from the task which exited abnormally, if
/// available. (This may not be available on all platforms.)
/// \param Errors the errors from the task which exited abnormally, if
/// available and SeparateErrors was true. (This may not be available on all
/// platforms.)
/// \param ProcInfo contains information like the operating process identifier
/// and resource usage if available
/// \param Context the context which was passed when the task was added
/// \param Signal the terminating signal number, if available. This may not be
/// available on all platforms. If it is ever provided, it should not be
/// removed in future versions of the compiler.
///
/// \returns a TaskFinishedResponse indicating whether or not execution
/// should proceed
using TaskSignalledCallback = std::function<TaskFinishedResponse(
ProcessId Pid, StringRef ErrorMsg, StringRef Output, StringRef Errors,
void *Context, Optional<int> Signal, TaskProcessInformation ProcInfo)>;
#pragma clang diagnostic pop
/// Indicates whether TaskQueue supports buffering output on the
/// current system.
///
/// \note If this returns false, the TaskFinishedCallback passed
/// to \ref execute will always receive an empty StringRef for output, even
/// if the task actually generated output.
static bool supportsBufferingOutput();
/// Indicates whether TaskQueue supports parallel execution on the
/// current system.
static bool supportsParallelExecution();
/// \returns the maximum number of tasks which this TaskQueue will execute in
/// parallel
unsigned getNumberOfParallelTasks() const;
/// Adds a task to the TaskQueue.
///
/// \param ExecPath the path to the executable which the task should execute
/// \param Args the arguments which should be passed to the task
/// \param Env the environment which should be used for the task;
/// must be null-terminated. If empty, inherits the parent's environment.
/// \param Context an optional context which will be associated with the task
/// \param SeparateErrors Controls whether error output is reported separately
virtual void addTask(const char *ExecPath, ArrayRef<const char *> Args,
ArrayRef<const char *> Env = llvm::None,
void *Context = nullptr, bool SeparateErrors = false);
/// Synchronously executes the tasks in the TaskQueue.
///
/// \param Began a callback which will be called when a task begins
/// \param Finished a callback which will be called when a task finishes
/// \param Signalled a callback which will be called if a task exited
/// abnormally due to a signal
///
/// \returns true if all tasks did not execute successfully
virtual bool
execute(TaskBeganCallback Began = TaskBeganCallback(),
TaskFinishedCallback Finished = TaskFinishedCallback(),
TaskSignalledCallback Signalled = TaskSignalledCallback());
/// Returns true if there are any tasks that have been queued but have not
/// yet been executed.
virtual bool hasRemainingTasks() {
return !QueuedTasks.empty();
}
};
/// A class which simulates execution of tasks with behavior similar to
/// TaskQueue.
class DummyTaskQueue : public TaskQueue {
class DummyTask {
public:
const char *ExecPath;
ArrayRef<const char *> Args;
ArrayRef<const char *> Env;
void *Context;
bool SeparateErrors;
DummyTask(const char *ExecPath, ArrayRef<const char *> Args,
ArrayRef<const char *> Env = llvm::None, void *Context = nullptr,
bool SeparateErrors = false)
: ExecPath(ExecPath), Args(Args), Env(Env), Context(Context),
SeparateErrors(SeparateErrors) {}
};
std::queue<std::unique_ptr<DummyTask>> QueuedTasks;
public:
/// Create a new DummyTaskQueue instance.
DummyTaskQueue(unsigned NumberOfParallelTasks = 0);
virtual ~DummyTaskQueue();
void addTask(const char *ExecPath, ArrayRef<const char *> Args,
ArrayRef<const char *> Env = llvm::None,
void *Context = nullptr, bool SeparateErrors = false) override;
bool
execute(TaskBeganCallback Began = TaskBeganCallback(),
TaskFinishedCallback Finished = TaskFinishedCallback(),
TaskSignalledCallback Signalled = TaskSignalledCallback()) override;
bool hasRemainingTasks() override {
// Need to override here because QueuedTasks is redeclared.
return !QueuedTasks.empty();
}
};
} // end namespace sys
namespace json {
template <> struct ObjectTraits<sys::TaskProcessInformation> {
static void mapping(Output &out, sys::TaskProcessInformation &value) {
value.provideMapping(out);
}
};
template <> struct ObjectTraits<sys::TaskProcessInformation::ResourceUsage> {
static void mapping(Output &out,
sys::TaskProcessInformation::ResourceUsage &value) {
value.provideMapping(out);
}
};
} // end namespace json
} // end namespace swift
#endif // SWIFT_BASIC_TASKQUEUE_H