Coverage Report

Created: 2026-10-13 07:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ninja/src/jobserver.h
Line
Count
Source
1
// Copyright 2024 Google Inc. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#pragma once
16
17
#include <stdint.h>
18
19
#include <memory>
20
#include <string>
21
#include <utility>
22
23
/// Jobserver provides types related to managing a pool of "job slots"
24
/// using the GNU Make jobserver ptocol described at:
25
///
26
/// https://www.gnu.org/software/make/manual/html_node/Job-Slots.html
27
///
28
struct Jobserver {
29
  /// A Jobserver::Slot models a single job slot that can be acquired from.
30
  /// or released to a jobserver pool. This class is move-only, and can
31
  /// wrap three types of values:
32
  ///
33
  /// - An "invalid" value (the default), used to indicate errors, e.g.
34
  ///   that no slot could be acquired from the pool.
35
  ///
36
  /// - The "implicit" value, used to model the job slot that is implicitly
37
  ///   assigned to a jobserver client by the parent process that spawned
38
  ///   it.
39
  ///
40
  /// - The "explicit" values, which correspond to an actual byte read from
41
  ///   the slot pool's pipe (for Posix), or a semaphore decrement operation
42
  ///   (for Windows).
43
  ///
44
  /// Use IsValid(), IsImplicit(), HasValue() to test for categories.
45
  ///
46
  /// TECHNICAL NOTE: This design complies with the requirements laid out
47
  /// on https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html
48
  /// which requires clients to write back the exact token values they
49
  /// received from a Posix pipe.
50
  ///
51
  /// Note that *currently* all pool implementations write the same token
52
  /// values to the pipe ('+' for GNU Make, and '|' for the Rust jobserver),
53
  /// and do not care about the values written back by clients.
54
  ///
55
  struct Slot {
56
    /// Default constructor creates invalid instance.
57
236k
    Slot() = default;
58
59
    /// Move operations are allowed.
60
0
    Slot(Slot&& o) noexcept : value_(o.value_) { o.value_ = -1; }
61
62
0
    Slot& operator=(Slot&& o) noexcept {
63
0
      if (this != &o) {
64
0
        this->value_ = o.value_;
65
0
        o.value_ = -1;
66
0
      }
67
0
      return *this;
68
0
    }
69
70
    /// Copy operations are disallowed.
71
    Slot(const Slot&) = delete;
72
    Slot& operator=(const Slot&) = delete;
73
74
    /// Return true if this instance is valid, i.e. that it is either
75
    /// implicit or explicit job slot.
76
0
    bool IsValid() const { return value_ >= 0; }
77
78
    /// Return true if this instance represents an implicit job slot.
79
0
    bool IsImplicit() const { return value_ == kImplicitValue; }
80
81
    /// Return true if this instance represents an explicit job slot
82
0
    bool IsExplicit() const { return IsValid() && !IsImplicit(); }
83
84
    /// Return value of an explicit slot. It is a runtime error to call
85
    /// this from an invalid instance.
86
    uint8_t GetExplicitValue() const;
87
88
    /// Create instance for explicit byte value.
89
0
    static Slot CreateExplicit(uint8_t value) {
90
0
      return Slot(static_cast<int16_t>(value));
91
0
    }
92
93
    /// Create instance for the implicit value.
94
0
    static Slot CreateImplicit() { return Slot(kImplicitValue); }
95
96
   private:
97
0
    Slot(int16_t value) : value_(value) {}
98
99
    static constexpr int16_t kImplicitValue = 256;
100
101
    int16_t value_ = -1;
102
  };
103
104
  /// A Jobserver::Config models how to access or implement a GNU jobserver
105
  /// implementation.
106
  struct Config {
107
    /// Different implementation modes for the slot pool.
108
    ///
109
    /// kModeNone means there is no pool.
110
    ///
111
    /// kModePipe means that `--jobserver-auth=R,W` is used to
112
    ///    pass a pair of file descriptors to client processes. This also
113
    ///    matches `--jobserver-fds=R,W` which is an old undocumented
114
    ///    variant of the same scheme. This mode is not supported by
115
    ///    Ninja, but recognized by the parser.
116
    ///
117
    /// kModePosixFifo means that `--jobserver-auth=fifo:PATH` is used to
118
    ///    pass the path of a Posix FIFO to client processes. This is not
119
    ///    supported on Windows. Implemented by GNU Make 4.4 and above
120
    ///    when `--jobserver-style=fifo` is used.
121
    ///
122
    /// kModeWin32Semaphore means that `--jobserver-auth=SEMAPHORE_NAME` is
123
    ///    used to pass the name of a Win32 semaphore to client processes.
124
    ///    This is not supported on Posix.
125
    ///
126
    /// kModeDefault is the default mode to enable on the current platform.
127
    ///    This is an alias for kModeWin32Semaphore on Windows ,and
128
    ///    kModePosixFifo on Posix.
129
    enum Mode {
130
      kModeNone = 0,
131
      kModePipe,
132
      kModePosixFifo,
133
      kModeWin32Semaphore,
134
#ifdef _WIN32
135
      kModeDefault = kModeWin32Semaphore,
136
#else   // _WIN32
137
      kModeDefault = kModePosixFifo,
138
#endif  // _WIN32
139
    };
140
141
    /// Implementation mode for the pool.
142
    Mode mode = kModeNone;
143
144
    /// For kModeFifo, this is the path to the Unix FIFO to use.
145
    /// For kModeSemaphore, this is the name of the Win32 semaphore to use.
146
    std::string path;
147
148
    /// Return true if this instance matches an active implementation mode.
149
    /// This does not try to validate configuration parameters though.
150
0
    bool HasMode() { return mode != kModeNone; }
151
  };
152
153
  /// Parse the value of a MAKEFLAGS environment variable. On success return
154
  /// true and set |*config|. On failure, return false and set |*error| to
155
  /// explain what's wrong. If |makeflags_env| is nullptr or an empty string,
156
  /// this returns success and sets |config->mode| to Config::kModeNone.
157
  static bool ParseMakeFlagsValue(const char* makeflags_env, Config* config,
158
                                  std::string* error);
159
160
  /// A variant of ParseMakeFlagsValue() that will return an error if the parsed
161
  /// result is not compatible with the native system. I.e.:
162
  ///
163
  ///   --jobserver-auth=R,W is not supported on any system (but recognized to
164
  ///       provide a relevant error message to the user).
165
  ///
166
  ///   --jobserver-auth=NAME onlw works on Windows.
167
  ///
168
  ///   --jobserver-auth=fifo:PATH only works on Posix.
169
  ///
170
  static bool ParseNativeMakeFlagsValue(const char* makeflags_env,
171
                                        Config* config, std::string* error);
172
173
  /// A Jobserver::Client instance models a client of an external GNU jobserver
174
  /// pool, which can be implemented as a Unix FIFO, or a Windows named
175
  /// semaphore. Usage is the following:
176
  ///
177
  ///  - Call Jobserver::Client::Create(), passing a Config value as argument,
178
  ///    (e.g. one initialized with ParseNativeMakeFlagsValue()) to create
179
  ///    a new instance.
180
  ///
181
  ///  - Call TryAcquire() to try to acquire a job slot from the pool.
182
  ///    If the result is not an invalid slot, store it until the
183
  ///    corresponding command completes, then call Release() to send it
184
  ///    back to the pool.
185
  ///
186
  ///  - It is important that all acquired slots are released to the pool,
187
  ///    even if Ninja terminates early (e.g. due to a build command failing).
188
  ///
189
  class Client {
190
   public:
191
0
    virtual ~Client() {}
192
193
    /// Try to acquire a slot from the pool. On failure, i.e. if no slot
194
    /// can be acquired, this returns an invalid Token instance.
195
    ///
196
    /// Note that this will always return the implicit slot value the first
197
    /// time this is called, without reading anything from the pool, as
198
    /// specified by the protocol. This implicit value *must* be released
199
    /// just like any other one. In general, users of this class should not
200
    /// care about this detail, except unit-tests.
201
0
    virtual Slot TryAcquire() { return Slot(); }
202
203
    /// Release a slot to the pool. Does nothing if slot is invalid,
204
    /// or if writing to the pool fails (and if this is not the implicit slot).
205
    /// If the pool is destroyed before Ninja, then only the implicit slot
206
    /// can be acquired in the next calls (if it was released). This simply
207
    /// enforces serialization of all commands, instead of blocking.
208
0
    virtual void Release(Slot slot) {}
209
210
#ifndef _WIN32
211
    /// Return the file descriptor that can be used to monitor
212
    /// jobserver token availability via ppoll/pselect().
213
0
    virtual int GetJobserverFD() const { return -1; }
214
#endif
215
216
    /// Create a new Client instance from a given configuration. On failure,
217
    /// this returns null after setting |*error|. Note that it is an error to
218
    /// call this function with |config.HasMode() == false|.
219
    static std::unique_ptr<Client> Create(const Config&, std::string* error);
220
221
   protected:
222
    Client() = default;
223
  };
224
};