11#include " derivations.hh"
22#include " globals.hh"
33#include " local-store.hh"
4+ #include " finally.hh"
45
56#include < functional>
67#include < queue>
910
1011#include < sys/types.h>
1112#include < sys/stat.h>
13+ #include < sys/statvfs.h>
1214#include < errno.h>
1315#include < fcntl.h>
1416#include < unistd.h>
@@ -18,7 +20,6 @@ namespace nix {
1820
1921
2022static string gcLockName = " gc.lock" ;
21- static string tempRootsDir = " temproots" ;
2223static string gcRootsDir = " gcroots" ;
2324
2425
@@ -153,30 +154,25 @@ void LocalStore::addTempRoot(const Path & path)
153154 if (!state->fdTempRoots ) {
154155
155156 while (1 ) {
156- Path dir = (format (" %1%/%2%" ) % stateDir % tempRootsDir).str ();
157- createDirs (dir);
158-
159- state->fnTempRoots = (format (" %1%/%2%" ) % dir % getpid ()).str ();
160-
161157 AutoCloseFD fdGCLock = openGCLock (ltRead);
162158
163- if (pathExists (state-> fnTempRoots ))
159+ if (pathExists (fnTempRoots))
164160 /* It *must* be stale, since there can be no two
165161 processes with the same pid. */
166- unlink (state-> fnTempRoots .c_str ());
162+ unlink (fnTempRoots.c_str ());
167163
168- state->fdTempRoots = openLockFile (state-> fnTempRoots , true );
164+ state->fdTempRoots = openLockFile (fnTempRoots, true );
169165
170166 fdGCLock = -1 ;
171167
172- debug (format (" acquiring read lock on '%1%'" ) % state-> fnTempRoots );
168+ debug (format (" acquiring read lock on '%1%'" ) % fnTempRoots);
173169 lockFile (state->fdTempRoots .get (), ltRead, true );
174170
175171 /* Check whether the garbage collector didn't get in our
176172 way. */
177173 struct stat st;
178174 if (fstat (state->fdTempRoots .get (), &st) == -1 )
179- throw SysError (format (" statting '%1%'" ) % state-> fnTempRoots );
175+ throw SysError (format (" statting '%1%'" ) % fnTempRoots);
180176 if (st.st_size == 0 ) break ;
181177
182178 /* The garbage collector deleted this file before we could
@@ -188,14 +184,14 @@ void LocalStore::addTempRoot(const Path & path)
188184
189185 /* Upgrade the lock to a write lock. This will cause us to block
190186 if the garbage collector is holding our lock. */
191- debug (format (" acquiring write lock on '%1%'" ) % state-> fnTempRoots );
187+ debug (format (" acquiring write lock on '%1%'" ) % fnTempRoots);
192188 lockFile (state->fdTempRoots .get (), ltWrite, true );
193189
194190 string s = path + ' \0 ' ;
195191 writeFull (state->fdTempRoots .get (), s);
196192
197193 /* Downgrade to a read lock. */
198- debug (format (" downgrading to read lock on '%1%'" ) % state-> fnTempRoots );
194+ debug (format (" downgrading to read lock on '%1%'" ) % fnTempRoots);
199195 lockFile (state->fdTempRoots .get (), ltRead, true );
200196}
201197
@@ -204,11 +200,10 @@ void LocalStore::readTempRoots(PathSet & tempRoots, FDs & fds)
204200{
205201 /* Read the `temproots' directory for per-process temporary root
206202 files. */
207- DirEntries tempRootFiles = readDirectory (
208- (format (" %1%/%2%" ) % stateDir % tempRootsDir).str ());
203+ DirEntries tempRootFiles = readDirectory (tempRootsDir);
209204
210205 for (auto & i : tempRootFiles) {
211- Path path = ( format ( " %1%/%2%/%3% " ) % stateDir % tempRootsDir % i.name ). str () ;
206+ Path path = tempRootsDir + " / " + i.name ;
212207
213208 debug (format (" reading temporary root file '%1%'" ) % path);
214209 FDPtr fd (new AutoCloseFD (open (path.c_str (), O_CLOEXEC | O_RDWR, 0666 )));
@@ -222,21 +217,25 @@ void LocalStore::readTempRoots(PathSet & tempRoots, FDs & fds)
222217 // FDPtr fd(new AutoCloseFD(openLockFile(path, false)));
223218 // if (*fd == -1) continue;
224219
225- /* Try to acquire a write lock without blocking. This can
226- only succeed if the owning process has died. In that case
227- we don't care about its temporary roots. */
228- if (lockFile (fd->get (), ltWrite, false )) {
229- printError (format (" removing stale temporary roots file '%1%'" ) % path);
230- unlink (path.c_str ());
231- writeFull (fd->get (), " d" );
232- continue ;
233- }
220+ if (path != fnTempRoots) {
221+
222+ /* Try to acquire a write lock without blocking. This can
223+ only succeed if the owning process has died. In that case
224+ we don't care about its temporary roots. */
225+ if (lockFile (fd->get (), ltWrite, false )) {
226+ printError (format (" removing stale temporary roots file '%1%'" ) % path);
227+ unlink (path.c_str ());
228+ writeFull (fd->get (), " d" );
229+ continue ;
230+ }
231+
232+ /* Acquire a read lock. This will prevent the owning process
233+ from upgrading to a write lock, therefore it will block in
234+ addTempRoot(). */
235+ debug (format (" waiting for read lock on '%1%'" ) % path);
236+ lockFile (fd->get (), ltRead, true );
234237
235- /* Acquire a read lock. This will prevent the owning process
236- from upgrading to a write lock, therefore it will block in
237- addTempRoot(). */
238- debug (format (" waiting for read lock on '%1%'" ) % path);
239- lockFile (fd->get (), ltRead, true );
238+ }
240239
241240 /* Read the entire file. */
242241 string contents = readFile (fd->get ());
@@ -848,4 +847,80 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
848847}
849848
850849
850+ void LocalStore::autoGC (bool sync)
851+ {
852+ auto getAvail = [this ]() {
853+ struct statvfs st;
854+ if (statvfs (realStoreDir.c_str (), &st))
855+ throw SysError (" getting filesystem info about '%s'" , realStoreDir);
856+
857+ return (uint64_t ) st.f_bavail * st.f_bsize ;
858+ };
859+
860+ std::shared_future<void > future;
861+
862+ {
863+ auto state (_state.lock ());
864+
865+ if (state->gcRunning ) {
866+ future = state->gcFuture ;
867+ debug (" waiting for auto-GC to finish" );
868+ goto sync;
869+ }
870+
871+ auto now = std::chrono::steady_clock::now ();
872+
873+ if (now < state->lastGCCheck + std::chrono::seconds (5 )) return ;
874+
875+ auto avail = getAvail ();
876+
877+ state->lastGCCheck = now;
878+
879+ if (avail >= settings.minFree || avail >= settings.maxFree ) return ;
880+
881+ if (avail > state->availAfterGC * 0.97 ) return ;
882+
883+ state->gcRunning = true ;
884+
885+ std::promise<void > promise;
886+ future = state->gcFuture = promise.get_future ().share ();
887+
888+ std::thread ([promise{std::move (promise)}, this , avail, getAvail]() mutable {
889+
890+ try {
891+
892+ /* Wake up any threads waiting for the auto-GC to finish. */
893+ Finally wakeup ([&]() {
894+ auto state (_state.lock ());
895+ state->gcRunning = false ;
896+ state->lastGCCheck = std::chrono::steady_clock::now ();
897+ promise.set_value ();
898+ });
899+
900+ printInfo (" running auto-GC to free %d bytes" , settings.maxFree - avail);
901+
902+ GCOptions options;
903+ options.maxFreed = settings.maxFree - avail;
904+
905+ GCResults results;
906+
907+ collectGarbage (options, results);
908+
909+ _state.lock ()->availAfterGC = getAvail ();
910+
911+ } catch (...) {
912+ // FIXME: we could propagate the exception to the
913+ // future, but we don't really care.
914+ ignoreException ();
915+ }
916+
917+ }).detach ();
918+ }
919+
920+ sync:
921+ // Wait for the future outside of the state lock.
922+ if (sync) future.get ();
923+ }
924+
925+
851926}
0 commit comments