diff --git a/third_party/nix/src/libexpr/eval.cc b/third_party/nix/src/libexpr/eval.cc index 0afa7567b..5f272b62b 100644 --- a/third_party/nix/src/libexpr/eval.cc +++ b/third_party/nix/src/libexpr/eval.cc @@ -12,6 +12,7 @@ #define GC_INCLUDE_NEW +#include #include #include #include @@ -34,6 +35,65 @@ #include "libutil/visitor.hh" namespace nix { +namespace { + +// Called when the Boehm GC runs out of memory. +static void* BoehmOomHandler(size_t requested) { + /* Convert this to a proper C++ exception. */ + LOG(FATAL) << "Garbage collector ran out of memory; requested " << requested + << " bytes"; + throw std::bad_alloc(); +} + +void ConfigureBoehmGc() { + /* Don't look for interior pointers. This reduces the odds of + misdetection a bit. */ + GC_set_all_interior_pointers(0); + + /* We don't have any roots in data segments, so don't scan from + there. */ + GC_set_no_dls(1); + + GC_INIT(); + + GC_set_oom_fn(BoehmOomHandler); + + /* Set the initial heap size to something fairly big (25% of + physical RAM, up to a maximum of 384 MiB) so that in most cases + we don't need to garbage collect at all. (Collection has a + fairly significant overhead.) The heap size can be overridden + through libgc's GC_INITIAL_HEAP_SIZE environment variable. We + should probably also provide a nix.conf setting for this. Note + that GC_expand_hp() causes a lot of virtual, but not physical + (resident) memory to be allocated. This might be a problem on + systems that don't overcommit. */ + if (getenv("GC_INITIAL_HEAP_SIZE") == nullptr) { + size_t size = 32 * 1024 * 1024; +#if HAVE_SYSCONF && defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES) + size_t maxSize = 384 * 1024 * 1024; + long pageSize = sysconf(_SC_PAGESIZE); + long pages = sysconf(_SC_PHYS_PAGES); + if (pageSize != -1) { + size = (pageSize * pages) / 4; + } // 25% of RAM + if (size > maxSize) { + size = maxSize; + } +#endif + DLOG(INFO) << "setting initial heap size to " << size << " bytes"; + GC_expand_hp(size); + } +} + +} // namespace + +namespace expr { + +absl::once_flag gc_flag; + +void InitGC() { absl::call_once(gc_flag, &ConfigureBoehmGc); } + +} // namespace expr static char* dupString(const char* s) { char* t; @@ -187,14 +247,6 @@ std::string showType(const Value& v) { abort(); } -#if HAVE_BOEHMGC -/* Called when the Boehm GC runs out of memory. */ -static void* oomHandler(size_t requested) { - /* Convert this to a proper C++ exception. */ - throw std::bad_alloc(); -} -#endif - static Symbol getName(const AttrName& name, EvalState& state, Env& env) { return std::visit( util::overloaded{[&](const Symbol& name) -> Symbol { return name; }, @@ -207,59 +259,6 @@ static Symbol getName(const AttrName& name, EvalState& state, Env& env) { name); } -static bool gcInitialised = false; - -void initGC() { - if (gcInitialised) { - return; - } - -#if HAVE_BOEHMGC - /* Initialise the Boehm garbage collector. */ - - /* Don't look for interior pointers. This reduces the odds of - misdetection a bit. */ - GC_set_all_interior_pointers(0); - - /* We don't have any roots in data segments, so don't scan from - there. */ - GC_set_no_dls(1); - - GC_INIT(); - - GC_set_oom_fn(oomHandler); - - /* Set the initial heap size to something fairly big (25% of - physical RAM, up to a maximum of 384 MiB) so that in most cases - we don't need to garbage collect at all. (Collection has a - fairly significant overhead.) The heap size can be overridden - through libgc's GC_INITIAL_HEAP_SIZE environment variable. We - should probably also provide a nix.conf setting for this. Note - that GC_expand_hp() causes a lot of virtual, but not physical - (resident) memory to be allocated. This might be a problem on - systems that don't overcommit. */ - if (getenv("GC_INITIAL_HEAP_SIZE") == nullptr) { - size_t size = 32 * 1024 * 1024; -#if HAVE_SYSCONF && defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES) - size_t maxSize = 384 * 1024 * 1024; - long pageSize = sysconf(_SC_PAGESIZE); - long pages = sysconf(_SC_PHYS_PAGES); - if (pageSize != -1) { - size = (pageSize * pages) / 4; - } // 25% of RAM - if (size > maxSize) { - size = maxSize; - } -#endif - DLOG(INFO) << "setting initial heap size to " << size << " bytes"; - GC_expand_hp(size); - } - -#endif - - gcInitialised = true; -} - /* Very hacky way to parse $NIX_PATH, which is colon-separated, but can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */ static Strings parseNixPath(const std::string& s) { @@ -334,9 +333,9 @@ EvalState::EvalState(const Strings& _searchPath, const ref& store) store(store), baseEnv(allocEnv(128)), staticBaseEnv(false, nullptr) { - countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0"; + expr::InitGC(); - assert(gcInitialised); + countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0"; /* Initialise the Nix expression search path. */ if (!evalSettings.pureEval) { diff --git a/third_party/nix/src/libexpr/eval.hh b/third_party/nix/src/libexpr/eval.hh index 2c285ed5c..e08ec04ef 100644 --- a/third_party/nix/src/libexpr/eval.hh +++ b/third_party/nix/src/libexpr/eval.hh @@ -16,6 +16,13 @@ #include "libutil/hash.hh" namespace nix { +namespace expr { + +// Initialise the Boehm GC once per program instance. This should be +// called in places that require the garbage collector. +void InitGC(); + +} // namespace expr class Store; class EvalState; @@ -59,9 +66,6 @@ std::ostream& operator<<(std::ostream& str, const Value& v); typedef std::pair SearchPathElem; typedef std::list SearchPath; -/* Initialise the Boehm GC, if applicable. */ -void initGC(); - typedef std::map, traceable_allocator>> FileParseCache; diff --git a/third_party/nix/src/nix/main.cc b/third_party/nix/src/nix/main.cc index 5536aac53..9ae06bc80 100644 --- a/third_party/nix/src/nix/main.cc +++ b/third_party/nix/src/nix/main.cc @@ -126,7 +126,6 @@ void mainWrapped(int argc, char** argv) { } initNix(); - initGC(); programPath = argv[0]; std::string programName = baseNameOf(programPath); diff --git a/third_party/nix/src/tests/attr-set.cc b/third_party/nix/src/tests/attr-set.cc index e8ad86664..ae323e6bd 100644 --- a/third_party/nix/src/tests/attr-set.cc +++ b/third_party/nix/src/tests/attr-set.cc @@ -115,7 +115,7 @@ class AttrSetTest : public ::testing::Test { protected: EvalState* eval_state_; void SetUp() override { - nix::initGC(); + nix::expr::InitGC(); auto store = std::make_shared(); eval_state_ = new EvalState({"."}, ref(store)); symbol_table = &eval_state_->symbols; diff --git a/third_party/nix/src/tests/language-tests.cc b/third_party/nix/src/tests/language-tests.cc index 9fb453e5e..af4a7dbfa 100644 --- a/third_party/nix/src/tests/language-tests.cc +++ b/third_party/nix/src/tests/language-tests.cc @@ -111,7 +111,7 @@ class NixEnvironment : public testing::Environment { public: void SetUp() override { google::InitGoogleLogging("--logtostderr=false"); - nix::initGC(); + nix::expr::InitGC(); } }; diff --git a/third_party/nix/src/tests/value-to-json.cc b/third_party/nix/src/tests/value-to-json.cc index 573eb6582..454274253 100644 --- a/third_party/nix/src/tests/value-to-json.cc +++ b/third_party/nix/src/tests/value-to-json.cc @@ -12,7 +12,7 @@ class ValueTest : public ::testing::Test { protected: - static void SetUpTestCase() { nix::initGC(); } + static void SetUpTestCase() { nix::expr::InitGC(); } static void TearDownTestCase() {} };