blob: d907380ada539b810d1b7845013205bee60d4bc7 [file] [log] [blame] [edit]
// Copyright 2024 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string>
#include "jobserver.h"
#include "util.h"
namespace {
// Return true if |fd| is a fifo or pipe descriptor.
bool IsFifoDescriptor(int fd) {
struct stat info;
int ret = ::fstat(fd, &info);
return (ret == 0) && ((info.st_mode & S_IFMT) == S_IFIFO);
}
// Implementation of Jobserver::Client for Posix systems
class PosixJobserverClient : public Jobserver::Client {
public:
virtual ~PosixJobserverClient() {
if (write_fd_ >= 0)
::close(write_fd_);
if (read_fd_ >= 0)
::close(read_fd_);
}
Jobserver::Slot TryAcquire() override {
if (has_implicit_slot_) {
has_implicit_slot_ = false;
return Jobserver::Slot::CreateImplicit();
}
uint8_t slot_char = '\0';
int ret;
do {
ret = ::read(read_fd_, &slot_char, 1);
} while (ret < 0 && errno == EINTR);
if (ret == 1) {
return Jobserver::Slot::CreateExplicit(slot_char);
}
return Jobserver::Slot();
}
void Release(Jobserver::Slot slot) override {
if (!slot.IsValid())
return;
if (slot.IsImplicit()) {
assert(!has_implicit_slot_ && "Implicit slot cannot be released twice!");
has_implicit_slot_ = true;
return;
}
uint8_t slot_char = slot.GetExplicitValue();
int ret;
do {
ret = ::write(write_fd_, &slot_char, 1);
} while (ret < 0 && errno == EINTR);
(void)ret; // Nothing can be done in case of error here.
}
// Initialize with FIFO file path.
bool InitWithFifo(const std::string& fifo_path, std::string* error) {
if (fifo_path.empty()) {
*error = "Empty fifo path";
return false;
}
read_fd_ = ::open(fifo_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (read_fd_ < 0) {
*error =
std::string("Error opening fifo for reading: ") + strerror(errno);
return false;
}
if (!IsFifoDescriptor(read_fd_)) {
*error = "Not a fifo path: " + fifo_path;
// Let destructor close read_fd_.
return false;
}
write_fd_ = ::open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK | O_CLOEXEC);
if (write_fd_ < 0) {
*error =
std::string("Error opening fifo for writing: ") + strerror(errno);
// Let destructor close read_fd_
return false;
}
return true;
}
private:
// Set to true if the implicit slot has not been acquired yet.
bool has_implicit_slot_ = true;
// read and write descriptors.
int read_fd_ = -1;
int write_fd_ = -1;
};
} // namespace
// static
std::unique_ptr<Jobserver::Client> Jobserver::Client::Create(
const Jobserver::Config& config, std::string* error) {
bool success = false;
auto client = std::unique_ptr<PosixJobserverClient>(new PosixJobserverClient);
if (config.mode == Jobserver::Config::kModePosixFifo) {
success = client->InitWithFifo(config.path, error);
} else {
*error = "Unsupported jobserver mode";
}
if (!success)
client.reset();
return client;
}