Ninja
jobserver-posix.cc
Go to the documentation of this file.
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 #include <assert.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <string.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 
22 #include <string>
23 
24 #include "jobserver.h"
25 #include "util.h"
26 
27 namespace {
28 
29 // Return true if |fd| is a fifo or pipe descriptor.
30 bool IsFifoDescriptor(int fd) {
31  struct stat info;
32  int ret = ::fstat(fd, &info);
33  return (ret == 0) && ((info.st_mode & S_IFMT) == S_IFIFO);
34 }
35 
36 // Implementation of Jobserver::Client for Posix systems
37 class PosixJobserverClient : public Jobserver::Client {
38  public:
39  virtual ~PosixJobserverClient() {
40  if (write_fd_ >= 0)
41  ::close(write_fd_);
42  if (read_fd_ >= 0)
43  ::close(read_fd_);
44  }
45 
46  Jobserver::Slot TryAcquire() override {
47  if (has_implicit_slot_) {
48  has_implicit_slot_ = false;
50  }
51  uint8_t slot_char = '\0';
52  ssize_t ret;
53  do {
54  ret = ::read(read_fd_, &slot_char, 1);
55  } while (ret < 0 && errno == EINTR);
56  if (ret == 1) {
57  return Jobserver::Slot::CreateExplicit(slot_char);
58  }
59  return Jobserver::Slot();
60  }
61 
62  void Release(Jobserver::Slot slot) override {
63  if (!slot.IsValid())
64  return;
65 
66  if (slot.IsImplicit()) {
67  assert(!has_implicit_slot_ && "Implicit slot cannot be released twice!");
68  has_implicit_slot_ = true;
69  return;
70  }
71 
72  uint8_t slot_char = slot.GetExplicitValue();
73  ssize_t ret;
74  do {
75  ret = ::write(write_fd_, &slot_char, 1);
76  } while (ret < 0 && errno == EINTR);
77  (void)ret; // Nothing can be done in case of error here.
78  }
79 
80  // Initialize with FIFO file path.
81  bool InitWithFifo(const std::string& fifo_path, std::string* error) {
82  if (fifo_path.empty()) {
83  *error = "Empty fifo path";
84  return false;
85  }
86  read_fd_ = ::open(fifo_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
87  if (read_fd_ < 0) {
88  *error =
89  std::string("Error opening fifo for reading: ") + strerror(errno);
90  return false;
91  }
92  if (!IsFifoDescriptor(read_fd_)) {
93  *error = "Not a fifo path: " + fifo_path;
94  // Let destructor close read_fd_.
95  return false;
96  }
97  write_fd_ = ::open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK | O_CLOEXEC);
98  if (write_fd_ < 0) {
99  *error =
100  std::string("Error opening fifo for writing: ") + strerror(errno);
101  // Let destructor close read_fd_
102  return false;
103  }
104  return true;
105  }
106 
107  private:
108  // Set to true if the implicit slot has not been acquired yet.
109  bool has_implicit_slot_ = true;
110 
111  // read and write descriptors.
112  int read_fd_ = -1;
113  int write_fd_ = -1;
114 };
115 
116 } // namespace
117 
118 // static
119 std::unique_ptr<Jobserver::Client> Jobserver::Client::Create(
120  const Jobserver::Config& config, std::string* error) {
121  bool success = false;
122  auto client = std::unique_ptr<PosixJobserverClient>(new PosixJobserverClient);
123  if (config.mode == Jobserver::Config::kModePosixFifo) {
124  success = client->InitWithFifo(config.path, error);
125  } else {
126  *error = "Unsupported jobserver mode";
127  }
128  if (!success)
129  client.reset();
130  return client;
131 }
A Jobserver::Client instance models a client of an external GNU jobserver pool, which can be implemen...
Definition: jobserver.h:189
virtual Slot TryAcquire()
Try to acquire a slot from the pool.
Definition: jobserver.h:201
static std::unique_ptr< Client > Create(const Config &, std::string *error)
Create a new Client instance from a given configuration.
virtual void Release(Slot slot)
Release a slot to the pool.
Definition: jobserver.h:208
A Jobserver::Config models how to access or implement a GNU jobserver implementation.
Definition: jobserver.h:106
std::string path
For kModeFifo, this is the path to the Unix FIFO to use.
Definition: jobserver.h:146
Mode mode
Implementation mode for the pool.
Definition: jobserver.h:142
A Jobserver::Slot models a single job slot that can be acquired from.
Definition: jobserver.h:55
uint8_t GetExplicitValue() const
Return value of an explicit slot.
Definition: jobserver.cc:64
static Slot CreateImplicit()
Create instance for the implicit value.
Definition: jobserver.h:94
static Slot CreateExplicit(uint8_t value)
Create instance for explicit byte value.
Definition: jobserver.h:89
bool IsImplicit() const
Return true if this instance represents an implicit job slot.
Definition: jobserver.h:79
bool IsValid() const
Return true if this instance is valid, i.e.
Definition: jobserver.h:76