diff --git a/src/libexpr/include/nix/expr/parallel-eval.hh b/src/libexpr/include/nix/expr/parallel-eval.hh index 6e771dc72d64..4f611328f4ff 100644 --- a/src/libexpr/include/nix/expr/parallel-eval.hh +++ b/src/libexpr/include/nix/expr/parallel-eval.hh @@ -7,6 +7,7 @@ #include +#include "nix/util/move-only-function.hh" #include "nix/util/sync.hh" #include "nix/util/logging.hh" #include "nix/util/environment-variables.hh" @@ -21,8 +22,7 @@ namespace nix { struct Executor { - // FIXME: support std::moveable_function. - using work_t = std::function; + using work_t = MoveOnlyFunction; struct Item { @@ -84,7 +84,9 @@ struct FutureVector void spawn(uint8_t prioPrefix, Executor::work_t && work) { - spawn({{std::move(work), prioPrefix}}); + Executor::WorkItems items; + items.emplace_back(std::move(work), prioPrefix); + spawn(std::move(items)); } void finishAll(); diff --git a/src/libexpr/parallel-eval.cc b/src/libexpr/parallel-eval.cc index e0cdab93a7b8..1c89b9c4db2c 100644 --- a/src/libexpr/parallel-eval.cc +++ b/src/libexpr/parallel-eval.cc @@ -294,9 +294,7 @@ static void prim_parallel(EvalState & state, const PosIdx pos, Value ** args, Va Executor::WorkItems work; for (auto value : args[0]->listView()) if (!value->isFinished()) - state.addWork(work, 0, [value(std::make_shared(value)), &state, pos]() { - state.forceValue(***value, pos); - }); + state.addWork(work, 0, [value(RootValue(value)), &state, pos]() { state.forceValue(**value, pos); }); state.executor->spawn(std::move(work)); } diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index 33477e1bc86d..134417e165d6 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -28,9 +28,8 @@ static void parallelForceDeep(EvalState & state, Value & v, PosIdx pos) if (v.attrs()->get(state.s.outPath)) return; for (auto & a : *v.attrs()) - state.addWork(work, 0, [value(std::make_shared(a.value)), pos(a.pos), &state]() { - parallelForceDeep(state, ***value, pos); - }); + state.addWork( + work, 0, [value(RootValue(a.value)), pos(a.pos), &state]() { parallelForceDeep(state, **value, pos); }); break; } diff --git a/src/libutil/include/nix/util/meson.build b/src/libutil/include/nix/util/meson.build index 56f8298f10dc..3af780eb2935 100644 --- a/src/libutil/include/nix/util/meson.build +++ b/src/libutil/include/nix/util/meson.build @@ -64,6 +64,7 @@ headers = [ config_pub_h ] + files( 'memo.hh', 'memory-source-accessor.hh', 'mounted-source-accessor.hh', + 'move-only-function.hh', 'muxable-pipe.hh', 'nar-accessor.hh', 'nar-cache.hh', diff --git a/src/libutil/include/nix/util/move-only-function.hh b/src/libutil/include/nix/util/move-only-function.hh new file mode 100644 index 000000000000..479812207822 --- /dev/null +++ b/src/libutil/include/nix/util/move-only-function.hh @@ -0,0 +1,83 @@ +#pragma once +///@file + +#include +#include +#include +#include + +namespace nix { + +#ifdef __cpp_lib_move_only_function + +template +using MoveOnlyFunction = std::move_only_function; + +#else + +/** + * Fallback implementation of `std::move_only_function` for standard + * libraries that don't provide it yet (e.g. libc++ as of version 21): + * a type-erased callable wrapper that, unlike `std::function`, only + * requires the callable to be move-constructible, so it can hold + * lambdas that capture move-only types. + */ +template +class MoveOnlyFunction; + +template +class MoveOnlyFunction +{ + struct Base + { + virtual Ret call(Args &&... args) = 0; + virtual ~Base() = default; + }; + + template + struct Impl final : Base + { + F f; + + template + requires std::is_same_v, F> + Impl(G && g) + : f(std::forward(g)) + { + } + + Ret call(Args &&... args) override + { + return f(std::forward(args)...); + } + }; + + std::unique_ptr impl; + +public: + MoveOnlyFunction() = default; + + template + requires(!std::is_same_v, MoveOnlyFunction> && std::is_invocable_r_v) + MoveOnlyFunction(F && f) + : impl(std::make_unique>>(std::forward(f))) + { + } + + MoveOnlyFunction(MoveOnlyFunction &&) noexcept = default; + MoveOnlyFunction & operator=(MoveOnlyFunction &&) noexcept = default; + + Ret operator()(Args... args) + { + return impl->call(std::forward(args)...); + } + + explicit operator bool() const + { + return (bool) impl; + } +}; + +#endif + +} // namespace nix