Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions llvm/include/llvm/Support/FileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,12 @@ LLVM_ABI Expected<file_t>
openNativeFileForRead(const Twine &Name, OpenFlags Flags = OF_None,
SmallVectorImpl<char> *RealPath = nullptr);

/// An enumeration for the lock kind.
enum class LockKind {
Exclusive, // Exclusive/writer lock
Shared // Shared/reader lock
};

/// Try to locks the file during the specified time.
///
/// This function implements advisory locking on entire file. If it returns
Expand All @@ -1184,6 +1190,7 @@ openNativeFileForRead(const Twine &Name, OpenFlags Flags = OF_None,
/// @param Timeout Time in milliseconds that the process should wait before
/// reporting lock failure. Zero value means try to get lock only
/// once.
/// @param Kind The kind of the lock used (exclusive/shared).
/// @returns errc::success if lock is successfully obtained,
/// errc::no_lock_available if the file cannot be locked, or platform-specific
/// error_code otherwise.
Expand All @@ -1194,12 +1201,15 @@ openNativeFileForRead(const Twine &Name, OpenFlags Flags = OF_None,
/// descriptor.
LLVM_ABI std::error_code
tryLockFile(int FD,
std::chrono::milliseconds Timeout = std::chrono::milliseconds(0));
std::chrono::milliseconds Timeout = std::chrono::milliseconds(0),
LockKind Kind = LockKind::Exclusive);

/// Lock the file.
///
/// This function acts as @ref tryLockFile but it waits infinitely.
LLVM_ABI std::error_code lockFile(int FD);
/// \param FD file descriptor to use for locking.
/// \param Kind of lock to used (exclusive/shared).
LLVM_ABI std::error_code lockFile(int FD, LockKind Kind = LockKind::Exclusive);

/// Unlock the file.
///
Expand Down
25 changes: 21 additions & 4 deletions llvm/lib/Support/Unix/Path.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1230,13 +1230,21 @@ Expected<size_t> readNativeFileSlice(file_t FD, MutableArrayRef<char> Buf,
return NumRead;
}

std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout) {
std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout,
LockKind Kind) {
auto Start = std::chrono::steady_clock::now();
auto End = Start + Timeout;
do {
struct flock Lock;
memset(&Lock, 0, sizeof(Lock));
Lock.l_type = F_WRLCK;
switch (Kind) {
case LockKind::Exclusive:
Lock.l_type = F_WRLCK;
break;
case LockKind::Shared:
Lock.l_type = F_RDLCK;
break;
}
Lock.l_whence = SEEK_SET;
Lock.l_start = 0;
Lock.l_len = 0;
Expand All @@ -1245,15 +1253,24 @@ std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout) {
int Error = errno;
if (Error != EACCES && Error != EAGAIN)
return std::error_code(Error, std::generic_category());
if (Timeout.count() == 0)
break;
usleep(1000);
} while (std::chrono::steady_clock::now() < End);
return make_error_code(errc::no_lock_available);
}

std::error_code lockFile(int FD) {
std::error_code lockFile(int FD, LockKind Kind) {
struct flock Lock;
memset(&Lock, 0, sizeof(Lock));
Lock.l_type = F_WRLCK;
switch (Kind) {
case LockKind::Exclusive:
Lock.l_type = F_WRLCK;
break;
case LockKind::Shared:
Lock.l_type = F_RDLCK;
break;
}
Lock.l_whence = SEEK_SET;
Lock.l_start = 0;
Lock.l_len = 0;
Expand Down
12 changes: 8 additions & 4 deletions llvm/lib/Support/Windows/Path.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1337,8 +1337,10 @@ Expected<size_t> readNativeFileSlice(file_t FileHandle,
return readNativeFileImpl(FileHandle, Buf, &Overlapped);
}

std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout) {
DWORD Flags = LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY;
std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout,
LockKind Kind) {
DWORD Flags = Kind == LockKind::Exclusive ? LOCKFILE_EXCLUSIVE_LOCK : 0;
Flags |= LOCKFILE_FAIL_IMMEDIATELY;
OVERLAPPED OV = {};
file_t File = convertFDToNativeFile(FD);
auto Start = std::chrono::steady_clock::now();
Expand All @@ -1348,6 +1350,8 @@ std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout) {
return std::error_code();
DWORD Error = ::GetLastError();
if (Error == ERROR_LOCK_VIOLATION) {
if (Timeout.count() == 0)
break;
::Sleep(1);
continue;
}
Expand All @@ -1356,8 +1360,8 @@ std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout) {
return mapWindowsError(ERROR_LOCK_VIOLATION);
}

std::error_code lockFile(int FD) {
DWORD Flags = LOCKFILE_EXCLUSIVE_LOCK;
std::error_code lockFile(int FD, LockKind Kind) {
DWORD Flags = Kind == LockKind::Exclusive ? LOCKFILE_EXCLUSIVE_LOCK : 0;
OVERLAPPED OV = {};
file_t File = convertFDToNativeFile(FD);
if (::LockFileEx(File, Flags, 0, MAXDWORD, MAXDWORD, &OV))
Expand Down
83 changes: 83 additions & 0 deletions llvm/unittests/Support/ProgramTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/ExponentialBackoff.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
Expand Down Expand Up @@ -573,6 +574,88 @@ TEST_F(ProgramEnvTest, TestLockFile) {
sys::fs::remove(LockedFile);
}

TEST_F(ProgramEnvTest, TestLockFileExclusive) {
using namespace llvm::sys;
using namespace std::chrono_literals;

if (const char *LockedFile = getenv("LLVM_PROGRAM_TEST_LOCKED_FILE")) {
// Child process.
int FD2;
ASSERT_NO_ERROR(fs::openFileForReadWrite(LockedFile, FD2,
fs::CD_OpenExisting, fs::OF_None));

// File should currently be non-exclusive locked by the main process, thus
// trying to acquire exclusive lock will fail and trying to acquire
// non-exclusive will succeed.
EXPECT_TRUE(
fs::tryLockFile(FD2, std::chrono::seconds(0), fs::LockKind::Exclusive));

EXPECT_FALSE(
fs::tryLockFile(FD2, std::chrono::seconds(0), fs::LockKind::Shared));

close(FD2);
// Write a file to indicate just finished.
std::string FinishFile = std::string(LockedFile) + "-finished";
int FD3;
ASSERT_NO_ERROR(fs::openFileForReadWrite(FinishFile, FD3, fs::CD_CreateNew,
fs::OF_None));
close(FD3);
exit(0);
}

// Create file that will be locked.
SmallString<64> LockedFile;
int FD1;
ASSERT_NO_ERROR(
fs::createUniqueDirectory("TestLockFileExclusive", LockedFile));
sys::path::append(LockedFile, "file");
ASSERT_NO_ERROR(
fs::openFileForReadWrite(LockedFile, FD1, fs::CD_CreateNew, fs::OF_None));

std::string Executable =
sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
StringRef argv[] = {Executable,
"--gtest_filter=ProgramEnvTest.TestLockFileExclusive"};

// Add LLVM_PROGRAM_TEST_LOCKED_FILE to the environment of the child.
std::string EnvVar = "LLVM_PROGRAM_TEST_LOCKED_FILE=";
EnvVar += LockedFile.str();
addEnvVar(EnvVar);

// Lock the file.
ASSERT_NO_ERROR(
fs::tryLockFile(FD1, std::chrono::seconds(0), fs::LockKind::Exclusive));

std::string Error;
bool ExecutionFailed;
ProcessInfo PI2 = ExecuteNoWait(Executable, argv, getEnviron(), {}, 0, &Error,
&ExecutionFailed);
ASSERT_FALSE(ExecutionFailed) << Error;
ASSERT_TRUE(Error.empty());
ASSERT_NE(PI2.Pid, ProcessInfo::InvalidPid) << "Invalid process id";

std::string FinishFile = std::string(LockedFile) + "-finished";
// Wait till child process writes the file to indicate the job finished.
bool Finished = false;
ExponentialBackoff Backoff(5s); // timeout 5s.
do {
if (fs::exists(FinishFile)) {
Finished = true;
break;
}
} while (Backoff.waitForNextAttempt());

ASSERT_TRUE(Finished);
ASSERT_NO_ERROR(fs::unlockFile(FD1));
ProcessInfo WaitResult = llvm::sys::Wait(PI2, /*SecondsToWait=*/1, &Error);
ASSERT_TRUE(Error.empty());
ASSERT_EQ(0, WaitResult.ReturnCode);
ASSERT_EQ(WaitResult.Pid, PI2.Pid);
sys::fs::remove(LockedFile);
sys::fs::remove(FinishFile);
sys::fs::remove_directories(sys::path::parent_path(LockedFile));
}

TEST_F(ProgramEnvTest, TestExecuteWithNoStacktraceHandler) {
using namespace llvm::sys;

Expand Down