LLVM 22.0.0git
OnDiskCommon.cpp
Go to the documentation of this file.
1//===- OnDiskCommon.cpp ---------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "OnDiskCommon.h"
10#include "llvm/Config/config.h"
11#include "llvm/Support/Error.h"
13#include <thread>
14
15#if __has_include(<sys/file.h>)
16#include <sys/file.h>
17#ifdef LOCK_SH
18#define HAVE_FLOCK 1
19#else
20#define HAVE_FLOCK 0
21#endif
22#endif
23
24#if __has_include(<fcntl.h>)
25#include <fcntl.h>
26#endif
27
28using namespace llvm;
29
30std::error_code cas::ondisk::lockFileThreadSafe(int FD,
31 sys::fs::LockKind Kind) {
32#if HAVE_FLOCK
33 if (flock(FD, Kind == sys::fs::LockKind::Exclusive ? LOCK_EX : LOCK_SH) == 0)
34 return std::error_code();
35 return std::error_code(errno, std::generic_category());
36#elif defined(_WIN32)
37 // On Windows this implementation is thread-safe.
38 return sys::fs::lockFile(FD, Kind);
39#else
40 return make_error_code(std::errc::no_lock_available);
41#endif
42}
43
44std::error_code cas::ondisk::unlockFileThreadSafe(int FD) {
45#if HAVE_FLOCK
46 if (flock(FD, LOCK_UN) == 0)
47 return std::error_code();
48 return std::error_code(errno, std::generic_category());
49#elif defined(_WIN32)
50 // On Windows this implementation is thread-safe.
51 return sys::fs::unlockFile(FD);
52#else
53 return make_error_code(std::errc::no_lock_available);
54#endif
55}
56
57std::error_code
58cas::ondisk::tryLockFileThreadSafe(int FD, std::chrono::milliseconds Timeout,
59 sys::fs::LockKind Kind) {
60#if HAVE_FLOCK
61 auto Start = std::chrono::steady_clock::now();
62 auto End = Start + Timeout;
63 do {
64 if (flock(FD, (Kind == sys::fs::LockKind::Exclusive ? LOCK_EX : LOCK_SH) |
65 LOCK_NB) == 0)
66 return std::error_code();
67 int Error = errno;
68 if (Error == EWOULDBLOCK) {
69 // Match sys::fs::tryLockFile, which sleeps for 1 ms per attempt.
70 std::this_thread::sleep_for(std::chrono::milliseconds(1));
71 continue;
72 }
73 return std::error_code(Error, std::generic_category());
74 } while (std::chrono::steady_clock::now() < End);
75 return make_error_code(std::errc::no_lock_available);
76#elif defined(_WIN32)
77 // On Windows this implementation is thread-safe.
78 return sys::fs::tryLockFile(FD, Timeout, Kind);
79#else
80 return make_error_code(std::errc::no_lock_available);
81#endif
82}
83
85 size_t NewSize) {
86 auto CreateError = [&](std::error_code EC) -> Expected<size_t> {
87 if (EC == std::errc::not_supported)
88 // Ignore ENOTSUP in case the filesystem cannot preallocate.
89 return NewSize;
90#if defined(HAVE_POSIX_FALLOCATE)
91 if (EC == std::errc::invalid_argument && CurrentSize < NewSize && // len > 0
92 NewSize < std::numeric_limits<off_t>::max()) // 0 <= offset, len < max
93 // Prior to 2024, POSIX required EINVAL for cases that should be ENOTSUP,
94 // so handle it the same as above if it is not one of the other ways to
95 // get EINVAL.
96 return NewSize;
97#endif
98 return createStringError(EC,
99 "failed to allocate to CAS file: " + EC.message());
100 };
101#if defined(HAVE_POSIX_FALLOCATE)
102 // Note: posix_fallocate returns its error directly, not via errno.
103 if (int Err = posix_fallocate(FD, CurrentSize, NewSize - CurrentSize))
104 return CreateError(std::error_code(Err, std::generic_category()));
105 return NewSize;
106#elif defined(__APPLE__)
107 fstore_t FAlloc;
108 FAlloc.fst_flags = F_ALLOCATEALL;
109#if defined(F_ALLOCATEPERSIST) && \
110 defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
111 __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 130000
112 // F_ALLOCATEPERSIST is introduced in macOS 13.
113 FAlloc.fst_flags |= F_ALLOCATEPERSIST;
114#endif
115 FAlloc.fst_posmode = F_PEOFPOSMODE;
116 FAlloc.fst_offset = 0;
117 FAlloc.fst_length = NewSize - CurrentSize;
118 FAlloc.fst_bytesalloc = 0;
119 if (fcntl(FD, F_PREALLOCATE, &FAlloc))
120 return CreateError(errnoAsErrorCode());
121 assert(CurrentSize + FAlloc.fst_bytesalloc >= NewSize);
122 return CurrentSize + FAlloc.fst_bytesalloc;
123#else
124 (void)CreateError; // Silence unused variable.
125 return NewSize; // Pretend it worked.
126#endif
127}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
Tagged union holding either a T or a Error.
Definition Error.h:485
std::error_code lockFileThreadSafe(int FD, llvm::sys::fs::LockKind Kind)
Thread-safe alternative to sys::fs::lockFile.
std::error_code unlockFileThreadSafe(int FD)
Thread-safe alternative to sys::fs::unlockFile.
std::error_code tryLockFileThreadSafe(int FD, std::chrono::milliseconds Timeout=std::chrono::milliseconds(0), llvm::sys::fs::LockKind Kind=llvm::sys::fs::LockKind::Exclusive)
Thread-safe alternative to sys::fs::tryLockFile.
Expected< size_t > preallocateFileTail(int FD, size_t CurrentSize, size_t NewSize)
Allocate space for the file FD on disk, if the filesystem supports it.
LLVM_ABI std::error_code lockFile(int FD, LockKind Kind=LockKind::Exclusive)
Lock the file.
LLVM_ABI std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout=std::chrono::milliseconds(0), LockKind Kind=LockKind::Exclusive)
Try to locks the file during the specified time.
LockKind
An enumeration for the lock kind.
LLVM_ABI std::error_code unlockFile(int FD)
Unlock the file.
This is an optimization pass for GlobalISel generic memory operations.
std::error_code make_error_code(BitcodeError E)
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1305
@ Timeout
Reached timeout while waiting for the owner to release the lock.
std::error_code errnoAsErrorCode()
Helper to get errno as an std::error_code.
Definition Error.h:1240