1 //===--- ThreadsafeFS.cpp -------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "support/ThreadsafeFS.h"
11 #include "llvm/ADT/SmallString.h"
12 #include "llvm/ADT/StringRef.h"
13 #include "llvm/Support/Path.h"
14 #include "llvm/Support/VirtualFileSystem.h"
21 /// Always opens files in the underlying filesystem as "volatile", meaning they
22 /// won't be memory-mapped. Memory-mapping isn't desirable for clangd:
23 /// - edits to the underlying files change contents MemoryBuffers owned by
24 // SourceManager, breaking its invariants and leading to crashes
25 /// - it locks files on windows, preventing edits
26 class VolatileFileSystem
: public llvm::vfs::ProxyFileSystem
{
28 explicit VolatileFileSystem(llvm::IntrusiveRefCntPtr
<FileSystem
> FS
)
29 : ProxyFileSystem(std::move(FS
)) {}
31 llvm::ErrorOr
<std::unique_ptr
<llvm::vfs::File
>>
32 openFileForRead(const llvm::Twine
&InPath
) override
{
33 llvm::SmallString
<128> Path
;
34 InPath
.toVector(Path
);
36 auto File
= getUnderlyingFS().openFileForRead(Path
);
39 // Try to guess preamble files, they can be memory-mapped even on Windows as
40 // clangd has exclusive access to those and nothing else should touch them.
41 llvm::StringRef FileName
= llvm::sys::path::filename(Path
);
42 if (FileName
.starts_with("preamble-") && FileName
.ends_with(".pch"))
44 return std::make_unique
<VolatileFile
>(std::move(*File
));
48 class VolatileFile
: public llvm::vfs::File
{
50 VolatileFile(std::unique_ptr
<llvm::vfs::File
> Wrapped
)
51 : Wrapped(std::move(Wrapped
)) {
52 assert(this->Wrapped
);
55 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
56 getBuffer(const llvm::Twine
&Name
, int64_t FileSize
,
57 bool RequiresNullTerminator
, bool /*IsVolatile*/) override
{
58 return Wrapped
->getBuffer(Name
, FileSize
, RequiresNullTerminator
,
62 llvm::ErrorOr
<llvm::vfs::Status
> status() override
{
63 return Wrapped
->status();
65 llvm::ErrorOr
<std::string
> getName() override
{ return Wrapped
->getName(); }
66 std::error_code
close() override
{ return Wrapped
->close(); }
69 std::unique_ptr
<File
> Wrapped
;
74 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
>
75 ThreadsafeFS::view(PathRef CWD
) const {
76 auto FS
= view(std::nullopt
);
77 if (auto EC
= FS
->setCurrentWorkingDirectory(CWD
))
78 elog("VFS: failed to set CWD to {0}: {1}", CWD
, EC
.message());
82 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
>
83 RealThreadsafeFS::viewImpl() const {
84 // Avoid using memory-mapped files.
85 // FIXME: Try to use a similar approach in Sema instead of relying on
86 // propagation of the 'isVolatile' flag through all layers.
87 return new VolatileFileSystem(llvm::vfs::createPhysicalFileSystem());