libexpr: refactor gc-agnostic helpers into one place
Change-Id: Icc4b367e4f670d47256f62a3a002cd248a5c2d3b
This commit is contained in:
parent
0109368c3f
commit
a3361557e3
9 changed files with 142 additions and 82 deletions
|
@ -1,5 +1,6 @@
|
||||||
#include "attr-set.hh"
|
#include "attr-set.hh"
|
||||||
#include "eval-inline.hh"
|
#include "eval.hh"
|
||||||
|
#include "gc-alloc.hh"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ Bindings * EvalState::allocBindings(size_t capacity)
|
||||||
throw Error("attribute set of size %d is too big", capacity);
|
throw Error("attribute set of size %d is too big", capacity);
|
||||||
nrAttrsets++;
|
nrAttrsets++;
|
||||||
nrAttrsInAttrsets += capacity;
|
nrAttrsInAttrsets += capacity;
|
||||||
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity);
|
return new (gcAllocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,26 +4,10 @@
|
||||||
#include "print.hh"
|
#include "print.hh"
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
#include "eval-error.hh"
|
#include "eval-error.hh"
|
||||||
|
#include "gc-alloc.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: Various places expect the allocated memory to be zeroed.
|
|
||||||
*/
|
|
||||||
[[gnu::always_inline]]
|
|
||||||
inline void * allocBytes(size_t n)
|
|
||||||
{
|
|
||||||
void * p;
|
|
||||||
#if HAVE_BOEHMGC
|
|
||||||
p = GC_MALLOC(n);
|
|
||||||
#else
|
|
||||||
p = calloc(n, 1);
|
|
||||||
#endif
|
|
||||||
if (!p) throw std::bad_alloc();
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
Value * EvalState::allocValue()
|
Value * EvalState::allocValue()
|
||||||
{
|
{
|
||||||
|
@ -43,7 +27,7 @@ Value * EvalState::allocValue()
|
||||||
*valueAllocCache = GC_NEXT(p);
|
*valueAllocCache = GC_NEXT(p);
|
||||||
GC_NEXT(p) = nullptr;
|
GC_NEXT(p) = nullptr;
|
||||||
#else
|
#else
|
||||||
void * p = allocBytes(sizeof(Value));
|
void * p = gcAllocBytes(sizeof(Value));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
nrValues++;
|
nrValues++;
|
||||||
|
@ -73,7 +57,7 @@ Env & EvalState::allocEnv(size_t size)
|
||||||
env = (Env *) p;
|
env = (Env *) p;
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
|
env = (Env *) gcAllocBytes(sizeof(Env) + size * sizeof(Value *));
|
||||||
|
|
||||||
/* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
|
/* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "downstream-placeholder.hh"
|
#include "downstream-placeholder.hh"
|
||||||
|
#include "gc-alloc.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh"
|
||||||
#include "filetransfer.hh"
|
#include "filetransfer.hh"
|
||||||
|
@ -48,19 +49,6 @@ using json = nlohmann::json;
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static char * allocString(size_t size)
|
|
||||||
{
|
|
||||||
char * t;
|
|
||||||
#if HAVE_BOEHMGC
|
|
||||||
t = (char *) GC_MALLOC_ATOMIC(size);
|
|
||||||
#else
|
|
||||||
t = (char *) malloc(size);
|
|
||||||
#endif
|
|
||||||
if (!t) throw std::bad_alloc();
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// When there's no need to write to the string, we can optimize away empty
|
// When there's no need to write to the string, we can optimize away empty
|
||||||
// string allocations.
|
// string allocations.
|
||||||
// This function handles makeImmutableString(std::string_view()) by returning
|
// This function handles makeImmutableString(std::string_view()) by returning
|
||||||
|
@ -70,13 +58,12 @@ static const char * makeImmutableString(std::string_view s)
|
||||||
const size_t size = s.size();
|
const size_t size = s.size();
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
return "";
|
return "";
|
||||||
auto t = allocString(size + 1);
|
auto t = gcAllocString(size + 1);
|
||||||
memcpy(t, s.data(), size);
|
memcpy(t, s.data(), size);
|
||||||
t[size] = '\0';
|
t[size] = '\0';
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RootValue allocRootValue(Value * v)
|
RootValue allocRootValue(Value * v)
|
||||||
{
|
{
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
|
@ -806,7 +793,7 @@ static void copyContextToValue(Value & v, const NixStringContext & context)
|
||||||
if (!context.empty()) {
|
if (!context.empty()) {
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
v.string.context = (const char * *)
|
v.string.context = (const char * *)
|
||||||
allocBytes((context.size() + 1) * sizeof(char *));
|
gcAllocBytes((context.size() + 1) * sizeof(char *));
|
||||||
for (auto & i : context)
|
for (auto & i : context)
|
||||||
v.string.context[n++] = makeImmutableString(i.to_string());
|
v.string.context[n++] = makeImmutableString(i.to_string());
|
||||||
v.string.context[n] = 0;
|
v.string.context[n] = 0;
|
||||||
|
@ -862,7 +849,7 @@ void EvalState::mkList(Value & v, size_t size)
|
||||||
{
|
{
|
||||||
v.mkList(size);
|
v.mkList(size);
|
||||||
if (size > 2)
|
if (size > 2)
|
||||||
v.bigList.elems = (Value * *) allocBytes(size * sizeof(Value *));
|
v.bigList.elems = (Value * *) gcAllocBytes(size * sizeof(Value *));
|
||||||
nrListElems += size;
|
nrListElems += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2076,7 +2063,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
Value. allocating a GC'd string directly and moving it into a
|
Value. allocating a GC'd string directly and moving it into a
|
||||||
Value lets us avoid an allocation and copy. */
|
Value lets us avoid an allocation and copy. */
|
||||||
const auto c_str = [&] {
|
const auto c_str = [&] {
|
||||||
char * result = allocString(sSize + 1);
|
char * result = gcAllocString(sSize + 1);
|
||||||
char * tmp = result;
|
char * tmp = result;
|
||||||
for (const auto & part : s) {
|
for (const auto & part : s) {
|
||||||
memcpy(tmp, part->data(), part->size());
|
memcpy(tmp, part->data(), part->size());
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "attr-set.hh"
|
#include "attr-set.hh"
|
||||||
#include "eval-error.hh"
|
#include "eval-error.hh"
|
||||||
|
#include "gc-alloc.hh"
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "value.hh"
|
#include "value.hh"
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
|
@ -37,22 +38,6 @@ namespace eval_cache {
|
||||||
class EvalCache;
|
class EvalCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Alias for std::map which uses boehmgc's allocator conditional on us actually
|
|
||||||
* using boehmgc in this build.
|
|
||||||
*/
|
|
||||||
#if HAVE_BOEHMGC
|
|
||||||
template<typename KeyT, typename ValueT>
|
|
||||||
using GcMap = std::map<
|
|
||||||
KeyT,
|
|
||||||
ValueT,
|
|
||||||
std::less<KeyT>,
|
|
||||||
traceable_allocator<std::pair<KeyT const, ValueT>>
|
|
||||||
>;
|
|
||||||
#else
|
|
||||||
using GcMap = std::map<KeyT, ValueT>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that implements a primop.
|
* Function that implements a primop.
|
||||||
*/
|
*/
|
||||||
|
|
119
src/libexpr/gc-alloc.hh
Normal file
119
src/libexpr/gc-alloc.hh
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
#pragma once
|
||||||
|
/// @file Aliases and wrapper functions that are transparently GC-enabled
|
||||||
|
/// if Lix is compiled with BoehmGC enabled.
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <new>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#if HAVE_BOEHMGC
|
||||||
|
#include <functional> // std::less
|
||||||
|
#include <utility> // std::pair
|
||||||
|
#define GC_INCLUDE_NEW
|
||||||
|
#include <gc/gc.h>
|
||||||
|
#include <gc/gc_allocator.h>
|
||||||
|
#include <gc/gc_cpp.h>
|
||||||
|
|
||||||
|
/// calloc, transparently GC-enabled.
|
||||||
|
#define LIX_GC_CALLOC(size) GC_MALLOC(size)
|
||||||
|
|
||||||
|
/// strdup, transaprently GC-enabled.
|
||||||
|
#define LIX_GC_STRDUP(str) GC_STRDUP(str)
|
||||||
|
|
||||||
|
/// Atomic GC malloc() with GC enabled, or regular malloc() otherwise.
|
||||||
|
#define LIX_GC_MALLOC_ATOMIC(size) GC_MALLOC_ATOMIC(size)
|
||||||
|
|
||||||
|
namespace nix
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Alias for std::map which uses BoehmGC's allocator conditional on this Lix
|
||||||
|
/// build having GC enabled.
|
||||||
|
template<typename KeyT, typename ValueT>
|
||||||
|
using GcMap = std::map<
|
||||||
|
KeyT,
|
||||||
|
ValueT,
|
||||||
|
std::less<KeyT>,
|
||||||
|
traceable_allocator<std::pair<KeyT const, ValueT>>
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// Alias for std::vector which uses BoehmGC's allocator conditional on this Lix
|
||||||
|
/// build having GC enabled.
|
||||||
|
template<typename ItemT>
|
||||||
|
using GcVector = std::vector<ItemT, traceable_allocator<ItemT>>;
|
||||||
|
|
||||||
|
/// Alias for std::list which uses BoehmGC's allocator conditional on this Lix
|
||||||
|
/// build having GC enabled.
|
||||||
|
template<typename ItemT>
|
||||||
|
using GcList = std::list<ItemT, traceable_allocator<ItemT>>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
/// calloc, transparently GC-enabled.
|
||||||
|
#define LIX_GC_CALLOC(size) calloc(size, 1)
|
||||||
|
|
||||||
|
/// strdup, transparently GC-enabled.
|
||||||
|
#define LIX_GC_STRDUP(str) strdup(str)
|
||||||
|
|
||||||
|
/// Atomic GC malloc() with GC enabled, or regular malloc() otherwise.
|
||||||
|
/// The returned memory must never contain pointers.
|
||||||
|
#define LIX_GC_MALLOC_ATOMIC(size) malloc(size)
|
||||||
|
|
||||||
|
namespace nix
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Alias for std::map which uses BoehmGC's allocator conditional on this Lix
|
||||||
|
/// build having GC enabled.
|
||||||
|
template<typename KeyT, typename ValueT>
|
||||||
|
using GcMap = std::map<KeyT, ValueT>;
|
||||||
|
|
||||||
|
/// Alias for std::vector which uses BoehmGC's allocator conditional on this Lix
|
||||||
|
/// build having GC enabled.
|
||||||
|
template<typename ItemT>
|
||||||
|
using GcVector = std::vector<ItemT>;
|
||||||
|
|
||||||
|
/// Alias for std::list which uses BoehmGC's allocator conditional on this Lix
|
||||||
|
/// build having GC enabled.
|
||||||
|
template<typename ItemT>
|
||||||
|
using GcList = std::list<ItemT>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace nix
|
||||||
|
{
|
||||||
|
|
||||||
|
[[gnu::always_inline]]
|
||||||
|
inline void * gcAllocBytes(size_t n)
|
||||||
|
{
|
||||||
|
// Note: various places expect the allocated memory to be zero.
|
||||||
|
// Hence: calloc().
|
||||||
|
void * ptr = LIX_GC_CALLOC(n);
|
||||||
|
if (ptr == nullptr) {
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GC-transparently allocates a buffer for a C-string of @ref size *bytes*,
|
||||||
|
/// meaning you should include the size needed by the NUL terminator in the
|
||||||
|
/// passed size. Memory allocated with this function must never contain other
|
||||||
|
/// pointers.
|
||||||
|
inline char * gcAllocString(size_t size)
|
||||||
|
{
|
||||||
|
char * cstr = static_cast<char *>(LIX_GC_MALLOC_ATOMIC(size));
|
||||||
|
if (cstr == nullptr) {
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
return cstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -80,13 +80,7 @@ public:
|
||||||
bool hasFailed() { return failed; };
|
bool hasFailed() { return failed; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using DrvInfos = GcList<DrvInfo>;
|
||||||
#if HAVE_BOEHMGC
|
|
||||||
typedef std::list<DrvInfo, traceable_allocator<DrvInfo>> DrvInfos;
|
|
||||||
#else
|
|
||||||
typedef std::list<DrvInfo> DrvInfos;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If value `v` denotes a derivation, return a DrvInfo object
|
* If value `v` denotes a derivation, return a DrvInfo object
|
||||||
|
|
|
@ -58,6 +58,7 @@ libexpr_headers = files(
|
||||||
'function-trace.hh',
|
'function-trace.hh',
|
||||||
'gc-small-vector.hh',
|
'gc-small-vector.hh',
|
||||||
'get-drvs.hh',
|
'get-drvs.hh',
|
||||||
|
'gc-alloc.hh',
|
||||||
'json-to-value.hh',
|
'json-to-value.hh',
|
||||||
'nixexpr.hh',
|
'nixexpr.hh',
|
||||||
'parser/change_head.hh',
|
'parser/change_head.hh',
|
||||||
|
|
|
@ -622,14 +622,13 @@ struct CompareValues
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// NOTE: this type must NEVER be outside of GC-scanned memory.
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
typedef std::list<Value *, gc_allocator<Value *>> ValueList;
|
using UnsafeValueList = std::list<Value *, gc_allocator<Value *>>;
|
||||||
#else
|
#else
|
||||||
typedef std::list<Value *> ValueList;
|
using UnsafeValueList = std::list<Value *>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static Bindings::iterator getAttr(
|
static Bindings::iterator getAttr(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
Symbol attrSym,
|
Symbol attrSym,
|
||||||
|
@ -652,7 +651,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
|
||||||
|
|
||||||
state.forceList(*startSet->value, noPos, "while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure");
|
state.forceList(*startSet->value, noPos, "while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure");
|
||||||
|
|
||||||
ValueList workSet;
|
UnsafeValueList workSet;
|
||||||
for (auto elem : startSet->value->listItems())
|
for (auto elem : startSet->value->listItems())
|
||||||
workSet.push_back(elem);
|
workSet.push_back(elem);
|
||||||
|
|
||||||
|
@ -668,7 +667,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
|
||||||
/* Construct the closure by applying the operator to elements of
|
/* Construct the closure by applying the operator to elements of
|
||||||
`workSet', adding the result to `workSet', continuing until
|
`workSet', adding the result to `workSet', continuing until
|
||||||
no new elements are found. */
|
no new elements are found. */
|
||||||
ValueList res;
|
UnsafeValueList res;
|
||||||
// `doneKeys' doesn't need to be a GC root, because its values are
|
// `doneKeys' doesn't need to be a GC root, because its values are
|
||||||
// reachable from res.
|
// reachable from res.
|
||||||
auto cmp = CompareValues(state, noPos, "while comparing the `key` attributes of two genericClosure elements");
|
auto cmp = CompareValues(state, noPos, "while comparing the `key` attributes of two genericClosure elements");
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
|
||||||
|
#include "gc-alloc.hh"
|
||||||
#include "symbol-table.hh"
|
#include "symbol-table.hh"
|
||||||
#include "value/context.hh"
|
#include "value/context.hh"
|
||||||
#include "input-accessor.hh"
|
#include "input-accessor.hh"
|
||||||
|
@ -11,9 +12,6 @@
|
||||||
#include "print-options.hh"
|
#include "print-options.hh"
|
||||||
#include "checked-arithmetic.hh"
|
#include "checked-arithmetic.hh"
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
|
||||||
#include <gc/gc_allocator.h>
|
|
||||||
#endif
|
|
||||||
#include <nlohmann/json_fwd.hpp>
|
#include <nlohmann/json_fwd.hpp>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -464,17 +462,9 @@ void Value::mkBlackhole()
|
||||||
thunk.expr = (Expr*) &eBlackHole;
|
thunk.expr = (Expr*) &eBlackHole;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using ValueVector = GcVector<Value *>;
|
||||||
#if HAVE_BOEHMGC
|
using ValueMap = GcMap<Symbol, Value *>;
|
||||||
typedef std::vector<Value *, traceable_allocator<Value *>> ValueVector;
|
using ValueVectorMap = std::map<Symbol, ValueVector>;
|
||||||
typedef std::map<Symbol, Value *, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, Value *>>> ValueMap;
|
|
||||||
typedef std::map<Symbol, ValueVector, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, ValueVector>>> ValueVectorMap;
|
|
||||||
#else
|
|
||||||
typedef std::vector<Value *> ValueVector;
|
|
||||||
typedef std::map<Symbol, Value *> ValueMap;
|
|
||||||
typedef std::map<Symbol, ValueVector> ValueVectorMap;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A value allocated in traceable memory.
|
* A value allocated in traceable memory.
|
||||||
|
|
Loading…
Reference in a new issue