LLVM 24.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/Support/Errno.h"
11#include "llvm/Support/Error.h"
13#include "llvm/Support/Path.h"
15#include <chrono>
16#include <mutex>
17#include <thread>
18
19#if __has_include(<sys/file.h>)
20#include <sys/file.h>
21#ifdef LOCK_SH
22#define HAVE_FLOCK 1
23#else
24#define HAVE_FLOCK 0
25#endif
26#endif
27
28#if __has_include(<fcntl.h>)
29#include <fcntl.h>
30#endif
31
32#if __has_include(<sys/mount.h>)
33#include <sys/mount.h> // statfs
34#endif
35
36#ifdef __APPLE__
37#if __has_include(<sys/sysctl.h>)
38#include <sys/sysctl.h>
39#endif
40#endif
41
42#ifdef _WIN32
44#endif
45
46using namespace llvm;
47
49
51 static std::once_flag Flag;
52 Error Err = Error::success();
53 std::call_once(Flag, [&Err] {
54 ErrorAsOutParameter EAO(&Err);
55 constexpr const char *EnvVar = "LLVM_CAS_MAX_MAPPING_SIZE";
56 auto Value = sys::Process::GetEnv(EnvVar);
57 if (!Value)
58 return;
59
60 uint64_t Size;
61 if (StringRef(*Value).getAsInteger(/*auto*/ 0, Size))
63 "invalid value for %s: expected integer", EnvVar);
65 });
66
67 if (Err)
68 return std::move(Err);
69
71 return std::nullopt;
72
74}
75
79
80std::error_code cas::ondisk::lockFileThreadSafe(int FD,
81 sys::fs::LockKind Kind) {
82#if HAVE_FLOCK
83#ifdef __APPLE__
84 // flock LOCK_SH after an exclusive lock is released may only wake a single
85 // waiter on Darwin, which can cause very long waits if the first shared
86 // access is long-running. Use a retry loop so waiters wake automatically.
87 if (Kind == sys::fs::LockKind::Shared) {
88 std::chrono::milliseconds Wait(1);
89 std::chrono::milliseconds MaxWait(20);
90 while (true) {
91 std::error_code EC = tryLockFileThreadSafe(FD, {}, Kind);
92 if (EC != std::errc::no_lock_available)
93 return EC;
94 std::this_thread::sleep_for(Wait);
95 Wait = std::min(2 * Wait, MaxWait);
96 }
97 }
98 // Fallthrough for exclusive lock
100#endif
102 -1, flock, FD,
103 Kind == sys::fs::LockKind::Exclusive ? LOCK_EX : LOCK_SH) == 0)
104 return std::error_code();
105 return std::error_code(errno, std::generic_category());
106#elif defined(_WIN32)
107 // On Windows this implementation is thread-safe.
108 return sys::fs::lockFile(FD, Kind);
109#else
110 return make_error_code(std::errc::no_lock_available);
111#endif
112}
113
114std::error_code cas::ondisk::unlockFileThreadSafe(int FD) {
115#if HAVE_FLOCK
116 if (sys::RetryAfterSignal(-1, flock, FD, LOCK_UN) == 0)
117 return std::error_code();
118 return std::error_code(errno, std::generic_category());
119#elif defined(_WIN32)
120 // On Windows this implementation is thread-safe.
121 return sys::fs::unlockFile(FD);
122#else
123 return make_error_code(std::errc::no_lock_available);
124#endif
125}
126
127std::error_code
128cas::ondisk::tryLockFileThreadSafe(int FD, std::chrono::milliseconds Timeout,
129 sys::fs::LockKind Kind) {
130#if HAVE_FLOCK
131 auto Start = std::chrono::steady_clock::now();
132 auto End = Start + Timeout;
133 do {
135 -1, flock, FD,
136 (Kind == sys::fs::LockKind::Exclusive ? LOCK_EX : LOCK_SH) |
137 LOCK_NB) == 0)
138 return std::error_code();
139 int Error = errno;
140 if (Error == EWOULDBLOCK) {
141 if (Timeout.count() == 0)
142 break;
143 // Match sys::fs::tryLockFile, which sleeps for 1 ms per attempt.
144 std::this_thread::sleep_for(std::chrono::milliseconds(1));
145 continue;
146 }
147 return std::error_code(Error, std::generic_category());
148 } while (std::chrono::steady_clock::now() < End);
149 return make_error_code(std::errc::no_lock_available);
150#elif defined(_WIN32)
151 // On Windows this implementation is thread-safe.
152 return sys::fs::tryLockFile(FD, Timeout, Kind);
153#else
154 return make_error_code(std::errc::no_lock_available);
155#endif
156}
157
159 size_t NewSize) {
160 auto CreateError = [&](std::error_code EC) -> Expected<size_t> {
161 if (EC == std::errc::not_supported)
162 // Ignore ENOTSUP in case the filesystem cannot preallocate.
163 return NewSize;
164#if defined(HAVE_POSIX_FALLOCATE)
165 if (EC == std::errc::invalid_argument && CurrentSize < NewSize && // len > 0
166 NewSize < std::numeric_limits<off_t>::max()) // 0 <= offset, len < max
167 // Prior to 2024, POSIX required EINVAL for cases that should be ENOTSUP,
168 // so handle it the same as above if it is not one of the other ways to
169 // get EINVAL.
170 return NewSize;
171#endif
172 return createStringError(EC,
173 "failed to allocate to CAS file: " + EC.message());
174 };
175#if defined(HAVE_POSIX_FALLOCATE)
176 // Note: posix_fallocate returns its error directly, not via errno.
177 int Err;
178 do {
179 Err = posix_fallocate(FD, CurrentSize, NewSize - CurrentSize);
180 } while (Err == EINTR);
181 if (Err)
182 return CreateError(std::error_code(Err, std::generic_category()));
183 return NewSize;
184#elif defined(__APPLE__)
185 fstore_t FAlloc;
186 FAlloc.fst_flags = F_ALLOCATEALL;
187#if defined(F_ALLOCATEPERSIST) && \
188 defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
189 __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 130000
190 // F_ALLOCATEPERSIST is introduced in macOS 13.
191 FAlloc.fst_flags |= F_ALLOCATEPERSIST;
192#endif
193 FAlloc.fst_posmode = F_PEOFPOSMODE;
194 FAlloc.fst_offset = 0;
195 FAlloc.fst_length = NewSize - CurrentSize;
196 FAlloc.fst_bytesalloc = 0;
197 if (sys::RetryAfterSignal(-1, ::fcntl, FD, F_PREALLOCATE, &FAlloc) == -1)
198 return CreateError(errnoAsErrorCode());
199 assert(CurrentSize + FAlloc.fst_bytesalloc >= NewSize);
200 return CurrentSize + FAlloc.fst_bytesalloc;
201#else
202 (void)CreateError; // Silence unused variable.
203 return NewSize; // Pretend it worked.
204#endif
205}
206
208 // Add exceptions to use small database file here.
209#if defined(__APPLE__) && __has_include(<sys/mount.h>)
210 // macOS tmpfs does not support sparse tails.
211 SmallString<128> PathStorage;
212 StringRef Path = P.toNullTerminatedStringRef(PathStorage);
213 struct statfs StatFS;
214 if (statfs(Path.data(), &StatFS) != 0)
215 return false;
216
217 if (strcmp(StatFS.f_fstypename, "tmpfs") == 0)
218 return true;
219#endif
220 // Default to use regular database file.
221 return false;
222}
223
225#ifdef __APPLE__
226#if __has_include(<sys/sysctl.h>) && defined(KERN_BOOTTIME)
227 struct timeval TV;
228 size_t TVLen = sizeof(TV);
229 int KernBoot[2] = {CTL_KERN, KERN_BOOTTIME};
230 if (sysctl(KernBoot, 2, &TV, &TVLen, nullptr, 0) < 0)
232 "failed to get boottime");
233 if (TVLen != sizeof(TV))
234 return createStringError("sysctl kern.boottime unexpected format");
235 return TV.tv_sec;
236#else
237 return 0;
238#endif
239#elif defined(__linux__)
240 // Use the mtime for /proc, which is recreated during system boot.
241 // We could also read /proc/stat and search for 'btime'.
243 if (std::error_code EC = sys::fs::status("/proc", Status))
244 return createFileError("/proc", EC);
245 return Status.getLastModificationTime().time_since_epoch().count();
246#elif defined(_WIN32)
247 // Compute it from the current time and the time since boot, which includes
248 // time spent asleep.
249 auto Uptime = std::chrono::milliseconds(GetTickCount64());
250 auto Boot = std::chrono::system_clock::now() - Uptime;
251 return std::chrono::duration_cast<std::chrono::seconds>(
252 Boot.time_since_epoch())
253 .count();
254#else
255 return 0;
256#endif
257}
258
261 StringRef CopyFromPath) {
262 // \c clonefile requires that the destination path doesn't exist. We create
263 // a "placeholder" temporary file, then modify its path a bit and use that
264 // for \c clonefile to write to.
265 // FIXME: Instead of creating a dummy file, add a new file system API for
266 // copying to a unique path that can loop while checking EEXIST.
267 SmallString<256> UniqueTmpPath;
268 SmallString<256> Model;
269 Model += ParentPath;
270 sys::path::append(Model, "%%%%%%%.tmp");
271 if (std::error_code EC = sys::fs::createUniqueFile(Model, UniqueTmpPath))
272 return createFileError(Model, EC);
273 TmpPath = std::move(UniqueTmpPath);
274 TmpPath += ".tmp"; // modify so that there's no file at that path.
275 // \c copy_file will use \c clonefile when applicable.
276 if (std::error_code EC = sys::fs::copy_file(CopyFromPath, TmpPath))
277 return createFileError(TmpPath, EC);
278
279 return TmpPath;
280}
281
283 if (std::error_code EC = sys::fs::rename(TmpPath, RenameToPath))
284 return createFileError(RenameToPath, EC);
285 TmpPath.clear();
286 return Error::success();
287}
288
290 if (!TmpPath.empty())
291 sys::fs::remove(TmpPath);
292 if (!UniqueTmpPath.empty())
293 sys::fs::remove(UniqueTmpPath);
294}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
uint64_t uint64_t
static uint64_t OnDiskCASMaxMappingSize
#define P(N)
Provides a library for accessing information about this process and other processes on the operating ...
Helper for Errors used as out-parameters.
Definition Error.h:1160
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
bool getAsInteger(unsigned Radix, T &Result) const
Parse the current string as an integer of the specified radix.
Definition StringRef.h:490
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
LLVM Value Representation.
Definition Value.h:75
Error renameTo(StringRef RenameToPath)
Rename the new unique file to RenameToPath.
Expected< StringRef > createAndCopyFrom(StringRef ParentPath, StringRef CopyFromPath)
Create a new unique file path under ParentPath and copy the contents of CopyFromPath into it.
static LLVM_ABI std::optional< std::string > GetEnv(StringRef name)
Represents the result of a call to sys::fs::status().
Definition FileSystem.h:214
LLVM_ABI_FOR_TEST void setMaxMappingSize(uint64_t Size)
Set MaxMappingSize for ondisk CAS.
Expected< std::optional< uint64_t > > getOverriddenMaxMappingSize()
Retrieves an overridden maximum mapping size for CAS files, if any, speicified by LLVM_CAS_MAX_MAPPIN...
LLVM_ABI_FOR_TEST std::error_code lockFileThreadSafe(int FD, llvm::sys::fs::LockKind Kind)
Thread-safe alternative to sys::fs::lockFile.
LLVM_ABI_FOR_TEST 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.
LLVM_ABI_FOR_TEST Expected< uint64_t > getBootTime()
Get boot time for the OS.
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.
bool useSmallMappingSize(const Twine &Path)
Whether to use a small file mapping for ondisk databases created in Path.
LLVM_ABI std::error_code rename(const Twine &from, const Twine &to)
Rename from to to.
LLVM_ABI std::error_code createUniqueFile(const Twine &Model, int &ResultFD, SmallVectorImpl< char > &ResultPath, OpenFlags Flags=OF_None, unsigned Mode=all_read|all_write)
Create a uniquely named file.
Definition Path.cpp:891
LLVM_ABI std::error_code lockFile(int FD, LockKind Kind=LockKind::Exclusive)
Lock the file.
LLVM_ABI std::error_code remove(const Twine &path, bool IgnoreNonExisting=true)
Remove path.
LLVM_ABI std::error_code copy_file(const Twine &From, const Twine &To)
Copy the contents of From to To.
Definition Path.cpp:1042
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 status(const Twine &path, file_status &result, bool follow=true)
Get file status as if by POSIX stat().
LLVM_ABI std::error_code unlockFile(int FD)
Unlock the file.
LLVM_ABI void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
Definition Path.cpp:467
decltype(auto) RetryAfterSignal(const FailT &Fail, const Fun &F, const Args &... As)
Definition Errno.h:33
This is an optimization pass for GlobalISel generic memory operations.
Error createFileError(const Twine &F, Error E)
Concatenate a source file path and/or name with an Error.
Definition Error.h:1415
std::error_code make_error_code(BitcodeError E)
@ Wait
Definition Threading.h:60
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1321
@ 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:1256
@ End
Run the machine verifier once, at the end of the pipeline.
Definition CodeGen.h:320