LLVM 22.0.0git
Jobserver.h
Go to the documentation of this file.
1//===- llvm/Support/Jobserver.h - Jobserver Client --------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines a client for the GNU Make jobserver protocol. This allows
10// LLVM tools to coordinate parallel execution with a parent `make` process.
11//
12// The jobserver protocol is a mechanism for GNU Make to share its pool of
13// available "job slots" with the subprocesses it invokes. This is particularly
14// useful for tools that can perform parallel operations themselves (e.g., a
15// multi-threaded linker or compiler). By participating in this protocol, a
16// tool can ensure the total number of concurrent jobs does not exceed the
17// limit specified by the user (e.g., `make -j8`).
18//
19// How it works:
20//
21// 1. Establishment:
22// A child process discovers the jobserver by inspecting the `MAKEFLAGS`
23// environment variable. If a jobserver is active, this variable will
24// contain a `--jobserver-auth=<value>` argument. The format of `<value>`
25// determines how to communicate with the server.
26//
27// 2. The Implicit Slot:
28// Every command invoked by `make` is granted one "implicit" job slot. This
29// means a tool can always perform at least one unit of work without needing
30// to communicate with the jobserver. This implicit slot should NEVER be
31// released back to the jobserver.
32//
33// 3. Acquiring and Releasing Slots:
34// On POSIX systems, the jobserver is implemented as a pipe. The
35// `--jobserver-auth` value specifies either a path to a named pipe
36// (`fifo:PATH`) or a pair of file descriptors (`R,W`). The pipe is
37// pre-loaded with single-character tokens, one for each available job slot.
38//
39// - To acquire an additional slot, a client reads a single-character token
40// from the pipe.
41// - To release a slot, the client must write the *exact same* character
42// token back to the pipe.
43//
44// It is critical that a client releases all acquired slots before it exits,
45// even in cases of error, to avoid deadlocking the build.
46//
47// Example:
48// A multi-threaded linker invoked by `make -j8` wants to use multiple
49// threads. It first checks for the jobserver. It knows it has one implicit
50// slot, so it can use one thread. It then tries to acquire 7 more slots by
51// reading 7 tokens from the jobserver pipe. If it only receives 3 tokens,
52// it knows it can use a total of 1 (implicit) + 3 (acquired) = 4 threads.
53// Before exiting, it must write the 3 tokens it read back to the pipe.
54//
55// For more context, see:
56// - GNU Make manual on job slots:
57// https://www.gnu.org/software/make/manual/html_node/Job-Slots.html
58// - LLVM RFC discussion on jobserver support:
59// https://discourse.llvm.org/t/rfc-adding-gnu-make-jobserver-
60// support-to-llvm-for-coordinated-parallelism/87034
61// - Ninja’s jobserver support PR:
62// https://github.com/ninja-build/ninja/pull/2506
63//
64//===----------------------------------------------------------------------===//
65
66#ifndef LLVM_SUPPORT_JOBSERVER_H
67#define LLVM_SUPPORT_JOBSERVER_H
68
69#include "llvm/ADT/StringRef.h"
70
71namespace llvm {
72
73/// A JobSlot represents a single job slot that can be acquired from or released
74/// to a jobserver pool. This class is move-only.
75class JobSlot {
76public:
77 /// Default constructor creates an invalid instance.
78 JobSlot() = default;
79
80 // Move operations are allowed.
81 JobSlot(JobSlot &&Other) noexcept : Value(Other.Value) {
82 Other.Value = kInvalidValue;
83 }
85 if (this != &Other) {
86 this->Value = Other.Value;
87 Other.Value = kInvalidValue;
88 }
89 return *this;
90 }
91
92 // Copy operations are disallowed.
93 JobSlot(const JobSlot &) = delete;
94 JobSlot &operator=(const JobSlot &) = delete;
95
96 /// Returns true if this instance is valid (either implicit or explicit).
97 bool isValid() const { return Value >= 0; }
98
99 /// Returns true if this instance represents the implicit job slot.
100 bool isImplicit() const { return Value == kImplicitValue; }
101
103 return JobSlot(static_cast<int16_t>(V));
104 }
105
106 static JobSlot createImplicit() { return JobSlot(kImplicitValue); }
107
109 bool isExplicit() const { return isValid() && !isImplicit(); }
110
111private:
112 friend class JobserverClient;
114
115 JobSlot(int16_t V) : Value(V) {}
116
117 /// The jobserver pipe carries explicit tokens (bytes 0–255). We reserve two
118 /// sentinels in Value for special cases:
119 /// kInvalidValue (-1): no slot held
120 /// kImplicitValue (INT16_MAX): implicit slot granted at startup (no pipe
121 /// I/O)
122 ///
123 /// We use int16_t so Value can store 0–255 explicit tokens and
124 /// sentinels without overflow, enforces fixed 16-bit width, and avoids
125 /// unsigned/signed mix-ups.
126 static constexpr int16_t kInvalidValue = -1;
127 static constexpr int16_t kImplicitValue = INT16_MAX;
128 int16_t Value = kInvalidValue;
129};
130
131/// The public interface for a jobserver client.
132/// This client is a lazy-initialized singleton that is created on first use.
134public:
136
137 /// Tries to acquire a job slot from the pool. On failure (e.g., if the pool
138 /// is empty), this returns an invalid JobSlot instance. The first successful
139 /// call will always return the implicit slot.
140 virtual JobSlot tryAcquire() = 0;
141
142 /// Releases a job slot back to the pool.
143 virtual void release(JobSlot Slot) = 0;
144
145 /// Returns the number of job slots available, as determined on first use.
146 /// This value is cached. Returns 0 if no jobserver is active.
147 virtual unsigned getNumJobs() const = 0;
148
149 /// Returns the singleton instance of the JobserverClient.
150 /// The instance is created on the first call to this function.
151 /// Returns a nullptr if no jobserver is configured or an error occurs.
153
154 /// Resets the singleton instance. For testing purposes only.
156};
157
158} // end namespace llvm
159
160#endif // LLVM_SUPPORT_JOBSERVER_H
#define LLVM_ABI_FOR_TEST
Definition Compiler.h:218
A JobSlot represents a single job slot that can be acquired from or released to a jobserver pool.
Definition Jobserver.h:75
static JobSlot createExplicit(uint8_t V)
Definition Jobserver.h:102
JobSlot()=default
Default constructor creates an invalid instance.
JobSlot & operator=(const JobSlot &)=delete
uint8_t getExplicitValue() const
JobSlot(JobSlot &&Other) noexcept
Definition Jobserver.h:81
bool isImplicit() const
Returns true if this instance represents the implicit job slot.
Definition Jobserver.h:100
JobSlot & operator=(JobSlot &&Other) noexcept
Definition Jobserver.h:84
bool isExplicit() const
Definition Jobserver.h:109
bool isValid() const
Returns true if this instance is valid (either implicit or explicit).
Definition Jobserver.h:97
friend class JobserverClient
Definition Jobserver.h:112
friend class JobserverClientImpl
Definition Jobserver.h:113
static JobSlot createImplicit()
Definition Jobserver.h:106
JobSlot(const JobSlot &)=delete
The public interface for a jobserver client.
Definition Jobserver.h:133
virtual JobSlot tryAcquire()=0
Tries to acquire a job slot from the pool.
virtual void release(JobSlot Slot)=0
Releases a job slot back to the pool.
static LLVM_ABI_FOR_TEST JobserverClient * getInstance()
Returns the singleton instance of the JobserverClient.
static LLVM_ABI_FOR_TEST void resetForTesting()
Resets the singleton instance. For testing purposes only.
virtual ~JobserverClient()
virtual unsigned getNumJobs() const =0
Returns the number of job slots available, as determined on first use.
LLVM Value Representation.
Definition Value.h:75
This is an optimization pass for GlobalISel generic memory operations.
FunctionAddr VTableAddr Value
Definition InstrProf.h:137
@ Other
Any other memory.
Definition ModRef.h:68