about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--third_party/nix/src/libexpr/eval.cc125
-rw-r--r--third_party/nix/src/libexpr/eval.hh10
-rw-r--r--third_party/nix/src/nix/main.cc1
-rw-r--r--third_party/nix/src/tests/attr-set.cc2
-rw-r--r--third_party/nix/src/tests/language-tests.cc2
-rw-r--r--third_party/nix/src/tests/value-to-json.cc2
6 files changed, 72 insertions, 70 deletions
diff --git a/third_party/nix/src/libexpr/eval.cc b/third_party/nix/src/libexpr/eval.cc
index 0afa7567bb..5f272b62b8 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 <absl/base/call_once.h>
 #include <absl/container/flat_hash_set.h>
 #include <absl/strings/match.h>
 #include <gc/gc.h>
@@ -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(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 2c285ed5c1..e08ec04ef5 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<std::string, std::string> SearchPathElem;
 typedef std::list<SearchPathElem> SearchPath;
 
-/* Initialise the Boehm GC, if applicable. */
-void initGC();
-
 typedef std::map<Path, Expr*, std::less<Path>,
                  traceable_allocator<std::pair<const Path, Expr*>>>
     FileParseCache;
diff --git a/third_party/nix/src/nix/main.cc b/third_party/nix/src/nix/main.cc
index 5536aac532..9ae06bc803 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 e8ad86664b..ae323e6bd3 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<DummyStore>();
     eval_state_ = new EvalState({"."}, ref<Store>(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 9fb453e5e5..af4a7dbfa8 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 573eb65828..4542742530 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() {}
 };