aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/build/goal.hh
blob: 17c3d85dbe1eab6c1c5ec9c7ee31bcf3fb93fb0b (plain)
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
#pragma once
///@file

#include "async-semaphore.hh"
#include "result.hh"
#include "types.hh"
#include "store-api.hh"
#include "build-result.hh"
#include <concepts> // IWYU pragma: keep
#include <kj/async.h>

namespace nix {

/**
 * Forward definition.
 */
struct Goal;
class Worker;

/**
 * A pointer to a goal.
 */
typedef std::shared_ptr<Goal> GoalPtr;

/**
 * Set of goals.
 */
typedef std::set<GoalPtr> Goals;

/**
 * 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;

    /**
     * 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;

    // for use by Worker only. will go away once work() is a promise.
    kj::Own<kj::PromiseFulfiller<void>> notify;

protected:
    AsyncSemaphore::Token slotToken;

public:

    struct Finished;

    struct [[nodiscard]] StillAlive {};
    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,
                                          Finished>
    {
        WorkResult() = delete;
        using variant::variant;
    };

protected:
    kj::Promise<Result<WorkResult>> waitForAWhile();
    kj::Promise<Result<WorkResult>>
    waitForGoals(kj::Array<std::pair<GoalPtr, kj::Promise<void>>> dependencies) noexcept;

    template<std::derived_from<Goal>... G>
    kj::Promise<Result<Goal::WorkResult>>
    waitForGoals(std::pair<std::shared_ptr<G>, kj::Promise<void>>... goals) noexcept
    {
        return waitForGoals(kj::arrOf<std::pair<GoalPtr, kj::Promise<void>>>(std::move(goals)...));
    }

public:

    /**
     * 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() noexcept = 0;

    virtual void waiteeDone(GoalPtr waitee) { }

    void trace(std::string_view s);

    std::string getName() const
    {
        return name;
    }

    virtual void cleanup() { }

    /**
     * @brief Hint for the scheduler, which concurrency limit applies.
     * @see JobCategory
     */
    virtual JobCategory jobCategory() const = 0;
};

}