diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2017-04-16 16:03:39 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2017-04-16 16:03:39 +0000 |
| commit | d2d3ebb81992e107edf95c1c136d7a342d9b1418 (patch) | |
| tree | bb1af8fff2b1400cf240e3b2532a1e5d22a121da /ELF/Filesystem.cpp | |
| parent | 16787c9ce0b96aaa669d7fab3a495916b35ce758 (diff) | |
Notes
Diffstat (limited to 'ELF/Filesystem.cpp')
| -rw-r--r-- | ELF/Filesystem.cpp | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/ELF/Filesystem.cpp b/ELF/Filesystem.cpp new file mode 100644 index 000000000000..75f7bda75a23 --- /dev/null +++ b/ELF/Filesystem.cpp @@ -0,0 +1,79 @@ +//===- Filesystem.cpp -----------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a few utility functions to handle files. +// +//===----------------------------------------------------------------------===// + +#include "Filesystem.h" +#include "Config.h" +#include "Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileOutputBuffer.h" +#include <thread> + +using namespace llvm; + +using namespace lld; +using namespace lld::elf; + +// Removes a given file asynchronously. This is a performance hack, +// so remove this when operating systems are improved. +// +// On Linux (and probably on other Unix-like systems), unlink(2) is a +// noticeably slow system call. As of 2016, unlink takes 250 +// milliseconds to remove a 1 GB file on ext4 filesystem on my machine. +// +// To create a new result file, we first remove existing file. So, if +// you repeatedly link a 1 GB program in a regular compile-link-debug +// cycle, every cycle wastes 250 milliseconds only to remove a file. +// Since LLD can link a 1 GB binary in about 5 seconds, that waste +// actually counts. +// +// This function spawns a background thread to call unlink. +// The calling thread returns almost immediately. +void elf::unlinkAsync(StringRef Path) { + if (!Config->Threads || !sys::fs::exists(Config->OutputFile)) + return; + + // First, rename Path to avoid race condition. We cannot remove + // Path from a different thread because we are now going to create + // Path as a new file. If we do that in a different thread, the new + // thread can remove the new file. + SmallString<128> TempPath; + if (sys::fs::createUniqueFile(Path + "tmp%%%%%%%%", TempPath)) + return; + if (sys::fs::rename(Path, TempPath)) { + sys::fs::remove(TempPath); + return; + } + + // Remove TempPath in background. + std::thread([=] { ::remove(TempPath.str().str().c_str()); }).detach(); +} + +// Returns true if a given file seems to be writable. +// +// Determining whether a file is writable or not is amazingly hard, +// and after all the only reliable way of doing that is to actually +// create a file. But we don't want to do that in this function +// because LLD shouldn't update any file if it will end in a failure. +// We also don't want to reimplement heuristics. So we'll let +// FileOutputBuffer do the work. +// +// FileOutputBuffer doesn't touch a desitnation file until commit() +// is called. We use that class without calling commit() to predict +// if the given file is writable. +bool elf::isFileWritable(StringRef Path, StringRef Desc) { + if (auto EC = FileOutputBuffer::create(Path, 1).getError()) { + error("cannot open " + Desc + " " + Path + ": " + EC.message()); + return false; + } + return true; +} |
