1 //===- BuildExecutionQueue.h ------------------------------------*- C++ -*-===//
2 //
3 // This source file is part of the Swift.org open source project
4 //
5 // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
6 // Licensed under Apache License v2.0 with Runtime Library Exception
7 //
8 // See http://swift.org/LICENSE.txt for license information
9 // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef LLBUILD_BUILDSYSTEM_BUILDEXECUTIONQUEUE_H
14 #define LLBUILD_BUILDSYSTEM_BUILDEXECUTIONQUEUE_H
15 
16 #include "llbuild/Basic/Compiler.h"
17 #include "llbuild/Basic/LLVM.h"
18 
19 #include "llvm/ADT/StringRef.h"
20 
21 #include <cstdint>
22 #include <functional>
23 
24 namespace llbuild {
25 namespace buildsystem {
26 
27 class BuildExecutionQueueDelegate;
28 class Command;
29 enum class CommandResult;
30 
31 /// Opaque type which allows the queue implementation to maintain additional
32 /// state and associate subsequent requests (e.g., \see executeProcess()) with
33 /// the dispatching job.
34 struct QueueJobContext;
35 
36 /// Wrapper for individual pieces of work that are added to the execution queue.
37 ///
38 /// All work on the exeuction queue is always done on behalf of some command in
39 /// the build system it is executing.
40 class QueueJob {
41   /// The command this job is running on behalf of.
42   Command* forCommand = nullptr;
43 
44   /// The function to execute to do the work.
45   typedef std::function<void(QueueJobContext*)> work_fn_ty;
46   work_fn_ty work;
47 
48 public:
49   /// Default constructor, for use as a sentinel.
QueueJob()50   QueueJob() {}
51 
52   /// General constructor.
QueueJob(Command * forCommand,work_fn_ty work)53   QueueJob(Command* forCommand, work_fn_ty work)
54       : forCommand(forCommand), work(work) {}
55 
getForCommand()56   Command* getForCommand() const { return forCommand; }
57 
execute(QueueJobContext * context)58   void execute(QueueJobContext* context) { work(context); }
59 };
60 
61 /// This abstact class encapsulates the interface needed by the build system for
62 /// contributing work which needs to be executed to perform a particular build.
63 class BuildExecutionQueue {
64   // DO NOT COPY
65   BuildExecutionQueue(const BuildExecutionQueue&)
66     LLBUILD_DELETED_FUNCTION;
67   void operator=(const BuildExecutionQueue&)
68     LLBUILD_DELETED_FUNCTION;
69   BuildExecutionQueue &operator=(BuildExecutionQueue&& rhs)
70     LLBUILD_DELETED_FUNCTION;
71 
72   BuildExecutionQueueDelegate& delegate;
73 
74 public:
75   BuildExecutionQueue(BuildExecutionQueueDelegate& delegate);
76   virtual ~BuildExecutionQueue();
77 
78   /// @name Accessors
79   /// @{
80 
getDelegate()81   BuildExecutionQueueDelegate& getDelegate() { return delegate; }
getDelegate()82   const BuildExecutionQueueDelegate& getDelegate() const { return delegate; }
83 
84   /// @}
85 
86   /// Add a job to be executed.
87   virtual void addJob(QueueJob job) = 0;
88 
89   /// Cancel all jobs and subprocesses of this queue.
90   virtual void cancelAllJobs() = 0;
91 
92   /// @name Execution Interfaces
93   ///
94   /// These are additional interfaces provided by the execution queue which can
95   /// be invoked by the individual \see QueueJob::execute() to perform
96   /// particular actions. The benefit of delegating to the execution queue to
97   /// perform these actions is that the queue can potentially do a better job of
98   /// scheduling activities.
99   ///
100   /// @{
101 
102   /// Execute the given command line.
103   ///
104   /// This will launch and execute the given command line and wait for it to
105   /// complete.
106   ///
107   /// \param context The context object passed to the job's worker function.
108   ///
109   /// \param commandLine The command line to execute.
110   ///
111   /// \param environment The environment to launch with.
112   ///
113   /// \param inheritEnvironment If true, the supplied environment will be
114   /// overlayed on top base environment supplied when creating the queue. If
115   /// false, only the supplied environment will be passed to the subprocess.
116   ///
117   /// \returns Result of the process execution.
118   //
119   // FIXME: This interface will need to get more complicated, and provide the
120   // command result and facilities for dealing with the output.
121   virtual CommandResult
122   executeProcess(QueueJobContext* context,
123                  ArrayRef<StringRef> commandLine,
124                  ArrayRef<std::pair<StringRef, StringRef>> environment,
125                  bool inheritEnvironment = true) = 0;
126 
127   /// @}
128 
129   /// Execute the given command, using an inherited environment.
130   CommandResult executeProcess(QueueJobContext* context,
131                       ArrayRef<StringRef> commandLine);
132 
133   /// Execute the given command using "/bin/sh".
134   ///
135   /// This will launch and execute the given command line and wait for it to
136   /// complete.
137   ///
138   /// \param context The context object passed to the job's worker function.
139   /// \param command The command to execute.
140   /// \returns True on success.
141   //
142   // FIXME: This interface will need to get more complicated, and provide the
143   // command result and facilities for dealing with the output.
144   bool executeShellCommand(QueueJobContext* context, StringRef command);
145 
146 };
147 
148 /// Delegate interface for execution queue status.
149 ///
150 /// All delegate interfaces are invoked synchronously by the execution queue,
151 /// and should defer any long running operations to avoid blocking the queue
152 /// unnecessarily.
153 ///
154 /// NOTE: The delegate *MUST* be thread-safe with respect to all calls, which
155 /// will arrive concurrently and without any specified thread.
156 class BuildExecutionQueueDelegate {
157   // DO NOT COPY
158   BuildExecutionQueueDelegate(const BuildExecutionQueueDelegate&)
159     LLBUILD_DELETED_FUNCTION;
160   void operator=(const BuildExecutionQueueDelegate&)
161     LLBUILD_DELETED_FUNCTION;
162   BuildExecutionQueueDelegate &operator=(BuildExecutionQueueDelegate&& rhs)
163     LLBUILD_DELETED_FUNCTION;
164 
165 public:
166   /// Handle used to communicate information about a launched process.
167   struct ProcessHandle {
168     /// Opaque ID.
169     uintptr_t id;
170   };
171 
172 public:
BuildExecutionQueueDelegate()173   BuildExecutionQueueDelegate() {}
174   virtual ~BuildExecutionQueueDelegate();
175 
176   /// Called when a command's job has been started.
177   ///
178   /// The queue guarantees that any commandStarted() call will be paired with
179   /// exactly one \see commandFinished() call.
180   //
181   // FIXME: We may eventually want to allow the individual job to provide some
182   // additional context here, for complex commands.
183   //
184   // FIXME: Design a way to communicate the "lane" here, for use in "super
185   // console" like UIs.
186   virtual void commandJobStarted(Command*) = 0;
187 
188   /// Called when a command's job has been finished.
189   ///
190   /// NOTE: This callback is invoked by the quee without any understanding of
191   /// how the command is tied to the engine. In particular, it is almost always
192   /// the case that the command will have already completed from the perspective
193   /// of the low-level engine (and its dependents may have started
194   /// executing). Clients which want to understand when a command is complete
195   /// before the engine has been notified as such should use \see
196   /// BuildSystem::commandFinished().
197   virtual void commandJobFinished(Command*) = 0;
198 
199   /// Called when a command's job has started executing an external process.
200   ///
201   /// The queue guarantees that any commandProcessStarted() call will be paired
202   /// with exactly one \see commandProcessFinished() call.
203   ///
204   /// \param handle - A unique handle used in subsequent delegate calls to
205   /// identify the process. This handle should only be used to associate
206   /// different status calls relating to the same process. It is only guaranteed
207   /// to be unique from when it has been provided here to when it has been
208   /// provided to the \see commandProcessFinished() call.
209   virtual void commandProcessStarted(Command*, ProcessHandle handle) = 0;
210 
211   /// Called to report an error in the management of a command process.
212   ///
213   /// \param handle - The process handle.
214   /// \param message - The error message.
215   //
216   // FIXME: Need to move to more structured error handling.
217   virtual void commandProcessHadError(Command*, ProcessHandle handle,
218                                       const Twine& message) = 0;
219 
220   /// Called to report a command processes' (merged) standard output and error.
221   ///
222   /// \param handle - The process handle.
223   /// \param data - The process output.
224   virtual void commandProcessHadOutput(Command*, ProcessHandle handle,
225                                        StringRef data) = 0;
226 
227   /// Called when a command's job has finished executing an external process.
228   ///
229   /// \param handle - The handle used to identify the process. This handle will
230   /// become invalid as soon as the client returns from this API call.
231   ///
232   /// \param result - Whether the process suceeded, failed or was cancelled.
233   /// \param exitStatus - The raw exit status of the process, or -1 if an error
234   /// was encountered.
235   //
236   // FIXME: Need to include additional information on the status here, e.g., the
237   // signal status, and the process output (if buffering).
238   virtual void commandProcessFinished(Command*, ProcessHandle handle,
239                                       CommandResult result,
240                                       int exitStatus) = 0;
241 };
242 
243 /// Create an execution queue that schedules jobs to individual lanes with a
244 /// capped limit on the number of concurrent lanes.
245 BuildExecutionQueue *createLaneBasedExecutionQueue(
246     BuildExecutionQueueDelegate& delegate, int numLanes,
247     const char* const* environment);
248 
249 }
250 }
251 
252 #endif
253