Files
codeql/swift/extractor/infra/TargetFile.cpp
Paolo Tranquilli f7dca4d70f Swift: trap output rework
Firstly, this change reworks how inter-process races are resolved.
Moreover some responsability reorganization has led to merging
`TrapArena` and `TrapOutput` again into a `TrapDomain` class.

A `TargetFile` class is introduced, that is successfully created
only for the first process that starts processing a given trap output
file. From then on `TargetFile` simply wraps around `<<` stream
operations, dumping them to a temporary file. When `TargetFile::commit`
is called, the temporary file is moved on to the actual target trap
file.

Processes that lose the race can now just ignore the unneeded
extraction and go on, while previously all processes would carry out
all extractions overwriting each other at the end.

Some of the file system logic contained in `SwiftExtractor.cpp` has been
moved to this class, and two TODOs are solved:
* introducing a better inter process file collision avoidance strategy
* better error handling for trap output operations: if unable to write
  to the trap file (or carry out other basic file operations), we just
  abort.

The changes to `ExprVisitor` and `StmtVisitor` are due to wanting to
hide the raw `TrapDomain::createLabel` from them, and bring more
funcionality under the generic caching/dispatching mechanism.
2022-07-13 11:19:57 +02:00

74 lines
2.0 KiB
C++

#include "swift/extractor/infra/TargetFile.h"
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/Path.h>
namespace codeql {
namespace {
[[noreturn]] void error(const char* action, const std::string& arg, std::error_code ec) {
std::cerr << "Unable to " << action << ": " << arg << " (" << ec.message() << ")\n";
std::abort();
}
[[noreturn]] void error(const char* action, const std::string& arg) {
error(action, arg, {errno, std::system_category()});
}
void ensureParentDir(const std::string& path) {
auto parent = llvm::sys::path::parent_path(path);
if (auto ec = llvm::sys::fs::create_directories(parent)) {
error("create directory", parent.str(), ec);
}
}
std::string initPath(std::string_view target, std::string_view dir) {
std::string ret{dir};
assert(!target.empty() && "target must be a non-empty path");
if (target[0] != '/') {
ret += '/';
}
ret.append(target);
ensureParentDir(ret);
return ret;
}
} // namespace
TargetFile::TargetFile(std::string_view target,
std::string_view targetDir,
std::string_view workingDir)
: workingPath{initPath(target, workingDir)}, targetPath{initPath(target, targetDir)} {
errno = 0;
// since C++17 "x" mode opens with O_EXCL (fails if file already exists)
if (auto f = std::fopen(targetPath.c_str(), "wx")) {
std::fclose(f);
out.open(workingPath);
checkOutput("open file for writing");
} else {
if (errno != EEXIST) {
error("open file for writing", targetPath);
}
// else we just lost the race, do nothing (good() will return false to signal this)
}
}
bool TargetFile::good() const {
return out && out.is_open();
}
void TargetFile::commit() {
assert(good());
out.close();
errno = 0;
if (std::rename(workingPath.c_str(), targetPath.c_str()) != 0) {
error("rename file", targetPath);
}
}
void TargetFile::checkOutput(const char* action) {
if (!out) {
error(action, workingPath);
}
}
} // namespace codeql