LLVM 23.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/Support/Error.h"
12
13#include <atomic>
14#include <memory>
15#include <mutex>
16#include <new>
17
18#define DEBUG_TYPE "jobserver"
19
20using namespace llvm;
21
22namespace {
23struct FdPair {
24 int Read = -1;
25 int Write = -1;
26 bool isValid() const { return Read >= 0 && Write >= 0; }
27};
28
29struct JobserverConfig {
30 enum Mode {
31 None,
32 PosixFifo,
33 PosixPipe,
34 Win32Semaphore,
35 };
36 Mode TheMode = None;
37 std::string Path;
38 FdPair PipeFDs;
39};
40
41/// A helper function that checks if `Input` starts with `Prefix`.
42/// If it does, it removes the prefix from `Input`, assigns the remainder to
43/// `Value`, and returns true. Otherwise, it returns false.
44bool getPrefixedValue(StringRef Input, StringRef Prefix, StringRef &Value) {
45 if (Input.consume_front(Prefix)) {
46 Value = Input;
47 return true;
48 }
49 return false;
50}
51
52/// A helper function to parse a string in the format "R,W" where R and W are
53/// non-negative integers representing file descriptors. It populates the
54/// `ReadFD` and `WriteFD` output parameters. Returns true on success.
55static std::optional<FdPair> getFileDescriptorPair(StringRef Input) {
56 FdPair FDs;
57 if (Input.consumeInteger(10, FDs.Read))
58 return std::nullopt;
59 if (!Input.consume_front(","))
60 return std::nullopt;
61 if (Input.consumeInteger(10, FDs.Write))
62 return std::nullopt;
63 if (!Input.empty() || !FDs.isValid())
64 return std::nullopt;
65 return FDs;
66}
67
68/// Parses the `MAKEFLAGS` environment variable string to find jobserver
69/// arguments. It splits the string into space-separated arguments and searches
70/// for `--jobserver-auth` or `--jobserver-fds`. Based on the value of these
71/// arguments, it determines the jobserver mode (Pipe, FIFO, or Semaphore) and
72/// connection details (file descriptors or path).
73Expected<JobserverConfig> parseNativeMakeFlags(StringRef MakeFlags) {
74 JobserverConfig Config;
75 if (MakeFlags.empty())
76 return Config;
77
78 // Split the MAKEFLAGS string into arguments.
80 SplitString(MakeFlags, Args);
81
82 // If '-n' (dry-run) is present as a legacy flag (not starting with '-'),
83 // disable the jobserver.
84 if (!Args.empty() && !Args[0].starts_with("-") && Args[0].contains('n'))
85 return Config;
86
87 // Iterate through arguments to find jobserver flags.
88 // Note that make may pass multiple --jobserver-auth flags; the last one wins.
89 for (StringRef Arg : Args) {
91 if (getPrefixedValue(Arg, "--jobserver-auth=", Value)) {
92 // Try to parse as a file descriptor pair first.
93 if (auto FDPair = getFileDescriptorPair(Value)) {
94 Config.TheMode = JobserverConfig::PosixPipe;
95 Config.PipeFDs = *FDPair;
96 } else {
97 StringRef FifoPath;
98 // If not FDs, try to parse as a named pipe (fifo).
99 if (getPrefixedValue(Value, "fifo:", FifoPath)) {
100 Config.TheMode = JobserverConfig::PosixFifo;
101 Config.Path = FifoPath.str();
102 } else {
103 // Otherwise, assume it's a Windows semaphore.
104 Config.TheMode = JobserverConfig::Win32Semaphore;
105 Config.Path = Value.str();
106 }
107 }
108 } else if (getPrefixedValue(Arg, "--jobserver-fds=", Value)) {
109 // This is an alternative, older syntax for the pipe-based server.
110 if (auto FDPair = getFileDescriptorPair(Value)) {
111 Config.TheMode = JobserverConfig::PosixPipe;
112 Config.PipeFDs = *FDPair;
113 } else {
115 "Invalid file descriptor pair in MAKEFLAGS");
116 }
117 }
118 }
119
120// Perform platform-specific validation.
121#ifdef _WIN32
122 if (Config.TheMode == JobserverConfig::PosixFifo ||
123 Config.TheMode == JobserverConfig::PosixPipe)
124 return createStringError(
126 "FIFO/Pipe-based jobserver is not supported on Windows");
127#else
128 if (Config.TheMode == JobserverConfig::Win32Semaphore)
129 return createStringError(
131 "Semaphore-based jobserver is not supported on this platform");
132#endif
133 return Config;
134}
135
136std::once_flag GJobserverOnceFlag;
137JobserverClient *GJobserver = nullptr;
138
139} // namespace
140
141namespace llvm {
143 bool IsInitialized = false;
144 std::atomic<bool> HasImplicitSlot{true};
145 unsigned NumJobs = 0;
146
147public:
148 JobserverClientImpl(const JobserverConfig &Config);
150
151 JobSlot tryAcquire() override;
152 void release(JobSlot Slot) override;
153 unsigned getNumJobs() const override { return NumJobs; }
154
155 bool isValid() const { return IsInitialized; }
156
157private:
158#if defined(LLVM_ON_UNIX)
159 int ReadFD = -1;
160 int WriteFD = -1;
161 std::string FifoPath;
162#elif defined(_WIN32)
163 void *Semaphore = nullptr;
164#endif
165};
166} // namespace llvm
167
168// Include the platform-specific parts of the class.
169#if defined(LLVM_ON_UNIX)
170#include "Unix/Jobserver.inc"
171#elif defined(_WIN32)
172#include "Windows/Jobserver.inc"
173#else
174// Dummy implementation for unsupported platforms.
175JobserverClientImpl::JobserverClientImpl(const JobserverConfig &Config) {}
179#endif
180
181namespace llvm {
183
185 assert(isExplicit() && "Cannot get value of implicit or invalid slot");
186 return static_cast<uint8_t>(Value);
187}
188
189/// This is the main entry point for acquiring a jobserver client. It uses a
190/// std::call_once to ensure the singleton `GJobserver` instance is created
191/// safely in a multi-threaded environment. On first call, it reads the
192/// `MAKEFLAGS` environment variable, parses it, and attempts to construct and
193/// initialize a `JobserverClientImpl`. If successful, the global instance is
194/// stored in `GJobserver`. Subsequent calls will return the existing instance.
196 std::call_once(GJobserverOnceFlag, []() {
198 dbgs()
199 << "JobserverClient::getInstance() called for the first time.\n");
200 const char *MakeFlagsEnv = getenv("MAKEFLAGS");
201 if (!MakeFlagsEnv) {
202 errs() << "Warning: failed to create jobserver client due to MAKEFLAGS "
203 "environment variable not found\n";
204 return;
205 }
206
207 LLVM_DEBUG(dbgs() << "Found MAKEFLAGS = \"" << MakeFlagsEnv << "\"\n");
208
209 auto ConfigOrErr = parseNativeMakeFlags(MakeFlagsEnv);
210 if (Error Err = ConfigOrErr.takeError()) {
211 errs() << "Warning: failed to create jobserver client due to invalid "
212 "MAKEFLAGS environment variable: "
213 << toString(std::move(Err)) << "\n";
214 return;
215 }
216
217 JobserverConfig Config = *ConfigOrErr;
218 if (Config.TheMode == JobserverConfig::None) {
219 errs() << "Warning: failed to create jobserver client due to jobserver "
220 "mode missing in MAKEFLAGS environment variable\n";
221 return;
222 }
223
224 if (Config.TheMode == JobserverConfig::PosixPipe) {
225#if defined(LLVM_ON_UNIX)
226 if (!areFdsValid(Config.PipeFDs.Read, Config.PipeFDs.Write)) {
227 errs() << "Warning: failed to create jobserver client due to invalid "
228 "Pipe FDs in MAKEFLAGS environment variable\n";
229 return;
230 }
231#endif
232 }
233
234 auto Client = std::make_unique<JobserverClientImpl>(Config);
235 if (Client->isValid()) {
236 LLVM_DEBUG(dbgs() << "Jobserver client created successfully!\n");
237 GJobserver = Client.release();
238 } else
239 errs() << "Warning: jobserver client initialization failed.\n";
240 });
241 return GJobserver;
242}
243
244/// For testing purposes only. This function resets the singleton instance by
245/// destroying the existing client and re-initializing the `std::once_flag`.
246/// This allows tests to simulate the first-time initialization of the
247/// jobserver client multiple times.
249 delete GJobserver;
250 GJobserver = nullptr;
251 // Re-construct the std::once_flag in place to reset the singleton state.
252 new (&GJobserverOnceFlag) std::once_flag();
253}
254} // 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 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:75
uint8_t getExplicitValue() const
bool isExplicit() const
Definition Jobserver.h:109
void release(JobSlot Slot) override
Releases a job slot back to the pool.
unsigned getNumJobs() const override
Returns the number of job slots available, as determined on first use.
JobSlot tryAcquire() override
Tries to acquire a job slot from the pool.
JobserverClientImpl(const JobserverConfig &Config)
The public interface for a jobserver client.
Definition Jobserver.h:133
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()
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.
Definition Types.h:26
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:94
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)