From a72709afd8ffe35613a6bacd698a36395e095a48 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 18 Jun 2008 09:34:17 +0000 Subject: [PATCH] * Some refactoring: put the GC options / results in separate structs. * The garbage collector now also prints the number of blocks freed. --- src/libstore/build.cc | 10 +-- src/libstore/gc.cc | 54 ++++++++------- src/libstore/local-store.cc | 5 +- src/libstore/local-store.hh | 18 ++--- src/libstore/optimise-store.cc | 1 + src/libstore/remote-store.cc | 22 +++---- src/libstore/remote-store.hh | 3 +- src/libstore/store-api.hh | 113 ++++++++++++++++++++++---------- src/libstore/worker-protocol.hh | 34 +++++----- src/libutil/serialise.cc | 31 +++++++++ src/libutil/serialise.hh | 2 + src/libutil/util.cc | 37 +++++++---- src/libutil/util.hh | 8 ++- src/nix-store/nix-store.cc | 57 ++++++++-------- src/nix-worker/nix-worker.cc | 23 +++---- 15 files changed, 252 insertions(+), 166 deletions(-) diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 0664a118a0..1d624723f9 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -578,17 +578,17 @@ void getOwnership(const Path & path) void deletePathWrapped(const Path & path, - unsigned long long & bytesFreed) + unsigned long long & bytesFreed, unsigned long long & blocksFreed) { try { /* First try to delete it ourselves. */ - deletePath(path, bytesFreed); + deletePath(path, bytesFreed, blocksFreed); } catch (SysError & e) { /* If this failed due to a permission error, then try it with the setuid helper. */ if (haveBuildUsers() && !amPrivileged()) { getOwnership(path); - deletePath(path, bytesFreed); + deletePath(path, bytesFreed, blocksFreed); } else throw; } @@ -597,8 +597,8 @@ void deletePathWrapped(const Path & path, void deletePathWrapped(const Path & path) { - unsigned long long dummy; - deletePathWrapped(path, dummy); + unsigned long long dummy1, dummy2; + deletePathWrapped(path, dummy1, dummy2); } diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index c46cc817bc..0caf1ce4e6 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -439,9 +439,9 @@ Paths topoSortPaths(const PathSet & paths) } -void LocalStore::tryToDelete(GCAction action, const PathSet & livePaths, - const PathSet & tempRootsClosed, PathSet & done, PathSet & deleted, - const Path & path, unsigned long long & bytesFreed) +void LocalStore::tryToDelete(const GCOptions & options, GCResults & results, + const PathSet & livePaths, const PathSet & tempRootsClosed, PathSet & done, + const Path & path) { if (done.find(path) != done.end()) return; done.insert(path); @@ -449,7 +449,7 @@ void LocalStore::tryToDelete(GCAction action, const PathSet & livePaths, debug(format("considering deletion of `%1%'") % path); if (livePaths.find(path) != livePaths.end()) { - if (action == gcDeleteSpecific) + if (options.action == GCOptions::gcDeleteSpecific) throw Error(format("cannot delete path `%1%' since it is still alive") % path); debug(format("live path `%1%'") % path); return; @@ -470,15 +470,18 @@ void LocalStore::tryToDelete(GCAction action, const PathSet & livePaths, queryReferrers(path, referrers); foreach (PathSet::iterator, i, referrers) if (*i != path) - tryToDelete(action, livePaths, tempRootsClosed, done, deleted, *i, bytesFreed); + tryToDelete(options, results, livePaths, tempRootsClosed, done, *i); debug(format("dead path `%1%'") % path); - deleted.insert(path); + results.paths.insert(path); /* If just returning the set of dead paths, we also return the space that would be freed if we deleted them. */ - if (action == gcReturnDead) { - bytesFreed += computePathSize(path); + if (options.action == GCOptions::gcReturnDead) { + unsigned long long bytesFreed, blocksFreed; + computePathSize(path, bytesFreed, blocksFreed); + results.bytesFreed += bytesFreed; + results.blocksFreed += blocksFreed; return; } @@ -504,9 +507,10 @@ void LocalStore::tryToDelete(GCAction action, const PathSet & livePaths, printMsg(lvlInfo, format("deleting `%1%'") % path); /* Okay, it's safe to delete. */ - unsigned long long freed; - deleteFromStore(path, freed); - bytesFreed += freed; + unsigned long long bytesFreed, blocksFreed; + deleteFromStore(path, bytesFreed, blocksFreed); + results.bytesFreed += bytesFreed; + results.blocksFreed += blocksFreed; #ifndef __CYGWIN__ if (fdLock != -1) @@ -516,12 +520,8 @@ void LocalStore::tryToDelete(GCAction action, const PathSet & livePaths, } -void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, - bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed) +void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) { - result.clear(); - bytesFreed = 0; - bool gcKeepOutputs = queryBoolSetting("gc-keep-outputs", false); bool gcKeepDerivations = @@ -537,7 +537,7 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, /* Find the roots. Since we've grabbed the GC lock, the set of permanent roots cannot increase now. */ printMsg(lvlError, format("finding garbage collector roots...")); - Roots rootMap = ignoreLiveness ? Roots() : nix::findRoots(true); + Roots rootMap = options.ignoreLiveness ? Roots() : nix::findRoots(true); PathSet roots; for (Roots::iterator i = rootMap.begin(); i != rootMap.end(); ++i) @@ -547,11 +547,11 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, NIX_ROOT_FINDER environment variable. This is typically used to add running programs to the set of roots (to prevent them from being garbage collected). */ - if (!ignoreLiveness) + if (!options.ignoreLiveness) addAdditionalRoots(roots); - if (action == gcReturnRoots) { - result = roots; + if (options.action == GCOptions::gcReturnRoots) { + results.paths = roots; return; } @@ -595,8 +595,8 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, } } - if (action == gcReturnLive) { - result = livePaths; + if (options.action == GCOptions::gcReturnLive) { + results.paths = livePaths; return; } @@ -633,27 +633,25 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, paths. */ printMsg(lvlError, format("reading the Nix store...")); PathSet storePaths; - if (action != gcDeleteSpecific) { + if (options.action != GCOptions::gcDeleteSpecific) { Paths entries = readDirectory(nixStore); for (Paths::iterator i = entries.begin(); i != entries.end(); ++i) storePaths.insert(canonPath(nixStore + "/" + *i)); } else { - for (PathSet::iterator i = pathsToDelete.begin(); - i != pathsToDelete.end(); ++i) - { + foreach (PathSet::iterator, i, options.pathsToDelete) { assertStorePath(*i); storePaths.insert(*i); } } /* Try to delete store paths in the topologically sorted order. */ - printMsg(lvlError, action == gcReturnDead + printMsg(lvlError, options.action == GCOptions::gcReturnDead ? format("looking for garbage...") : format("deleting garbage...")); PathSet done; foreach (PathSet::iterator, i, storePaths) - tryToDelete(action, livePaths, tempRootsClosed, done, result, *i, bytesFreed); + tryToDelete(options, results, livePaths, tempRootsClosed, done, *i); } diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 2e53d0dc6d..105f711228 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -851,7 +851,8 @@ Path LocalStore::importPath(bool requireSignature, Source & source) } -void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFreed) +void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFreed, + unsigned long long & blocksFreed) { bytesFreed = 0; @@ -871,7 +872,7 @@ void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFr invalidatePath(path); } - deletePathWrapped(path, bytesFreed); + deletePathWrapped(path, bytesFreed, blocksFreed); } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 444a5f3402..1957b9f579 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -25,10 +25,11 @@ struct OptimiseStats unsigned long sameContents; unsigned long filesLinked; unsigned long long bytesFreed; + unsigned long long blocksFreed; OptimiseStats() { totalFiles = sameContents = filesLinked = 0; - bytesFreed = 0; + bytesFreed = blocksFreed = 0; } }; @@ -89,11 +90,11 @@ public: Roots findRoots(); - void collectGarbage(GCAction action, const PathSet & pathsToDelete, - bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed); + void collectGarbage(const GCOptions & options, GCResults & results); /* Delete a path from the Nix store. */ - void deleteFromStore(const Path & path, unsigned long long & bytesFreed); + void deleteFromStore(const Path & path, unsigned long long & bytesFreed, + unsigned long long & blocksFreed); /* Optimise the disk space usage of the Nix store by hard-linking files with the same contents. */ @@ -143,10 +144,9 @@ private: void upgradeStore12(); - void tryToDelete(GCAction action, const PathSet & livePaths, - const PathSet & tempRootsClosed, PathSet & done, PathSet & deleted, - const Path & path, unsigned long long & bytesFreed); - + void tryToDelete(const GCOptions & options, GCResults & results, + const PathSet & livePaths, const PathSet & tempRootsClosed, PathSet & done, + const Path & path); }; @@ -179,7 +179,7 @@ void getOwnership(const Path & path); /* Like deletePath(), but changes the ownership of `path' using the setuid wrapper if necessary (and possible). */ void deletePathWrapped(const Path & path, - unsigned long long & bytesFreed); + unsigned long long & bytesFreed, unsigned long long & blocksFreed); void deletePathWrapped(const Path & path); diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index c260f253e3..bd76a1aecc 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -101,6 +101,7 @@ static void hashAndLink(bool dryRun, HashToPath & hashToPath, stats.filesLinked++; stats.bytesFreed += st.st_size; + stats.blocksFreed += st.st_blocks; } if (S_ISDIR(st.st_mode)) { diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 14412b8c8b..274196a2b6 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -372,24 +372,20 @@ Roots RemoteStore::findRoots() } -void RemoteStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, - bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed) +void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results) { - result.clear(); - bytesFreed = 0; writeInt(wopCollectGarbage, to); - writeInt(action, to); - writeStringSet(pathsToDelete, to); - writeInt(ignoreLiveness, to); + writeInt(options.action, to); + writeStringSet(options.pathsToDelete, to); + writeInt(options.ignoreLiveness, to); + writeLongLong(options.maxFreed, to); + writeInt(options.maxLinks, to); processStderr(); - result = readStringSet(from); - - /* Ugh - NAR integers are 64 bits, but read/writeInt() aren't. */ - unsigned int lo = readInt(from); - unsigned int hi = readInt(from); - bytesFreed = (((unsigned long long) hi) << 32) | lo; + results.paths = readStringSet(from); + results.bytesFreed = readLongLong(from); + results.blocksFreed = readLongLong(from); } diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 1501591e2a..c40feeaa47 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -65,8 +65,7 @@ public: Roots findRoots(); - void collectGarbage(GCAction action, const PathSet & pathsToDelete, - bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed); + void collectGarbage(const GCOptions & options, GCResults & results); private: AutoCloseFD fdSocket; diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index a50dcf6451..760e71adc7 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -16,14 +16,82 @@ namespace nix { typedef std::map Roots; -/* Garbage collector operation. */ -typedef enum { - gcReturnRoots, - gcReturnLive, - gcReturnDead, - gcDeleteDead, - gcDeleteSpecific, -} GCAction; + + +struct GCOptions +{ + /* Garbage collector operation: + + - `gcReturnRoots': find and return the set of roots for the + garbage collector. These are the store paths symlinked to in + the `gcroots' directory. + + - `gcReturnLive': return the set of paths reachable from + (i.e. in the closure of) the roots. + + - `gcReturnDead': return the set of paths not reachable from + the roots. + + - `gcDeleteDead': actually delete the latter set. + + - `gcDeleteSpecific': delete the paths listed in + `pathsToDelete', insofar as they are not reachable. + */ + typedef enum { + gcReturnRoots, + gcReturnLive, + gcReturnDead, + gcDeleteDead, + gcDeleteSpecific, + } GCAction; + + GCAction action; + + /* If `ignoreLiveness' is set, then reachability from the roots is + ignored (dangerous!). However, the paths must still be + unreferenced *within* the store (i.e., there can be no other + store paths that depend on them). */ + bool ignoreLiveness; + + /* For `gcDeleteSpecific', the paths to delete. */ + PathSet pathsToDelete; + + /* Stop after at least `maxFreed' bytes have been freed. */ + unsigned long long maxFreed; + + /* Stop after the number of hard links to the Nix store directory + has dropped to at least `maxLinks'. */ + unsigned int maxLinks; + + GCOptions() + { + action = gcDeleteDead; + ignoreLiveness = false; + maxFreed = ULLONG_MAX; + maxLinks = UINT_MAX; + } +}; + + +struct GCResults +{ + /* Depending on the action, the GC roots, or the paths that would + be or have been deleted. */ + PathSet paths; + + /* For `gcReturnDead', `gcDeleteDead' and `gcDeleteSpecific', the + number of bytes that would be or was freed. */ + unsigned long long bytesFreed; + + /* The number of file system blocks that would be or was freed. */ + unsigned long long blocksFreed; + + GCResults() + { + bytesFreed = 0; + blocksFreed = 0; + } +}; class StoreAPI @@ -137,33 +205,8 @@ public: outside of the Nix store that point to `storePath'. */ virtual Roots findRoots() = 0; - /* Depending on `action', this function does the following: - - - `gcReturnRoots': find and return the set of roots for the - garbage collector. These are the store paths symlinked to in - the `gcroots' directory. - - - `gcReturnLive': return the set of paths reachable from - (i.e. in the closure of) the roots. - - - `gcReturnDead': return the set of paths not reachable from - the roots. - - - `gcDeleteDead': actually delete the latter set. - - - `gcDeleteSpecific': delete the paths listed in - `pathsToDelete', insofar as they are not reachable. - - If `ignoreLiveness' is set, then reachability from the roots is - ignored (dangerous!). However, the paths must still be - unreferenced *within* the store (i.e., there can be no other - store paths that depend on them). - - For `gcReturnDead', `gcDeleteDead' and `gcDeleteSpecific', the - number of bytes that would be or was freed is returned in - `bytesFreed'. */ - virtual void collectGarbage(GCAction action, const PathSet & pathsToDelete, - bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed) = 0; + /* Perform a garbage collection. */ + virtual void collectGarbage(const GCOptions & options, GCResults & results) = 0; }; diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 44db4ad9b1..d887ee59bd 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -15,24 +15,24 @@ namespace nix { typedef enum { wopQuit = 0, - wopIsValidPath, + wopIsValidPath = 1, wopHasSubstitutes = 3, - wopQueryPathHash, - wopQueryReferences, - wopQueryReferrers, - wopAddToStore, - wopAddTextToStore, - wopBuildDerivations, - wopEnsurePath, - wopAddTempRoot, - wopAddIndirectRoot, - wopSyncWithGC, - wopFindRoots, - wopCollectGarbage, - wopExportPath, - wopImportPath, - wopQueryDeriver, - wopSetOptions, + wopQueryPathHash = 4, + wopQueryReferences = 5, + wopQueryReferrers = 6, + wopAddToStore = 7, + wopAddTextToStore = 8, + wopBuildDerivations = 9, + wopEnsurePath = 10, + wopAddTempRoot = 11, + wopAddIndirectRoot = 12, + wopSyncWithGC = 13, + wopFindRoots = 14, + wopExportPath = 16, + wopImportPath = 17, + wopQueryDeriver = 18, + wopSetOptions = 19, + wopCollectGarbage = 20, } WorkerOp; diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index c3fd4dd10c..c13e8c7e38 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -41,6 +41,21 @@ void writeInt(unsigned int n, Sink & sink) } +void writeLongLong(unsigned long long n, Sink & sink) +{ + unsigned char buf[8]; + buf[0] = n & 0xff; + buf[1] = (n >> 8) & 0xff; + buf[2] = (n >> 16) & 0xff; + buf[3] = (n >> 24) & 0xff; + buf[4] = (n >> 32) & 0xff; + buf[5] = (n >> 40) & 0xff; + buf[6] = (n >> 48) & 0xff; + buf[7] = (n >> 56) & 0xff; + sink(buf, sizeof(buf)); +} + + void writeString(const string & s, Sink & sink) { unsigned int len = s.length(); @@ -84,6 +99,22 @@ unsigned int readInt(Source & source) } +unsigned long long readLongLong(Source & source) +{ + unsigned char buf[8]; + source(buf, sizeof(buf)); + return + ((unsigned long long) buf[0]) | + ((unsigned long long) buf[1] << 8) | + ((unsigned long long) buf[2] << 16) | + ((unsigned long long) buf[3] << 24) | + ((unsigned long long) buf[4] << 32) | + ((unsigned long long) buf[5] << 40) | + ((unsigned long long) buf[6] << 48) | + ((unsigned long long) buf[7] << 56); +} + + string readString(Source & source) { unsigned int len = readInt(source); diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index c18e82463b..75d17bc605 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -95,11 +95,13 @@ struct StringSource : Source void writePadding(unsigned int len, Sink & sink); void writeInt(unsigned int n, Sink & sink); +void writeLongLong(unsigned long long n, Sink & sink); void writeString(const string & s, Sink & sink); void writeStringSet(const StringSet & ss, Sink & sink); void readPadding(unsigned int len, Source & source); unsigned int readInt(Source & source); +unsigned long long readLongLong(Source & source); string readString(Source & source); StringSet readStringSet(Source & source); diff --git a/src/libutil/util.cc b/src/libutil/util.cc index e18f9841fd..1873ccfe53 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -229,30 +229,38 @@ void writeFile(const Path & path, const string & s) } -unsigned long long computePathSize(const Path & path) +static void _computePathSize(const Path & path, + unsigned long long & bytes, unsigned long long & blocks) { - unsigned long long size = 0; - checkInterrupt(); struct stat st; if (lstat(path.c_str(), &st)) throw SysError(format("getting attributes of path `%1%'") % path); - size += st.st_size; + bytes += st.st_size; + blocks += st.st_blocks; if (S_ISDIR(st.st_mode)) { Strings names = readDirectory(path); for (Strings::iterator i = names.begin(); i != names.end(); ++i) - size += computePathSize(path + "/" + *i); + _computePathSize(path + "/" + *i, bytes, blocks); } - - return size; } -static void _deletePath(const Path & path, unsigned long long & bytesFreed) +void computePathSize(const Path & path, + unsigned long long & bytes, unsigned long long & blocks) +{ + bytes = 0; + blocks = 0; + _computePathSize(path, bytes, blocks); +} + + +static void _deletePath(const Path & path, unsigned long long & bytesFreed, + unsigned long long & blocksFreed) { checkInterrupt(); @@ -263,6 +271,7 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed) throw SysError(format("getting attributes of path `%1%'") % path); bytesFreed += st.st_size; + blocksFreed += st.st_blocks; if (S_ISDIR(st.st_mode)) { Strings names = readDirectory(path); @@ -274,7 +283,7 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed) } for (Strings::iterator i = names.begin(); i != names.end(); ++i) - _deletePath(path + "/" + *i, bytesFreed); + _deletePath(path + "/" + *i, bytesFreed, blocksFreed); } if (remove(path.c_str()) == -1) @@ -284,17 +293,19 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed) void deletePath(const Path & path) { - unsigned long long dummy; - deletePath(path, dummy); + unsigned long long dummy1, dummy2; + deletePath(path, dummy1, dummy2); } -void deletePath(const Path & path, unsigned long long & bytesFreed) +void deletePath(const Path & path, unsigned long long & bytesFreed, + unsigned long long & blocksFreed) { startNest(nest, lvlDebug, format("recursively deleting path `%1%'") % path); bytesFreed = 0; - _deletePath(path, bytesFreed); + blocksFreed = 0; + _deletePath(path, bytesFreed, blocksFreed); } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index d52ab3e4d9..d1e30fa6b9 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -61,14 +61,16 @@ string readFile(const Path & path); void writeFile(const Path & path, const string & s); /* Compute the sum of the sizes of all files in `path'. */ -unsigned long long computePathSize(const Path & path); +void computePathSize(const Path & path, + unsigned long long & bytes, unsigned long long & blocks); /* Delete a path; i.e., in the case of a directory, it is deleted recursively. Don't use this at home, kids. The second variant - returns the number of bytes freed. */ + returns the number of bytes and blocks freed. */ void deletePath(const Path & path); -void deletePath(const Path & path, unsigned long long & bytesFreed); +void deletePath(const Path & path, unsigned long long & bytesFreed, + unsigned long long & blocksFreed); /* Make a path read-only recursively. */ void makePathReadOnly(const Path & path); diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index df027fcc76..9618823ea6 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -489,19 +489,19 @@ static void opCheckValidity(Strings opFlags, Strings opArgs) } -static string showBytes(unsigned long long bytes) +static string showBytes(unsigned long long bytes, unsigned long long blocks) { - return (format("%d bytes (%.2f MiB)") - % bytes % (bytes / (1024.0 * 1024.0))).str(); + return (format("%d bytes (%.2f MiB, %d blocks)") + % bytes % (bytes / (1024.0 * 1024.0)) % blocks).str(); } struct PrintFreed { bool show, dryRun; - unsigned long long bytesFreed; - PrintFreed(bool show, bool dryRun) - : show(show), dryRun(dryRun), bytesFreed(0) { } + const GCResults & results; + PrintFreed(bool show, bool dryRun, const GCResults & results) + : show(show), dryRun(dryRun), results(results) { } ~PrintFreed() { if (show) @@ -509,33 +509,35 @@ struct PrintFreed (dryRun ? "%1% would be freed\n" : "%1% freed\n")) - % showBytes(bytesFreed); + % showBytes(results.bytesFreed, results.blocksFreed); } }; static void opGC(Strings opFlags, Strings opArgs) { - GCAction action = gcDeleteDead; + GCOptions options; + options.action = GCOptions::gcDeleteDead; + + GCResults results; /* Do what? */ for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ++i) - if (*i == "--print-roots") action = gcReturnRoots; - else if (*i == "--print-live") action = gcReturnLive; - else if (*i == "--print-dead") action = gcReturnDead; - else if (*i == "--delete") action = gcDeleteDead; + if (*i == "--print-roots") options.action = GCOptions::gcReturnRoots; + else if (*i == "--print-live") options.action = GCOptions::gcReturnLive; + else if (*i == "--print-dead") options.action = GCOptions::gcReturnDead; + else if (*i == "--delete") options.action = GCOptions::gcDeleteDead; else throw UsageError(format("bad sub-operation `%1%' in GC") % *i); - PathSet result; - PrintFreed freed(action == gcDeleteDead || action == gcReturnDead, - action == gcReturnDead); - store->collectGarbage(action, PathSet(), false, result, freed.bytesFreed); + PrintFreed freed( + options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcReturnDead, + options.action == GCOptions::gcReturnDead, results); + store->collectGarbage(options, results); - if (action != gcDeleteDead) { - for (PathSet::iterator i = result.begin(); i != result.end(); ++i) + if (options.action != GCOptions::gcDeleteDead) + foreach (PathSet::iterator, i, results.paths) cout << *i << std::endl; - } } @@ -544,22 +546,21 @@ static void opGC(Strings opFlags, Strings opArgs) roots). */ static void opDelete(Strings opFlags, Strings opArgs) { - bool ignoreLiveness = false; + GCOptions options; + options.action = GCOptions::gcDeleteSpecific; for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ++i) - if (*i == "--ignore-liveness") ignoreLiveness = true; + if (*i == "--ignore-liveness") options.ignoreLiveness = true; else throw UsageError(format("unknown flag `%1%'") % *i); - PathSet pathsToDelete; for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i) - pathsToDelete.insert(followLinksToStorePath(*i)); + options.pathsToDelete.insert(followLinksToStorePath(*i)); - PathSet dummy; - PrintFreed freed(true, false); - store->collectGarbage(gcDeleteSpecific, pathsToDelete, ignoreLiveness, - dummy, freed.bytesFreed); + GCResults results; + PrintFreed freed(true, false, results); + store->collectGarbage(options, results); } @@ -653,7 +654,7 @@ static void showOptimiseStats(OptimiseStats & stats) { printMsg(lvlError, format("%1% freed by hard-linking %2% files; there are %3% files with equal contents out of %4% files in total") - % showBytes(stats.bytesFreed) + % showBytes(stats.bytesFreed, stats.blocksFreed) % stats.filesLinked % stats.sameContents % stats.totalFiles); diff --git a/src/nix-worker/nix-worker.cc b/src/nix-worker/nix-worker.cc index 0d0964f3a6..d8d86434ea 100644 --- a/src/nix-worker/nix-worker.cc +++ b/src/nix-worker/nix-worker.cc @@ -395,23 +395,24 @@ static void performOp(unsigned int clientVersion, } case wopCollectGarbage: { - GCAction action = (GCAction) readInt(from); - PathSet pathsToDelete = readStorePaths(from); - bool ignoreLiveness = readInt(from); + GCOptions options; + options.action = (GCOptions::GCAction) readInt(from); + options.pathsToDelete = readStorePaths(from); + options.ignoreLiveness = readInt(from); + options.maxFreed = readLongLong(from); + options.maxLinks = readInt(from); - PathSet result; - unsigned long long bytesFreed; + GCResults results; startWork(); - if (ignoreLiveness) + if (options.ignoreLiveness) throw Error("you are not allowed to ignore liveness"); - store->collectGarbage(action, pathsToDelete, ignoreLiveness, - result, bytesFreed); + store->collectGarbage(options, results); stopWork(); - writeStringSet(result, to); - writeInt(bytesFreed & 0xffffffff, to); - writeInt(bytesFreed >> 32, to); + writeStringSet(results.paths, to); + writeLongLong(results.bytesFreed, to); + writeLongLong(results.blocksFreed, to); break; }