1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
#pragma once
///@file
#include "result.hh"
#include "types.hh"
#include "store-api.hh"
#include "build-result.hh"
#include <kj/async.h>
namespace nix {
/**
* Forward definition.
*/
struct Goal;
class Worker;
/**
* A pointer to a goal.
*/
typedef std::shared_ptr<Goal> GoalPtr;
typedef std::weak_ptr<Goal> WeakGoalPtr;
struct CompareGoalPtrs {
bool operator() (const GoalPtr & a, const GoalPtr & b) const;
};
/**
* Set of goals.
*/
typedef std::set<GoalPtr, CompareGoalPtrs> Goals;
typedef std::set<WeakGoalPtr, std::owner_less<WeakGoalPtr>> WeakGoals;
/**
* A map of paths to goals (and the other way around).
*/
typedef std::map<StorePath, WeakGoalPtr> WeakGoalMap;
/**
* Used as a hint to the worker on how to schedule a particular goal. For example,
* builds are typically CPU- and memory-bound, while substitutions are I/O bound.
* Using this information, the worker might decide to schedule more or fewer goals
* of each category in parallel.
*/
enum struct JobCategory {
/**
* A build of a derivation; it will use CPU and disk resources.
*/
Build,
/**
* A substitution an arbitrary store object; it will use network resources.
*/
Substitution,
};
struct Goal
{
typedef enum {ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
/**
* Backlink to the worker.
*/
Worker & worker;
/**
* Whether this goal is only a dependency of other goals. Toplevel
* goals that are also dependencies of other toplevel goals do not
* set this, only goals that are exclusively dependencies do this.
*/
const bool isDependency;
/**
* Goals that this goal is waiting for.
*/
Goals waitees;
/**
* Goals waiting for this one to finish. Must use weak pointers
* here to prevent cycles.
*/
WeakGoals waiters;
/**
* Number of goals we are/were waiting for that have failed.
*/
size_t nrFailed = 0;
/**
* Number of substitution goals we are/were waiting for that
* failed because there are no substituters.
*/
size_t nrNoSubstituters = 0;
/**
* Number of substitution goals we are/were waiting for that
* failed because they had unsubstitutable references.
*/
size_t nrIncompleteClosure = 0;
/**
* Name of this goal for debugging purposes.
*/
std::string name;
/**
* Whether the goal is finished.
*/
std::optional<ExitCode> exitCode;
/**
* Build result.
*/
BuildResult buildResult;
public:
struct Finished;
struct [[nodiscard]] StillAlive {};
struct [[nodiscard]] WaitForSlot {};
struct [[nodiscard]] WaitForAWhile {};
struct [[nodiscard]] ContinueImmediately {};
struct [[nodiscard]] WaitForGoals {
Goals goals;
};
struct [[nodiscard]] WaitForWorld {
kj::Promise<Outcome<void, Finished>> promise;
bool inBuildSlot;
};
struct [[nodiscard]] Finished {
ExitCode exitCode;
BuildResult result;
std::shared_ptr<Error> ex;
bool permanentFailure = false;
bool timedOut = false;
bool hashMismatch = false;
bool checkMismatch = false;
};
struct [[nodiscard]] WorkResult : std::variant<
StillAlive,
WaitForSlot,
WaitForAWhile,
ContinueImmediately,
WaitForGoals,
WaitForWorld,
Finished>
{
WorkResult() = delete;
using variant::variant;
};
/**
* Exception containing an error message, if any.
*/
std::shared_ptr<Error> ex;
explicit Goal(Worker & worker, bool isDependency)
: worker(worker)
, isDependency(isDependency)
{ }
virtual ~Goal() noexcept(false)
{
trace("goal destroyed");
}
virtual kj::Promise<Result<WorkResult>> work(bool inBuildSlot) noexcept = 0;
virtual void waiteeDone(GoalPtr waitee) { }
void trace(std::string_view s);
std::string getName() const
{
return name;
}
virtual std::string key() = 0;
virtual void cleanup() { }
/**
* @brief Hint for the scheduler, which concurrency limit applies.
* @see JobCategory
*/
virtual JobCategory jobCategory() const = 0;
};
}
|