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#include <memory>
71#include <string>
72
73namespace llvm {
74
75/// A JobSlot represents a single job slot that can be acquired from or released
76/// to a jobserver pool. This class is move-only.
77class JobSlot {
78public:
79 /// Default constructor creates an invalid instance.
80 JobSlot() = default;
81
82 // Move operations are allowed.
83 JobSlot(JobSlot &&Other) noexcept : Value(Other.Value) {
84 Other.Value = kInvalidValue;
85 }
87 if (this != &Other) {
88 this->Value = Other.Value;
89 Other.Value = kInvalidValue;
90 }
91 return *this;
92 }
93
94 // Copy operations are disallowed.
95 JobSlot(const JobSlot &) = delete;
96 JobSlot &operator=(const JobSlot &) = delete;
97
98 /// Returns true if this instance is valid (either implicit or explicit).
99 bool isValid() const { return Value >= 0; }
100
101 /// Returns true if this instance represents the implicit job slot.
102 bool isImplicit() const { return Value == kImplicitValue; }
103
105 return JobSlot(static_cast<int16_t>(V));
106 }
107
108 static JobSlot createImplicit() { return JobSlot(kImplicitValue); }
109
111 bool isExplicit() const { return isValid() && !isImplicit(); }
112
113private:
114 friend class JobserverClient;
116
117 JobSlot(int16_t V) : Value(V) {}
118
119 /// The jobserver pipe carries explicit tokens (bytes 0–255). We reserve two
120 /// sentinels in Value for special cases:
121 /// kInvalidValue (-1): no slot held
122 /// kImplicitValue (INT16_MAX): implicit slot granted at startup (no pipe
123 /// I/O)
124 ///
125 /// We use int16_t so Value can store 0–255 explicit tokens and
126 /// sentinels without overflow, enforces fixed 16-bit width, and avoids
127 /// unsigned/signed mix-ups.
128 static constexpr int16_t kInvalidValue = -1;
129 static constexpr int16_t kImplicitValue = INT16_MAX;
130 int16_t Value = kInvalidValue;
131};
132
133/// The public interface for a jobserver client.
134/// This client is a lazy-initialized singleton that is created on first use.
136public:
138
139 /// Tries to acquire a job slot from the pool. On failure (e.g., if the pool
140 /// is empty), this returns an invalid JobSlot instance. The first successful
141 /// call will always return the implicit slot.
142 virtual JobSlot tryAcquire() = 0;
143
144 /// Releases a job slot back to the pool.
145 virtual void release(JobSlot Slot) = 0;
146
147 /// Returns the number of job slots available, as determined on first use.
148 /// This value is cached. Returns 0 if no jobserver is active.
149 virtual unsigned getNumJobs() const = 0;
150
151 /// Returns the singleton instance of the JobserverClient.
152 /// The instance is created on the first call to this function.
153 /// Returns a nullptr if no jobserver is configured or an error occurs.
155
156 /// Resets the singleton instance. For testing purposes only.
157 static void resetForTesting();
158};
159
160} // end namespace llvm
161
162#endif // LLVM_SUPPORT_JOBSERVER_H
A JobSlot represents a single job slot that can be acquired from or released to a jobserver pool.
Definition Jobserver.h:77
static JobSlot createExplicit(uint8_t V)
Definition Jobserver.h:104
JobSlot()=default
Default constructor creates an invalid instance.
JobSlot & operator=(const JobSlot &)=delete
uint8_t getExplicitValue() const
JobSlot(JobSlot &&Other) noexcept
Definition Jobserver.h:83
bool isImplicit() const
Returns true if this instance represents the implicit job slot.
Definition Jobserver.h:102
JobSlot & operator=(JobSlot &&Other) noexcept
Definition Jobserver.h:86
bool isExplicit() const
Definition Jobserver.h:111
bool isValid() const
Returns true if this instance is valid (either implicit or explicit).
Definition Jobserver.h:99
friend class JobserverClient
Definition Jobserver.h:114
friend class JobserverClientImpl
Definition Jobserver.h:115
static JobSlot createImplicit()
Definition Jobserver.h:108
JobSlot(const JobSlot &)=delete
The public interface for a jobserver client.
Definition Jobserver.h:135
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 JobserverClient * getInstance()
Returns the singleton instance of the JobserverClient.
static 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