LLVM 22.0.0git
Jobserver.cpp
Go to the documentation of this file.
1//===- llvm/Support/Jobserver.cpp - Jobserver Client Implementation -------===//
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
11#include "llvm/ADT/Statistic.h"
13#include "llvm/Config/llvm-config.h"
14#include "llvm/Support/Debug.h"
15#include "llvm/Support/Error.h"
17
18#include <atomic>
19#include <memory>
20#include <mutex>
21#include <new>
22
23#define DEBUG_TYPE "jobserver"
24
25using namespace llvm;
26
27namespace {
28struct FdPair {
29 int Read = -1;
30 int Write = -1;
31 bool isValid() const { return Read >= 0 && Write >= 0; }
32};
33
34struct JobserverConfig {
35 enum Mode {
36 None,
37 PosixFifo,
38 PosixPipe,
39 Win32Semaphore,
40 };
41 Mode TheMode = None;
42 std::string Path;
43 FdPair PipeFDs;
44};
45
46/// A helper function that checks if `Input` starts with `Prefix`.
47/// If it does, it removes the prefix from `Input`, assigns the remainder to
48/// `Value`, and returns true. Otherwise, it returns false.
49bool getPrefixedValue(StringRef Input, StringRef Prefix, StringRef &Value) {
50 if (Input.consume_front(Prefix)) {
51 Value = Input;
52 return true;
53 }
54 return false;
55}
56
57/// A helper function to parse a string in the format "R,W" where R and W are
58/// non-negative integers representing file descriptors. It populates the
59/// `ReadFD` and `WriteFD` output parameters. Returns true on success.
60static std::optional<FdPair> getFileDescriptorPair(StringRef Input) {
61 FdPair FDs;
62 if (Input.consumeInteger(10, FDs.Read))
63 return std::nullopt;
64 if (!Input.consume_front(","))
65 return std::nullopt;
66 if (Input.consumeInteger(10, FDs.Write))
67 return std::nullopt;
68 if (!Input.empty() || !FDs.isValid())
69 return std::nullopt;
70 return FDs;
71}
72
73/// Parses the `MAKEFLAGS` environment variable string to find jobserver
74/// arguments. It splits the string into space-separated arguments and searches
75/// for `--jobserver-auth` or `--jobserver-fds`. Based on the value of these
76/// arguments, it determines the jobserver mode (Pipe, FIFO, or Semaphore) and
77/// connection details (file descriptors or path).
78Expected<JobserverConfig> parseNativeMakeFlags(StringRef MakeFlags) {
79 JobserverConfig Config;
80 if (MakeFlags.empty())
81 return Config;
82
83 // Split the MAKEFLAGS string into arguments.
85 SplitString(MakeFlags, Args);
86
87 // If '-n' (dry-run) is present as a legacy flag (not starting with '-'),
88 // disable the jobserver.
89 if (!Args.empty() && !Args[0].starts_with("-") && Args[0].contains('n'))
90 return Config;
91
92 // Iterate through arguments to find jobserver flags.
93 // Note that make may pass multiple --jobserver-auth flags; the last one wins.
94 for (StringRef Arg : Args) {
96 if (getPrefixedValue(Arg, "--jobserver-auth=", Value)) {
97 // Try to parse as a file descriptor pair first.
98 if (auto FDPair = getFileDescriptorPair(Value)) {
99 Config.TheMode = JobserverConfig::PosixPipe;
100 Config.PipeFDs = *FDPair;
101 } else {
102 StringRef FifoPath;
103 // If not FDs, try to parse as a named pipe (fifo).
104 if (getPrefixedValue(Value, "fifo:", FifoPath)) {
105 Config.TheMode = JobserverConfig::PosixFifo;
106 Config.Path = FifoPath.str();
107 } else {
108 // Otherwise, assume it's a Windows semaphore.
109 Config.TheMode = JobserverConfig::Win32Semaphore;
110 Config.Path = Value.str();
111 }
112 }
113 } else if (getPrefixedValue(Arg, "--jobserver-fds=", Value)) {
114 // This is an alternative, older syntax for the pipe-based server.
115 if (auto FDPair = getFileDescriptorPair(Value)) {
116 Config.TheMode = JobserverConfig::PosixPipe;
117 Config.PipeFDs = *FDPair;
118 } else {
120 "Invalid file descriptor pair in MAKEFLAGS");
121 }
122 }
123 }
124
125// Perform platform-specific validation.
126#ifdef _WIN32
127 if (Config.TheMode == JobserverConfig::PosixFifo ||
128 Config.TheMode == JobserverConfig::PosixPipe)
129 return createStringError(
131 "FIFO/Pipe-based jobserver is not supported on Windows");
132#else
133 if (Config.TheMode == JobserverConfig::Win32Semaphore)
134 return createStringError(
136 "Semaphore-based jobserver is not supported on this platform");
137#endif
138 return Config;
139}
140
141std::once_flag GJobserverOnceFlag;
142JobserverClient *GJobserver = nullptr;
143
144} // namespace
145
146namespace llvm {
148 bool IsInitialized = false;
149 std::atomic<bool> HasImplicitSlot{true};
150 unsigned NumJobs = 0;
151
152public:
153 JobserverClientImpl(const JobserverConfig &Config);
155
156 JobSlot tryAcquire() override;
157 void release(JobSlot Slot) override;
158 unsigned getNumJobs() const override { return NumJobs; }
159
160 bool isValid() const { return IsInitialized; }
161
162private:
163#if defined(LLVM_ON_UNIX)
164 int ReadFD = -1;
165 int WriteFD = -1;
166 std::string FifoPath;
167#elif defined(_WIN32)
168 void *Semaphore = nullptr;
169#endif
170};
171} // namespace llvm
172
173// Include the platform-specific parts of the class.
174#if defined(LLVM_ON_UNIX)
175#include "Unix/Jobserver.inc"
176#elif defined(_WIN32)
177#include "Windows/Jobserver.inc"
178#else
179// Dummy implementation for unsupported platforms.
180JobserverClientImpl::JobserverClientImpl(const JobserverConfig &Config) {}
182JobSlot JobserverClientImpl::tryAcquire() { return JobSlot(); }
184#endif
185
186namespace llvm {
188
190 assert(isExplicit() && "Cannot get value of implicit or invalid slot");
191 return static_cast<uint8_t>(Value);
192}
193
194/// This is the main entry point for acquiring a jobserver client. It uses a
195/// std::call_once to ensure the singleton `GJobserver` instance is created
196/// safely in a multi-threaded environment. On first call, it reads the
197/// `MAKEFLAGS` environment variable, parses it, and attempts to construct and
198/// initialize a `JobserverClientImpl`. If successful, the global instance is
199/// stored in `GJobserver`. Subsequent calls will return the existing instance.
201 std::call_once(GJobserverOnceFlag, []() {
203 dbgs()
204 << "JobserverClient::getInstance() called for the first time.\n");
205 const char *MakeFlagsEnv = getenv("MAKEFLAGS");
206 if (!MakeFlagsEnv) {
207 errs() << "Warning: failed to create jobserver client due to MAKEFLAGS "
208 "environment variable not found\n";
209 return;
210 }
211
212 LLVM_DEBUG(dbgs() << "Found MAKEFLAGS = \"" << MakeFlagsEnv << "\"\n");
213
214 auto ConfigOrErr = parseNativeMakeFlags(MakeFlagsEnv);
215 if (Error Err = ConfigOrErr.takeError()) {
216 errs() << "Warning: failed to create jobserver client due to invalid "
217 "MAKEFLAGS environment variable: "
218 << toString(std::move(Err)) << "\n";
219 return;
220 }
221
222 JobserverConfig Config = *ConfigOrErr;
223 if (Config.TheMode == JobserverConfig::None) {
224 errs() << "Warning: failed to create jobserver client due to jobserver "
225 "mode missing in MAKEFLAGS environment variable\n";
226 return;
227 }
228
229 if (Config.TheMode == JobserverConfig::PosixPipe) {
230#if defined(LLVM_ON_UNIX)
231 if (!areFdsValid(Config.PipeFDs.Read, Config.PipeFDs.Write)) {
232 errs() << "Warning: failed to create jobserver client due to invalid "
233 "Pipe FDs in MAKEFLAGS environment variable\n";
234 return;
235 }
236#endif
237 }
238
239 auto Client = std::make_unique<JobserverClientImpl>(Config);
240 if (Client->isValid()) {
241 LLVM_DEBUG(dbgs() << "Jobserver client created successfully!\n");
242 GJobserver = Client.release();
243 } else
244 errs() << "Warning: jobserver client initialization failed.\n";
245 });
246 return GJobserver;
247}
248
249/// For testing purposes only. This function resets the singleton instance by
250/// destroying the existing client and re-initializing the `std::once_flag`.
251/// This allows tests to simulate the first-time initialization of the
252/// jobserver client multiple times.
254 delete GJobserver;
255 GJobserver = nullptr;
256 // Re-construct the std::once_flag in place to reset the singleton state.
257 new (&GJobserverOnceFlag) std::once_flag();
258}
259} // namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static cl::opt< RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode > Mode("regalloc-enable-advisor", cl::Hidden, cl::init(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Default), cl::desc("Enable regalloc advisor mode"), cl::values(clEnumValN(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Default, "default", "Default"), clEnumValN(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Release, "release", "precompiled"), clEnumValN(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Development, "development", "for training")))
static bool isValid(const char C)
Returns true if C is a valid mangled character: <0-9a-zA-Z_>.
This file defines the SmallVector class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
This file contains some functions that are useful when dealing with strings.
#define LLVM_DEBUG(...)
Definition Debug.h:114
The Input class is used to parse a yaml document into in-memory structs and vectors.
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
Tagged union holding either a T or a Error.
Definition Error.h:485
A JobSlot represents a single job slot that can be acquired from or released to a jobserver pool.
Definition Jobserver.h:77
uint8_t getExplicitValue() const
bool isExplicit() const
Definition Jobserver.h:111
JobserverClientImpl(const JobserverConfig &Config)
void release(JobSlot Slot) override
Releases a job slot back to the pool.
JobSlot tryAcquire() override
Tries to acquire a job slot from the pool.
unsigned getNumJobs() const override
Returns the number of job slots available, as determined on first use.
The public interface for a jobserver client.
Definition Jobserver.h:135
static JobserverClient * getInstance()
Returns the singleton instance of the JobserverClient.
static void resetForTesting()
Resets the singleton instance. For testing purposes only.
virtual ~JobserverClient()
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
std::string str() const
str - Get the contents as an std::string.
Definition StringRef.h:225
constexpr bool empty() const
empty - Check if the string is empty.
Definition StringRef.h:143
LLVM Value Representation.
Definition Value.h:75
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:98
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1305
LLVM_ABI void SplitString(StringRef Source, SmallVectorImpl< StringRef > &OutFragments, StringRef Delimiters=" \t\n\v\f\r")
SplitString - Split up the specified string according to the specified delimiters,...
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:207
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
std::string toString(const APInt &I, unsigned Radix, bool Signed, bool formatAsCLiteral=false, bool UpperCase=true, bool InsertSeparators=false)