Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Package state in GlobalState (#4631)
* Move PackageDB to GlobalState * Avoid re-processing all package files when package specs have not changed * remove TODOs * don't use unique_ptr in PackageDB::deepCopy * Apply suggestions from code review Co-authored-by: Jake Zimmerman <zimmerman.jake@gmail.com> Co-authored-by: Jake Zimmerman <zimmerman.jake@gmail.com>
- Loading branch information
Showing
10 changed files
with
399 additions
and
183 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
#include "core/packages/PackageDB.h" | ||
#include "core/Loc.h" | ||
|
||
using namespace std; | ||
|
||
namespace sorbet::core::packages { | ||
|
||
namespace { | ||
class NonePackage final : public PackageInfo { | ||
public: | ||
core::NameRef mangledName() const { | ||
return NameRef::noName(); | ||
} | ||
|
||
const vector<string> &pathPrefixes() const { | ||
ENFORCE(false); | ||
return prefixes; | ||
} | ||
|
||
unique_ptr<PackageInfo> deepCopy() const { | ||
ENFORCE(false); | ||
return make_unique<NonePackage>(); | ||
} | ||
|
||
Loc definitionLoc() const { | ||
ENFORCE(false); | ||
return Loc::none(); | ||
} | ||
|
||
~NonePackage() {} | ||
|
||
private: | ||
const vector<string> prefixes; | ||
}; | ||
static const NonePackage NONE_PKG; | ||
} // namespace | ||
|
||
UnfreezePackages::UnfreezePackages(PackageDB &db) : db(db) { | ||
ENFORCE(db.frozen); | ||
db.writerThread = this_thread::get_id(); | ||
db.frozen = false; | ||
} | ||
|
||
UnfreezePackages::~UnfreezePackages() { | ||
ENFORCE(!db.frozen); | ||
db.writerThread = std::thread::id(); | ||
db.frozen = true; | ||
// Note in the future we may want to change the data structures involved in a way that requires | ||
// a finalization step. Controlling freeze/unfreeze with RAII gives a good hook to do that. | ||
} | ||
|
||
NameRef PackageDB::enterPackage(unique_ptr<PackageInfo> pkg) { | ||
ENFORCE(!frozen); | ||
ENFORCE(writerThread == this_thread::get_id(), "PackageDB writes are not thread safe"); | ||
auto nr = pkg->mangledName(); | ||
auto prev = packages.find(nr); | ||
if (prev == packages.end()) { | ||
for (const auto &prefix : pkg->pathPrefixes()) { | ||
packagesByPathPrefix[prefix] = nr; | ||
} | ||
} else { | ||
// Package files do not have full featured content hashing. If the contents of one changes | ||
// we always run slow-path and fully rebuild the set of packages. In some cases, the LSP | ||
// fast-path may re-run on an unchanged package file. Sanity check to ensure the loc and | ||
// prefixes are the same. | ||
ENFORCE(prev->second->definitionLoc() == pkg->definitionLoc()); | ||
ENFORCE(prev->second->pathPrefixes() == pkg->pathPrefixes()); | ||
} | ||
packages[nr] = move(pkg); | ||
return nr; | ||
} | ||
|
||
NameRef PackageDB::lookupPackage(NameRef pkgMangledName) const { | ||
ENFORCE(pkgMangledName.exists()); | ||
auto it = packages.find(pkgMangledName); | ||
if (it == packages.end()) { | ||
return NameRef::noName(); | ||
} | ||
return it->first; | ||
} | ||
|
||
const PackageInfo &PackageDB::getPackageForFile(const core::GlobalState &gs, core::FileRef file) const { | ||
ENFORCE(frozen); | ||
auto &fileData = file.data(gs); | ||
string_view path = fileData.path(); | ||
int curPrefixPos = path.find_last_of('/'); | ||
while (curPrefixPos != string::npos) { | ||
const auto &it = packagesByPathPrefix.find(path.substr(0, curPrefixPos + 1)); | ||
if (it != packagesByPathPrefix.end()) { | ||
const auto &pkg = getPackageInfo(it->second); | ||
ENFORCE(pkg.exists()); | ||
return pkg; | ||
} | ||
|
||
if (fileData.sourceType == core::File::Type::Package) { | ||
// When looking up a `__package.rb` file do not search parent directories | ||
break; | ||
} | ||
curPrefixPos = path.find_last_of('/', curPrefixPos - 1); | ||
} | ||
return NONE_PKG; | ||
} | ||
|
||
const PackageInfo &PackageDB::getPackageInfo(core::NameRef mangledName) const { | ||
auto it = packages.find(mangledName); | ||
if (it == packages.end()) { | ||
return NONE_PKG; | ||
} | ||
return *it->second; | ||
} | ||
|
||
PackageDB PackageDB::deepCopy() const { | ||
ENFORCE(frozen); | ||
PackageDB result; | ||
result.packages.reserve(this->packages.size()); | ||
for (auto const &[nr, pkgInfo] : this->packages) { | ||
result.packages[nr] = pkgInfo->deepCopy(); | ||
} | ||
result.packagesByPathPrefix = this->packagesByPathPrefix; | ||
return result; | ||
} | ||
|
||
UnfreezePackages PackageDB::unfreeze() { | ||
return UnfreezePackages(*this); | ||
} | ||
|
||
} // namespace sorbet::core::packages |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
#ifndef SORBET_CORE_PACKAGES_PACKAGEDB_H | ||
#define SORBET_CORE_PACKAGES_PACKAGEDB_H | ||
|
||
#include "common/common.h" | ||
#include "core/Files.h" | ||
#include "core/Names.h" | ||
#include "core/packages/PackageInfo.h" | ||
|
||
namespace sorbet::core::packages { | ||
|
||
class PackageDB; | ||
|
||
class UnfreezePackages final { | ||
public: | ||
PackageDB &db; | ||
|
||
UnfreezePackages(PackageDB &); | ||
~UnfreezePackages(); | ||
}; | ||
|
||
class PackageDB final { | ||
public: | ||
NameRef enterPackage(std::unique_ptr<PackageInfo> pkg); | ||
NameRef lookupPackage(NameRef pkgMangledName) const; | ||
|
||
const PackageInfo &getPackageForFile(const core::GlobalState &gs, core::FileRef file) const; | ||
const PackageInfo &getPackageInfo(core::NameRef mangledName) const; | ||
|
||
PackageDB deepCopy() const; | ||
|
||
UnfreezePackages unfreeze(); | ||
|
||
PackageDB() = default; | ||
PackageDB(const PackageDB &) = delete; | ||
PackageDB(PackageDB &&) = default; | ||
PackageDB &operator=(const PackageDB &) = delete; | ||
PackageDB &operator=(PackageDB &&) = default; | ||
|
||
private: | ||
UnorderedMap<core::NameRef, std::unique_ptr<packages::PackageInfo>> packages; | ||
UnorderedMap<std::string, core::NameRef> packagesByPathPrefix; | ||
bool frozen = true; | ||
std::thread::id writerThread; | ||
|
||
friend class UnfreezePackages; | ||
}; | ||
|
||
} // namespace sorbet::core::packages | ||
#endif // SORBET_CORE_PACKAGES_PACKAGEDB_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#include "core/packages/PackageInfo.h" | ||
#include "core/Loc.h" | ||
#include "core/NameRef.h" | ||
|
||
using namespace std; | ||
|
||
namespace sorbet::core::packages { | ||
bool PackageInfo::exists() const { | ||
return mangledName().exists(); | ||
} | ||
|
||
PackageInfo::~PackageInfo() { | ||
// see https://eli.thegreenplace.net/2010/11/13/pure-virtual-destructors-in-c | ||
} | ||
} // namespace sorbet::core::packages |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#ifndef SORBET_CORE_PACKAGES_PACKAGEINFO_H | ||
#define SORBET_CORE_PACKAGES_PACKAGEINFO_H | ||
|
||
#include <vector> | ||
|
||
namespace sorbet::core { | ||
class NameRef; | ||
class Loc; | ||
} // namespace sorbet::core | ||
|
||
namespace sorbet::core::packages { | ||
class PackageInfo { | ||
public: | ||
virtual core::NameRef mangledName() const = 0; | ||
virtual const std::vector<std::string> &pathPrefixes() const = 0; | ||
virtual std::unique_ptr<PackageInfo> deepCopy() const = 0; | ||
virtual core::Loc definitionLoc() const = 0; | ||
virtual bool exists() const final; | ||
|
||
virtual ~PackageInfo() = 0; | ||
PackageInfo() = default; | ||
PackageInfo(PackageInfo &) = delete; | ||
explicit PackageInfo(const PackageInfo &) = default; | ||
PackageInfo &operator=(PackageInfo &&) = delete; | ||
PackageInfo &operator=(const PackageInfo &) = delete; | ||
}; | ||
} // namespace sorbet::core::packages | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.