summaryrefslogtreecommitdiff
path: root/lib/Support/Unix
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Support/Unix')
-rw-r--r--lib/Support/Unix/Host.inc2
-rw-r--r--lib/Support/Unix/Memory.inc12
-rw-r--r--lib/Support/Unix/Path.inc175
-rw-r--r--lib/Support/Unix/Process.inc31
-rw-r--r--lib/Support/Unix/Program.inc68
-rw-r--r--lib/Support/Unix/Signals.inc302
-rw-r--r--lib/Support/Unix/ThreadLocal.inc2
-rw-r--r--lib/Support/Unix/Threading.inc17
-rw-r--r--lib/Support/Unix/Unix.h2
-rw-r--r--lib/Support/Unix/Watchdog.inc2
10 files changed, 397 insertions, 216 deletions
diff --git a/lib/Support/Unix/Host.inc b/lib/Support/Unix/Host.inc
index 5580e63893c6..b65f84bf4444 100644
--- a/lib/Support/Unix/Host.inc
+++ b/lib/Support/Unix/Host.inc
@@ -64,5 +64,5 @@ std::string sys::getDefaultTargetTriple() {
TargetTripleString = EnvTriple;
#endif
- return Triple::normalize(TargetTripleString);
+ return TargetTripleString;
}
diff --git a/lib/Support/Unix/Memory.inc b/lib/Support/Unix/Memory.inc
index 848548d18177..adbfff2f59a5 100644
--- a/lib/Support/Unix/Memory.inc
+++ b/lib/Support/Unix/Memory.inc
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "Unix.h"
+#include "llvm/Config/config.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Process.h"
@@ -24,6 +25,10 @@
#include <mach/mach.h>
#endif
+#ifdef __Fuchsia__
+#include <zircon/syscalls.h>
+#endif
+
#if defined(__mips__)
# if defined(__OpenBSD__)
# include <mips64/sysarch.h>
@@ -32,7 +37,7 @@
# endif
#endif
-#ifdef __APPLE__
+#if defined(__APPLE__)
extern "C" void sys_icache_invalidate(const void *Addr, size_t len);
#else
extern "C" void __clear_cache(void *, void*);
@@ -205,6 +210,11 @@ void Memory::InvalidateInstructionCache(const void *Addr,
sys_icache_invalidate(const_cast<void *>(Addr), Len);
# endif
+#elif defined(__Fuchsia__)
+
+ zx_status_t Status = zx_cache_flush(Addr, Len, ZX_CACHE_FLUSH_INSN);
+ assert(Status == ZX_OK && "cannot invalidate instruction cache");
+
#else
# if (defined(__POWERPC__) || defined (__ppc__) || \
diff --git a/lib/Support/Unix/Path.inc b/lib/Support/Unix/Path.inc
index 2ecb97316c87..7ad57d892ff1 100644
--- a/lib/Support/Unix/Path.inc
+++ b/lib/Support/Unix/Path.inc
@@ -31,23 +31,8 @@
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
-#if HAVE_DIRENT_H
-# include <dirent.h>
-# define NAMLEN(dirent) strlen((dirent)->d_name)
-#else
-# define dirent direct
-# define NAMLEN(dirent) (dirent)->d_namlen
-# if HAVE_SYS_NDIR_H
-# include <sys/ndir.h>
-# endif
-# if HAVE_SYS_DIR_H
-# include <sys/dir.h>
-# endif
-# if HAVE_NDIR_H
-# include <ndir.h>
-# endif
-#endif
+#include <dirent.h>
#include <pwd.h>
#ifdef __APPLE__
@@ -108,6 +93,9 @@ using namespace llvm;
namespace llvm {
namespace sys {
namespace fs {
+
+const file_t kInvalidFile = -1;
+
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
defined(__minix) || defined(__FreeBSD_kernel__) || defined(__linux__) || \
defined(__CYGWIN__) || defined(__DragonFly__) || defined(_AIX)
@@ -380,6 +368,12 @@ static bool is_local_impl(struct STATVFS &Vfs) {
#elif defined(__CYGWIN__)
// Cygwin doesn't expose this information; would need to use Win32 API.
return false;
+#elif defined(__Fuchsia__)
+ // Fuchsia doesn't yet support remote filesystem mounts.
+ return true;
+#elif defined(__HAIKU__)
+ // Haiku doesn't expose this information.
+ return false;
#elif defined(__sun)
// statvfs::f_basetype contains a null-terminated FSType name of the mounted target
StringRef fstype(Vfs.f_basetype);
@@ -530,7 +524,7 @@ static void expandTildeExpr(SmallVectorImpl<char> &Path) {
}
static std::error_code fillStatus(int StatRet, const struct stat &Status,
- file_status &Result) {
+ file_status &Result) {
if (StatRet != 0) {
std::error_code ec(errno, std::generic_category());
if (ec == errc::no_such_file_or_directory)
@@ -643,7 +637,8 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset,
mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
uint64_t offset, std::error_code &ec)
- : Size(length), Mapping() {
+ : Size(length), Mapping(), Mode(mode) {
+ (void)Mode;
ec = init(fd, offset, mode);
if (ec)
Mapping = nullptr;
@@ -702,7 +697,7 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
if (cur_dir == nullptr && errno != 0) {
return std::error_code(errno, std::generic_category());
} else if (cur_dir != nullptr) {
- StringRef name(cur_dir->d_name, NAMLEN(cur_dir));
+ StringRef name(cur_dir->d_name);
if ((name.size() == 1 && name[0] == '.') ||
(name.size() == 2 && name[0] == '.' && name[1] == '.'))
return directory_iterator_increment(it);
@@ -729,21 +724,83 @@ static bool hasProcSelfFD() {
}
#endif
-std::error_code openFileForRead(const Twine &Name, int &ResultFD,
- SmallVectorImpl<char> *RealPath) {
- SmallString<128> Storage;
- StringRef P = Name.toNullTerminatedStringRef(Storage);
- int OpenFlags = O_RDONLY;
+static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags,
+ FileAccess Access) {
+ int Result = 0;
+ if (Access == FA_Read)
+ Result |= O_RDONLY;
+ else if (Access == FA_Write)
+ Result |= O_WRONLY;
+ else if (Access == (FA_Read | FA_Write))
+ Result |= O_RDWR;
+
+ // This is for compatibility with old code that assumed F_Append implied
+ // would open an existing file. See Windows/Path.inc for a longer comment.
+ if (Flags & F_Append)
+ Disp = CD_OpenAlways;
+
+ if (Disp == CD_CreateNew) {
+ Result |= O_CREAT; // Create if it doesn't exist.
+ Result |= O_EXCL; // Fail if it does.
+ } else if (Disp == CD_CreateAlways) {
+ Result |= O_CREAT; // Create if it doesn't exist.
+ Result |= O_TRUNC; // Truncate if it does.
+ } else if (Disp == CD_OpenAlways) {
+ Result |= O_CREAT; // Create if it doesn't exist.
+ } else if (Disp == CD_OpenExisting) {
+ // Nothing special, just don't add O_CREAT and we get these semantics.
+ }
+
+ if (Flags & F_Append)
+ Result |= O_APPEND;
+
#ifdef O_CLOEXEC
- OpenFlags |= O_CLOEXEC;
+ if (!(Flags & OF_ChildInherit))
+ Result |= O_CLOEXEC;
#endif
- if ((ResultFD = sys::RetryAfterSignal(-1, open, P.begin(), OpenFlags)) < 0)
+
+ return Result;
+}
+
+std::error_code openFile(const Twine &Name, int &ResultFD,
+ CreationDisposition Disp, FileAccess Access,
+ OpenFlags Flags, unsigned Mode) {
+ int OpenFlags = nativeOpenFlags(Disp, Flags, Access);
+
+ SmallString<128> Storage;
+ StringRef P = Name.toNullTerminatedStringRef(Storage);
+ if ((ResultFD = sys::RetryAfterSignal(-1, ::open, P.begin(), OpenFlags, Mode)) <
+ 0)
return std::error_code(errno, std::generic_category());
#ifndef O_CLOEXEC
- int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC);
- (void)r;
- assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed");
+ if (!(Flags & OF_ChildInherit)) {
+ int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC);
+ (void)r;
+ assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed");
+ }
#endif
+ return std::error_code();
+}
+
+Expected<int> openNativeFile(const Twine &Name, CreationDisposition Disp,
+ FileAccess Access, OpenFlags Flags,
+ unsigned Mode) {
+
+ int FD;
+ std::error_code EC = openFile(Name, FD, Disp, Access, Flags, Mode);
+ if (EC)
+ return errorCodeToError(EC);
+ return FD;
+}
+
+std::error_code openFileForRead(const Twine &Name, int &ResultFD,
+ OpenFlags Flags,
+ SmallVectorImpl<char> *RealPath) {
+ std::error_code EC =
+ openFile(Name, ResultFD, CD_OpenExisting, FA_Read, Flags, 0666);
+ if (EC)
+ return EC;
+
// Attempt to get the real name of the file, if the user asked
if(!RealPath)
return std::error_code();
@@ -763,6 +820,9 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD,
if (CharCount > 0)
RealPath->append(Buffer, Buffer + CharCount);
} else {
+ SmallString<128> Storage;
+ StringRef P = Name.toNullTerminatedStringRef(Storage);
+
// Use ::realpath to get the real path name
if (::realpath(P.begin(), Buffer) != nullptr)
RealPath->append(Buffer, Buffer + strlen(Buffer));
@@ -771,41 +831,18 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD,
return std::error_code();
}
-std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
- sys::fs::OpenFlags Flags, unsigned Mode) {
- // Verify that we don't have both "append" and "excl".
- assert((!(Flags & sys::fs::F_Excl) || !(Flags & sys::fs::F_Append)) &&
- "Cannot specify both 'excl' and 'append' file creation flags!");
-
- int OpenFlags = O_CREAT;
-
-#ifdef O_CLOEXEC
- OpenFlags |= O_CLOEXEC;
-#endif
-
- if (Flags & F_RW)
- OpenFlags |= O_RDWR;
- else
- OpenFlags |= O_WRONLY;
-
- if (Flags & F_Append)
- OpenFlags |= O_APPEND;
- else
- OpenFlags |= O_TRUNC;
-
- if (Flags & F_Excl)
- OpenFlags |= O_EXCL;
+Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags,
+ SmallVectorImpl<char> *RealPath) {
+ file_t ResultFD;
+ std::error_code EC = openFileForRead(Name, ResultFD, Flags, RealPath);
+ if (EC)
+ return errorCodeToError(EC);
+ return ResultFD;
+}
- SmallString<128> Storage;
- StringRef P = Name.toNullTerminatedStringRef(Storage);
- if ((ResultFD = sys::RetryAfterSignal(-1, open, P.begin(), OpenFlags, Mode)) < 0)
- return std::error_code(errno, std::generic_category());
-#ifndef O_CLOEXEC
- int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC);
- (void)r;
- assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed");
-#endif
- return std::error_code();
+void closeFile(file_t &F) {
+ ::close(F);
+ F = kInvalidFile;
}
template <typename T>
@@ -860,12 +897,12 @@ std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest,
return real_path(Storage, dest, false);
}
- int fd;
- std::error_code EC = openFileForRead(path, fd, &dest);
-
- if (EC)
- return EC;
- ::close(fd);
+ SmallString<128> Storage;
+ StringRef P = path.toNullTerminatedStringRef(Storage);
+ char Buffer[PATH_MAX];
+ if (::realpath(P.begin(), Buffer) == nullptr)
+ return std::error_code(errno, std::generic_category());
+ dest.append(Buffer, Buffer + strlen(Buffer));
return std::error_code();
}
diff --git a/lib/Support/Unix/Process.inc b/lib/Support/Unix/Process.inc
index e43650d707e3..fa515d44f3f2 100644
--- a/lib/Support/Unix/Process.inc
+++ b/lib/Support/Unix/Process.inc
@@ -14,6 +14,7 @@
#include "Unix.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Config/config.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/MutexGuard.h"
@@ -78,7 +79,7 @@ unsigned Process::getPageSize() {
#elif defined(HAVE_SYSCONF)
static long page_size = ::sysconf(_SC_PAGE_SIZE);
#else
-#warning Cannot get the page size on this machine
+#error Cannot get the page size on this machine
#endif
return static_cast<unsigned>(page_size);
}
@@ -172,15 +173,6 @@ Optional<std::string> Process::GetEnv(StringRef Name) {
return std::string(Val);
}
-std::error_code
-Process::GetArgumentVector(SmallVectorImpl<const char *> &ArgsOut,
- ArrayRef<const char *> ArgsIn,
- SpecificBumpPtrAllocator<char> &) {
- ArgsOut.append(ArgsIn.begin(), ArgsIn.end());
-
- return std::error_code();
-}
-
namespace {
class FDCloser {
public:
@@ -207,7 +199,7 @@ std::error_code Process::FixupStandardFileDescriptors() {
for (int StandardFD : StandardFDs) {
struct stat st;
errno = 0;
- if (RetryAfterSignal(-1, fstat, StandardFD, &st) < 0) {
+ if (RetryAfterSignal(-1, ::fstat, StandardFD, &st) < 0) {
assert(errno && "expected errno to be set if fstat failed!");
// fstat should return EBADF if the file descriptor is closed.
if (errno != EBADF)
@@ -219,7 +211,7 @@ std::error_code Process::FixupStandardFileDescriptors() {
assert(errno == EBADF && "expected errno to have EBADF at this point!");
if (NullFD < 0) {
- if ((NullFD = RetryAfterSignal(-1, open, "/dev/null", O_RDWR)) < 0)
+ if ((NullFD = RetryAfterSignal(-1, ::open, "/dev/null", O_RDWR)) < 0)
return std::error_code(errno, std::generic_category());
}
@@ -369,6 +361,21 @@ static bool terminalHasColors(int fd) {
// Return true if we found a color capabilities for the current terminal.
if (HasColors)
return true;
+#else
+ // When the terminfo database is not available, check if the current terminal
+ // is one of terminals that are known to support ANSI color escape codes.
+ if (const char *TermStr = std::getenv("TERM")) {
+ return StringSwitch<bool>(TermStr)
+ .Case("ansi", true)
+ .Case("cygwin", true)
+ .Case("linux", true)
+ .StartsWith("screen", true)
+ .StartsWith("xterm", true)
+ .StartsWith("vt100", true)
+ .StartsWith("rxvt", true)
+ .EndsWith("color", true)
+ .Default(false);
+ }
#endif
// Otherwise, be conservative.
diff --git a/lib/Support/Unix/Program.inc b/lib/Support/Unix/Program.inc
index 4f791991f3e8..d0abc3763e82 100644
--- a/lib/Support/Unix/Program.inc
+++ b/lib/Support/Unix/Program.inc
@@ -23,6 +23,7 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/StringSaver.h"
#include "llvm/Support/raw_ostream.h"
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
@@ -164,8 +165,18 @@ static void SetMemoryLimits(unsigned size) {
}
-static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args,
- const char **Envp, ArrayRef<Optional<StringRef>> Redirects,
+static std::vector<const char *>
+toNullTerminatedCStringArray(ArrayRef<StringRef> Strings, StringSaver &Saver) {
+ std::vector<const char *> Result;
+ for (StringRef S : Strings)
+ Result.push_back(Saver.save(S).data());
+ Result.push_back(nullptr);
+ return Result;
+}
+
+static bool Execute(ProcessInfo &PI, StringRef Program,
+ ArrayRef<StringRef> Args, Optional<ArrayRef<StringRef>> Env,
+ ArrayRef<Optional<StringRef>> Redirects,
unsigned MemoryLimit, std::string *ErrMsg) {
if (!llvm::sys::fs::exists(Program)) {
if (ErrMsg)
@@ -174,6 +185,18 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args,
return false;
}
+ BumpPtrAllocator Allocator;
+ StringSaver Saver(Allocator);
+ std::vector<const char *> ArgVector, EnvVector;
+ const char **Argv = nullptr;
+ const char **Envp = nullptr;
+ ArgVector = toNullTerminatedCStringArray(Args, Saver);
+ Argv = ArgVector.data();
+ if (Env) {
+ EnvVector = toNullTerminatedCStringArray(*Env, Saver);
+ Envp = EnvVector.data();
+ }
+
// If this OS has posix_spawn and there is no memory limit being implied, use
// posix_spawn. It is more efficient than fork/exec.
#ifdef HAVE_POSIX_SPAWN
@@ -227,7 +250,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args,
// positive.
pid_t PID = 0;
int Err = posix_spawn(&PID, Program.str().c_str(), FileActions,
- /*attrp*/nullptr, const_cast<char **>(Args),
+ /*attrp*/ nullptr, const_cast<char **>(Argv),
const_cast<char **>(Envp));
if (FileActions)
@@ -237,6 +260,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args,
return !MakeErrMsg(ErrMsg, "posix_spawn failed", Err);
PI.Pid = PID;
+ PI.Process = PID;
return true;
}
@@ -279,12 +303,10 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args,
// Execute!
std::string PathStr = Program;
if (Envp != nullptr)
- execve(PathStr.c_str(),
- const_cast<char **>(Args),
+ execve(PathStr.c_str(), const_cast<char **>(Argv),
const_cast<char **>(Envp));
else
- execv(PathStr.c_str(),
- const_cast<char **>(Args));
+ execv(PathStr.c_str(), const_cast<char **>(Argv));
// If the execve() failed, we should exit. Follow Unix protocol and
// return 127 if the executable was not found, and 126 otherwise.
// Use _exit rather than exit so that atexit functions and static
@@ -300,6 +322,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args,
}
PI.Pid = child;
+ PI.Process = child;
return true;
}
@@ -404,14 +427,14 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait,
return WaitResult;
}
- std::error_code sys::ChangeStdinToBinary(){
+std::error_code sys::ChangeStdinToBinary() {
// Do nothing, as Unix doesn't differentiate between text and binary.
- return std::error_code();
+ return std::error_code();
}
- std::error_code sys::ChangeStdoutToBinary(){
+std::error_code sys::ChangeStdoutToBinary() {
// Do nothing, as Unix doesn't differentiate between text and binary.
- return std::error_code();
+ return std::error_code();
}
std::error_code
@@ -432,29 +455,38 @@ llvm::sys::writeFileWithEncoding(StringRef FileName, StringRef Contents,
}
bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program,
- ArrayRef<const char *> Args) {
+ ArrayRef<StringRef> Args) {
static long ArgMax = sysconf(_SC_ARG_MAX);
+ // POSIX requires that _POSIX_ARG_MAX is 4096, which is the lowest possible
+ // value for ARG_MAX on a POSIX compliant system.
+ static long ArgMin = _POSIX_ARG_MAX;
+
+ // This the same baseline used by xargs.
+ long EffectiveArgMax = 128 * 1024;
+
+ if (EffectiveArgMax > ArgMax)
+ EffectiveArgMax = ArgMax;
+ else if (EffectiveArgMax < ArgMin)
+ EffectiveArgMax = ArgMin;
// System says no practical limit.
if (ArgMax == -1)
return true;
// Conservatively account for space required by environment variables.
- long HalfArgMax = ArgMax / 2;
+ long HalfArgMax = EffectiveArgMax / 2;
size_t ArgLength = Program.size() + 1;
- for (const char* Arg : Args) {
- size_t length = strlen(Arg);
-
+ for (StringRef Arg : Args) {
// Ensure that we do not exceed the MAX_ARG_STRLEN constant on Linux, which
// does not have a constant unlike what the man pages would have you
// believe. Since this limit is pretty high, perform the check
// unconditionally rather than trying to be aggressive and limiting it to
// Linux only.
- if (length >= (32 * 4096))
+ if (Arg.size() >= (32 * 4096))
return false;
- ArgLength += length + 1;
+ ArgLength += Arg.size() + 1;
if (ArgLength > size_t(HalfArgMax)) {
return false;
}
diff --git a/lib/Support/Unix/Signals.inc b/lib/Support/Unix/Signals.inc
index aaf760c5b616..de26695d64ea 100644
--- a/lib/Support/Unix/Signals.inc
+++ b/lib/Support/Unix/Signals.inc
@@ -11,9 +11,31 @@
// Unix signals occurring while your program is running.
//
//===----------------------------------------------------------------------===//
+//
+// This file is extremely careful to only do signal-safe things while in a
+// signal handler. In particular, memory allocation and acquiring a mutex
+// while in a signal handler should never occur. ManagedStatic isn't usable from
+// a signal handler for 2 reasons:
+//
+// 1. Creating a new one allocates.
+// 2. The signal handler could fire while llvm_shutdown is being processed, in
+// which case the ManagedStatic is in an unknown state because it could
+// already have been destroyed, or be in the process of being destroyed.
+//
+// Modifying the behavior of the signal handlers (such as registering new ones)
+// can acquire a mutex, but all this guarantees is that the signal handler
+// behavior is only modified by one thread at a time. A signal handler can still
+// fire while this occurs!
+//
+// Adding work to a signal handler requires lock-freedom (and assume atomics are
+// always lock-free) because the signal handler could fire while new work is
+// being added.
+//
+//===----------------------------------------------------------------------===//
#include "Unix.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/Config/config.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
@@ -59,24 +81,133 @@ using namespace llvm;
static RETSIGTYPE SignalHandler(int Sig); // defined below.
-static ManagedStatic<sys::SmartMutex<true> > SignalsMutex;
+/// The function to call if ctrl-c is pressed.
+using InterruptFunctionType = void (*)();
+static std::atomic<InterruptFunctionType> InterruptFunction =
+ ATOMIC_VAR_INIT(nullptr);
+
+namespace {
+/// Signal-safe removal of files.
+/// Inserting and erasing from the list isn't signal-safe, but removal of files
+/// themselves is signal-safe. Memory is freed when the head is freed, deletion
+/// is therefore not signal-safe either.
+class FileToRemoveList {
+ std::atomic<char *> Filename = ATOMIC_VAR_INIT(nullptr);
+ std::atomic<FileToRemoveList *> Next = ATOMIC_VAR_INIT(nullptr);
+
+ FileToRemoveList() = default;
+ // Not signal-safe.
+ FileToRemoveList(const std::string &str) : Filename(strdup(str.c_str())) {}
+
+public:
+ // Not signal-safe.
+ ~FileToRemoveList() {
+ if (FileToRemoveList *N = Next.exchange(nullptr))
+ delete N;
+ if (char *F = Filename.exchange(nullptr))
+ free(F);
+ }
+
+ // Not signal-safe.
+ static void insert(std::atomic<FileToRemoveList *> &Head,
+ const std::string &Filename) {
+ // Insert the new file at the end of the list.
+ FileToRemoveList *NewHead = new FileToRemoveList(Filename);
+ std::atomic<FileToRemoveList *> *InsertionPoint = &Head;
+ FileToRemoveList *OldHead = nullptr;
+ while (!InsertionPoint->compare_exchange_strong(OldHead, NewHead)) {
+ InsertionPoint = &OldHead->Next;
+ OldHead = nullptr;
+ }
+ }
+
+ // Not signal-safe.
+ static void erase(std::atomic<FileToRemoveList *> &Head,
+ const std::string &Filename) {
+ // Use a lock to avoid concurrent erase: the comparison would access
+ // free'd memory.
+ static ManagedStatic<sys::SmartMutex<true>> Lock;
+ sys::SmartScopedLock<true> Writer(*Lock);
+
+ for (FileToRemoveList *Current = Head.load(); Current;
+ Current = Current->Next.load()) {
+ if (char *OldFilename = Current->Filename.load()) {
+ if (OldFilename != Filename)
+ continue;
+ // Leave an empty filename.
+ OldFilename = Current->Filename.exchange(nullptr);
+ // The filename might have become null between the time we
+ // compared it and we exchanged it.
+ if (OldFilename)
+ free(OldFilename);
+ }
+ }
+ }
-/// InterruptFunction - The function to call if ctrl-c is pressed.
-static void (*InterruptFunction)() = nullptr;
+ // Signal-safe.
+ static void removeAllFiles(std::atomic<FileToRemoveList *> &Head) {
+ // If cleanup were to occur while we're removing files we'd have a bad time.
+ // Make sure we're OK by preventing cleanup from doing anything while we're
+ // removing files. If cleanup races with us and we win we'll have a leak,
+ // but we won't crash.
+ FileToRemoveList *OldHead = Head.exchange(nullptr);
+
+ for (FileToRemoveList *currentFile = OldHead; currentFile;
+ currentFile = currentFile->Next.load()) {
+ // If erasing was occuring while we're trying to remove files we'd look
+ // at free'd data. Take away the path and put it back when done.
+ if (char *path = currentFile->Filename.exchange(nullptr)) {
+ // Get the status so we can determine if it's a file or directory. If we
+ // can't stat the file, ignore it.
+ struct stat buf;
+ if (stat(path, &buf) != 0)
+ continue;
+
+ // If this is not a regular file, ignore it. We want to prevent removal
+ // of special files like /dev/null, even if the compiler is being run
+ // with the super-user permissions.
+ if (!S_ISREG(buf.st_mode))
+ continue;
+
+ // Otherwise, remove the file. We ignore any errors here as there is
+ // nothing else we can do.
+ unlink(path);
+
+ // We're done removing the file, erasing can safely proceed.
+ currentFile->Filename.exchange(path);
+ }
+ }
-static ManagedStatic<std::vector<std::string>> FilesToRemove;
+ // We're done removing files, cleanup can safely proceed.
+ Head.exchange(OldHead);
+ }
+};
+static std::atomic<FileToRemoveList *> FilesToRemove = ATOMIC_VAR_INIT(nullptr);
+
+/// Clean up the list in a signal-friendly manner.
+/// Recall that signals can fire during llvm_shutdown. If this occurs we should
+/// either clean something up or nothing at all, but we shouldn't crash!
+struct FilesToRemoveCleanup {
+ // Not signal-safe.
+ ~FilesToRemoveCleanup() {
+ FileToRemoveList *Head = FilesToRemove.exchange(nullptr);
+ if (Head)
+ delete Head;
+ }
+};
+} // namespace
static StringRef Argv0;
-// IntSigs - Signals that represent requested termination. There's no bug
-// or failure, or if there is, it's not our direct responsibility. For whatever
-// reason, our continued execution is no longer desirable.
+// Signals that represent requested termination. There's no bug or failure, or
+// if there is, it's not our direct responsibility. For whatever reason, our
+// continued execution is no longer desirable.
static const int IntSigs[] = {
SIGHUP, SIGINT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2
};
-// KillSigs - Signals that represent that we have a bug, and our prompt
-// termination has been ordered.
+// Signals that represent that we have a bug, and our prompt termination has
+// been ordered.
static const int KillSigs[] = {
SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGBUS, SIGSEGV, SIGQUIT
#ifdef SIGSYS
@@ -93,30 +224,12 @@ static const int KillSigs[] = {
#endif
};
-static unsigned NumRegisteredSignals = 0;
+static std::atomic<unsigned> NumRegisteredSignals = ATOMIC_VAR_INIT(0);
static struct {
struct sigaction SA;
int SigNo;
} RegisteredSignalInfo[array_lengthof(IntSigs) + array_lengthof(KillSigs)];
-
-static void RegisterHandler(int Signal) {
- assert(NumRegisteredSignals < array_lengthof(RegisteredSignalInfo) &&
- "Out of space for signal handlers!");
-
- struct sigaction NewHandler;
-
- NewHandler.sa_handler = SignalHandler;
- NewHandler.sa_flags = SA_NODEFER | SA_RESETHAND | SA_ONSTACK;
- sigemptyset(&NewHandler.sa_mask);
-
- // Install the new handler, save the old one in RegisteredSignalInfo.
- sigaction(Signal, &NewHandler,
- &RegisteredSignalInfo[NumRegisteredSignals].SA);
- RegisteredSignalInfo[NumRegisteredSignals].SigNo = Signal;
- ++NumRegisteredSignals;
-}
-
#if defined(HAVE_SIGALTSTACK)
// Hold onto both the old and new alternate signal stack so that it's not
// reported as a leak. We don't make any attempt to remove our alt signal
@@ -138,7 +251,7 @@ static void CreateSigAltStack() {
return;
stack_t AltStack = {};
- AltStack.ss_sp = reinterpret_cast<char *>(malloc(AltStackSize));
+ AltStack.ss_sp = static_cast<char *>(safe_malloc(AltStackSize));
NewAltStackPointer = AltStack.ss_sp; // Save to avoid reporting a leak.
AltStack.ss_size = AltStackSize;
if (sigaltstack(&AltStack, &OldAltStack) != 0)
@@ -148,64 +261,59 @@ static void CreateSigAltStack() {
static void CreateSigAltStack() {}
#endif
-static void RegisterHandlers() {
- sys::SmartScopedLock<true> Guard(*SignalsMutex);
+static void RegisterHandlers() { // Not signal-safe.
+ // The mutex prevents other threads from registering handlers while we're
+ // doing it. We also have to protect the handlers and their count because
+ // a signal handler could fire while we're registeting handlers.
+ static ManagedStatic<sys::SmartMutex<true>> SignalHandlerRegistrationMutex;
+ sys::SmartScopedLock<true> Guard(*SignalHandlerRegistrationMutex);
// If the handlers are already registered, we're done.
- if (NumRegisteredSignals != 0) return;
+ if (NumRegisteredSignals.load() != 0)
+ return;
// Create an alternate stack for signal handling. This is necessary for us to
// be able to reliably handle signals due to stack overflow.
CreateSigAltStack();
- for (auto S : IntSigs) RegisterHandler(S);
- for (auto S : KillSigs) RegisterHandler(S);
+ auto registerHandler = [&](int Signal) {
+ unsigned Index = NumRegisteredSignals.load();
+ assert(Index < array_lengthof(RegisteredSignalInfo) &&
+ "Out of space for signal handlers!");
+
+ struct sigaction NewHandler;
+
+ NewHandler.sa_handler = SignalHandler;
+ NewHandler.sa_flags = SA_NODEFER | SA_RESETHAND | SA_ONSTACK;
+ sigemptyset(&NewHandler.sa_mask);
+
+ // Install the new handler, save the old one in RegisteredSignalInfo.
+ sigaction(Signal, &NewHandler, &RegisteredSignalInfo[Index].SA);
+ RegisteredSignalInfo[Index].SigNo = Signal;
+ ++NumRegisteredSignals;
+ };
+
+ for (auto S : IntSigs)
+ registerHandler(S);
+ for (auto S : KillSigs)
+ registerHandler(S);
}
static void UnregisterHandlers() {
// Restore all of the signal handlers to how they were before we showed up.
- for (unsigned i = 0, e = NumRegisteredSignals; i != e; ++i)
+ for (unsigned i = 0, e = NumRegisteredSignals.load(); i != e; ++i) {
sigaction(RegisteredSignalInfo[i].SigNo,
&RegisteredSignalInfo[i].SA, nullptr);
- NumRegisteredSignals = 0;
+ --NumRegisteredSignals;
+ }
}
-
-/// RemoveFilesToRemove - Process the FilesToRemove list. This function
-/// should be called with the SignalsMutex lock held.
-/// NB: This must be an async signal safe function. It cannot allocate or free
-/// memory, even in debug builds.
+/// Process the FilesToRemove list.
static void RemoveFilesToRemove() {
- // Avoid constructing ManagedStatic in the signal handler.
- // If FilesToRemove is not constructed, there are no files to remove.
- if (!FilesToRemove.isConstructed())
- return;
-
- // We avoid iterators in case of debug iterators that allocate or release
- // memory.
- std::vector<std::string>& FilesToRemoveRef = *FilesToRemove;
- for (unsigned i = 0, e = FilesToRemoveRef.size(); i != e; ++i) {
- const char *path = FilesToRemoveRef[i].c_str();
-
- // Get the status so we can determine if it's a file or directory. If we
- // can't stat the file, ignore it.
- struct stat buf;
- if (stat(path, &buf) != 0)
- continue;
-
- // If this is not a regular file, ignore it. We want to prevent removal of
- // special files like /dev/null, even if the compiler is being run with the
- // super-user permissions.
- if (!S_ISREG(buf.st_mode))
- continue;
-
- // Otherwise, remove the file. We ignore any errors here as there is nothing
- // else we can do.
- unlink(path);
- }
+ FileToRemoveList::removeAllFiles(FilesToRemove);
}
-// SignalHandler - The signal handler that runs.
+// The signal handler that runs.
static RETSIGTYPE SignalHandler(int Sig) {
// Restore the signal behavior to default, so that the program actually
// crashes when we return and the signal reissues. This also ensures that if
@@ -219,20 +327,13 @@ static RETSIGTYPE SignalHandler(int Sig) {
sigprocmask(SIG_UNBLOCK, &SigMask, nullptr);
{
- unique_lock<sys::SmartMutex<true>> Guard(*SignalsMutex);
RemoveFilesToRemove();
if (std::find(std::begin(IntSigs), std::end(IntSigs), Sig)
!= std::end(IntSigs)) {
- if (InterruptFunction) {
- void (*IF)() = InterruptFunction;
- Guard.unlock();
- InterruptFunction = nullptr;
- IF(); // run the interrupt function.
- return;
- }
+ if (auto OldInterruptFunction = InterruptFunction.exchange(nullptr))
+ return OldInterruptFunction();
- Guard.unlock();
raise(Sig); // Execute the default handler.
return;
}
@@ -252,45 +353,36 @@ static RETSIGTYPE SignalHandler(int Sig) {
}
void llvm::sys::RunInterruptHandlers() {
- sys::SmartScopedLock<true> Guard(*SignalsMutex);
RemoveFilesToRemove();
}
void llvm::sys::SetInterruptFunction(void (*IF)()) {
- {
- sys::SmartScopedLock<true> Guard(*SignalsMutex);
- InterruptFunction = IF;
- }
+ InterruptFunction.exchange(IF);
RegisterHandlers();
}
-// RemoveFileOnSignal - The public API
+// The public API
bool llvm::sys::RemoveFileOnSignal(StringRef Filename,
std::string* ErrMsg) {
- {
- sys::SmartScopedLock<true> Guard(*SignalsMutex);
- FilesToRemove->push_back(Filename);
- }
-
+ // Ensure that cleanup will occur as soon as one file is added.
+ static ManagedStatic<FilesToRemoveCleanup> FilesToRemoveCleanup;
+ *FilesToRemoveCleanup;
+ FileToRemoveList::insert(FilesToRemove, Filename.str());
RegisterHandlers();
return false;
}
-// DontRemoveFileOnSignal - The public API
+// The public API
void llvm::sys::DontRemoveFileOnSignal(StringRef Filename) {
- sys::SmartScopedLock<true> Guard(*SignalsMutex);
- std::vector<std::string>::reverse_iterator RI =
- find(reverse(*FilesToRemove), Filename);
- std::vector<std::string>::iterator I = FilesToRemove->end();
- if (RI != FilesToRemove->rend())
- I = FilesToRemove->erase(RI.base()-1);
+ FileToRemoveList::erase(FilesToRemove, Filename.str());
}
-/// AddSignalHandler - Add a function to be called when a signal is delivered
-/// to the process. The handler can have a cookie passed to it to identify
-/// what instance of the handler it is.
-void llvm::sys::AddSignalHandler(void (*FnPtr)(void *), void *Cookie) {
- CallBacksToRun->push_back(std::make_pair(FnPtr, Cookie));
+/// Add a function to be called when a signal is delivered to the process. The
+/// handler can have a cookie passed to it to identify what instance of the
+/// handler it is.
+void llvm::sys::AddSignalHandler(sys::SignalHandlerCallback FnPtr,
+ void *Cookie) { // Signal-safe.
+ insertSignalHandler(FnPtr, Cookie);
RegisterHandlers();
}
@@ -383,8 +475,8 @@ static int unwindBacktrace(void **StackTrace, int MaxEntries) {
}
#endif
-// PrintStackTrace - In the case of a program crash or fault, print out a stack
-// trace so that the user has an indication of why and where we died.
+// In the case of a program crash or fault, print out a stack trace so that the
+// user has an indication of why and where we died.
//
// On glibc systems we have the 'backtrace' function, which works nicely, but
// doesn't demangle symbols.
@@ -463,8 +555,8 @@ static void PrintStackTraceSignalHandler(void *) {
void llvm::sys::DisableSystemDialogsOnCrash() {}
-/// PrintStackTraceOnErrorSignal - When an error signal (such as SIGABRT or
-/// SIGSEGV) is delivered to the process, print a stack trace and then exit.
+/// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the
+/// process, print a stack trace and then exit.
void llvm::sys::PrintStackTraceOnErrorSignal(StringRef Argv0,
bool DisableCrashReporting) {
::Argv0 = Argv0;
diff --git a/lib/Support/Unix/ThreadLocal.inc b/lib/Support/Unix/ThreadLocal.inc
index 31c3f3835b29..a6564f0fa281 100644
--- a/lib/Support/Unix/ThreadLocal.inc
+++ b/lib/Support/Unix/ThreadLocal.inc
@@ -16,6 +16,8 @@
//=== is guaranteed to work on *all* UNIX variants.
//===----------------------------------------------------------------------===//
+#include "llvm/Config/config.h"
+
#if defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD_GETSPECIFIC)
#include <cassert>
diff --git a/lib/Support/Unix/Threading.inc b/lib/Support/Unix/Threading.inc
index 7369cff8466c..2d49ce1ad747 100644
--- a/lib/Support/Unix/Threading.inc
+++ b/lib/Support/Unix/Threading.inc
@@ -21,8 +21,8 @@
#include <pthread.h>
-#if defined(__FreeBSD__)
-#include <pthread_np.h> // For pthread_getthreadid_np()
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <pthread_np.h> // For pthread_getthreadid_np() / pthread_set_name_np()
#endif
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
@@ -98,8 +98,6 @@ uint64_t llvm::get_threadid() {
return uint64_t(gettid());
#elif defined(__linux__)
return uint64_t(syscall(SYS_gettid));
-#elif defined(LLVM_ON_WIN32)
- return uint64_t(::GetCurrentThreadId());
#else
return uint64_t(pthread_self());
#endif
@@ -119,6 +117,8 @@ static constexpr uint32_t get_max_thread_name_length_impl() {
#endif
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
return 16;
+#elif defined(__OpenBSD__)
+ return 32;
#else
return 0;
#endif
@@ -138,8 +138,9 @@ void llvm::set_thread_name(const Twine &Name) {
// terminated, but additionally the end of a long thread name will usually
// be more unique than the beginning, since a common pattern is for similar
// threads to share a common prefix.
+ // Note that the name length includes the null terminator.
if (get_max_thread_name_length() > 0)
- NameStr = NameStr.take_back(get_max_thread_name_length());
+ NameStr = NameStr.take_back(get_max_thread_name_length() - 1);
(void)NameStr;
#if defined(__linux__)
#if (defined(__GLIBC__) && defined(_GNU_SOURCE)) || defined(__ANDROID__)
@@ -147,7 +148,7 @@ void llvm::set_thread_name(const Twine &Name) {
::pthread_setname_np(::pthread_self(), NameStr.data());
#endif
#endif
-#elif defined(__FreeBSD__)
+#elif defined(__FreeBSD__) || defined(__OpenBSD__)
::pthread_set_name_np(::pthread_self(), NameStr.data());
#elif defined(__NetBSD__)
::pthread_setname_np(::pthread_self(), "%s",
@@ -175,7 +176,7 @@ void llvm::get_thread_name(SmallVectorImpl<char> &Name) {
if (kp == nullptr || (error != 0 && errno == ENOMEM)) {
// Add extra space in case threads are added before next call.
len += sizeof(*kp) + len / 10;
- nkp = (struct kinfo_proc *)realloc(kp, len);
+ nkp = (struct kinfo_proc *)::realloc(kp, len);
if (nkp == nullptr) {
free(kp);
return;
@@ -203,7 +204,6 @@ void llvm::get_thread_name(SmallVectorImpl<char> &Name) {
Name.append(buf, buf + strlen(buf));
#elif defined(__linux__)
-#if (defined(__GLIBC__) && defined(_GNU_SOURCE)) || defined(__ANDROID__)
#if HAVE_PTHREAD_GETNAME_NP
constexpr uint32_t len = get_max_thread_name_length_impl();
char Buffer[len] = {'\0'}; // FIXME: working around MSan false positive.
@@ -211,5 +211,4 @@ void llvm::get_thread_name(SmallVectorImpl<char> &Name) {
Name.append(Buffer, Buffer + strlen(Buffer));
#endif
#endif
-#endif
}
diff --git a/lib/Support/Unix/Unix.h b/lib/Support/Unix/Unix.h
index 239a6d60aaef..0c5d4de556d5 100644
--- a/lib/Support/Unix/Unix.h
+++ b/lib/Support/Unix/Unix.h
@@ -56,7 +56,7 @@
/// This function builds an error message into \p ErrMsg using the \p prefix
/// string and the Unix error number given by \p errnum. If errnum is -1, the
/// default then the value of errno is used.
-/// @brief Make an error message
+/// Make an error message
///
/// If the error number can be converted to a string, it will be
/// separated from prefix by ": ".
diff --git a/lib/Support/Unix/Watchdog.inc b/lib/Support/Unix/Watchdog.inc
index 5d89c0e51b11..f4253391d952 100644
--- a/lib/Support/Unix/Watchdog.inc
+++ b/lib/Support/Unix/Watchdog.inc
@@ -11,6 +11,8 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/Config/config.h"
+
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif