* Topologically sort paths under the references relation to ensure
that they are deleted in an order that maintains the closure invariant. * Presence of a path in a temporary roots file does not imply that all paths in its closure are also present, so add the closure.
This commit is contained in:
		
							parent
							
								
									33c5d23b81
								
							
						
					
					
						commit
						252c9c91ab
					
				
					 3 changed files with 83 additions and 38 deletions
				
			
		|  | @ -110,7 +110,6 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds) | |||
|             throw SysError(format("statting `%1%'") % path); | ||||
|         unsigned char buf[st.st_size]; /* !!! stack space */ | ||||
|         readFull(*fd, buf, st.st_size); | ||||
|         debug(format("FILE SIZE %1%") % st.st_size); | ||||
| 
 | ||||
|         /* Extract the roots. */ | ||||
|         string contents((char *) buf, st.st_size); | ||||
|  | @ -129,6 +128,37 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void dfsVisit(const PathSet & paths, const Path & path, | ||||
|     PathSet & visited, Paths & sorted) | ||||
| { | ||||
|     if (visited.find(path) != visited.end()) return; | ||||
|     visited.insert(path); | ||||
|      | ||||
|     PathSet references; | ||||
|     if (isValidPath(path)) | ||||
|         queryReferences(path, references); | ||||
|      | ||||
|     for (PathSet::iterator i = references.begin(); | ||||
|          i != references.end(); ++i) | ||||
|         /* Don't traverse into paths that don't exist.  That can
 | ||||
|            happen due to substitutes for non-existent paths. */ | ||||
|         if (*i != path && paths.find(*i) != paths.end()) | ||||
|             dfsVisit(paths, *i, visited, sorted); | ||||
| 
 | ||||
|     sorted.push_front(path); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static Paths topoSort(const PathSet & paths) | ||||
| { | ||||
|     Paths sorted; | ||||
|     PathSet visited; | ||||
|     for (PathSet::const_iterator i = paths.begin(); i != paths.end(); ++i) | ||||
|         dfsVisit(paths, *i, visited, sorted); | ||||
|     return sorted; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void collectGarbage(const PathSet & roots, GCAction action, | ||||
|     PathSet & result) | ||||
| { | ||||
|  | @ -160,74 +190,86 @@ void collectGarbage(const PathSet & roots, GCAction action, | |||
|     FDs fds; | ||||
|     readTempRoots(tempRoots, fds); | ||||
| 
 | ||||
|     for (FDs::iterator i = fds.begin(); i != fds.end(); ++i) | ||||
|         debug(format("FD %1%") % (int) **i); | ||||
|     /* Close the temporary roots.  Note that we *cannot* do this in
 | ||||
|        readTempRoots(), because there we may not have all locks yet, | ||||
|        meaning that an invalid path can become valid (and thus add to | ||||
|        the references graph) after we have added it to the closure | ||||
|        (and computeFSClosure() assumes that the presence of a path | ||||
|        means that it has already been closed). */ | ||||
|     PathSet tempRootsClosed; | ||||
|     for (PathSet::iterator i = tempRoots.begin(); i != tempRoots.end(); ++i) | ||||
|         if (isValidPath(*i)) | ||||
|             computeFSClosure(*i, tempRootsClosed); | ||||
|         else | ||||
|             tempRootsClosed.insert(*i); | ||||
| 
 | ||||
|     /* After this point the set of roots or temporary roots cannot
 | ||||
|        increase, since we hold locks on everything.  So everything | ||||
|        that is not currently in in `livePaths' or `tempRootsClosed' | ||||
|        can be deleted. */ | ||||
|      | ||||
|     /* !!! TODO: Try to acquire (without blocking) exclusive locks on
 | ||||
|        the files in the `pending' directory.  Delete all files for | ||||
|        which we managed to acquire such a lock (since if we could get | ||||
|        such a lock, that means that the process that owned the file | ||||
|        has died). */ | ||||
| 
 | ||||
|     /* !!! TODO: Acquire shared locks on all files in the pending
 | ||||
|        directories.  This prevents the set of pending paths from | ||||
|        increasing while we are garbage-collecting.  Read the set of | ||||
|        pending paths from those files. */ | ||||
| 
 | ||||
|     /* Read the Nix store directory to find all currently existing
 | ||||
|        paths. */ | ||||
|     Strings storeNames = readDirectory(nixStore); | ||||
|     Paths storePaths = readDirectory(nixStore); | ||||
|     PathSet storePaths2; | ||||
|     for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) | ||||
|         storePaths2.insert(canonPath(nixStore + "/" + *i)); | ||||
| 
 | ||||
|     for (Strings::iterator i = storeNames.begin(); i != storeNames.end(); ++i) { | ||||
|         Path path = canonPath(nixStore + "/" + *i); | ||||
|     /* Topologically sort them under the `referers' relation.  That
 | ||||
|        is, a < b iff a is in referers(b).  This gives us the order in | ||||
|        which things can be deleted safely. */ | ||||
|     /* !!! when we have multiple output paths per derivation, this
 | ||||
|        will not work anymore because we get cycles. */ | ||||
|     storePaths = topoSort(storePaths2); | ||||
|      | ||||
|     for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) { | ||||
| 
 | ||||
|         if (livePaths.find(path) != livePaths.end()) { | ||||
|             debug(format("live path `%1%'") % path); | ||||
|         debug(format("considering deletion of `%1%'") % *i); | ||||
|          | ||||
|         if (livePaths.find(*i) != livePaths.end()) { | ||||
|             debug(format("live path `%1%'") % *i); | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         if (tempRoots.find(path) != tempRoots.end()) { | ||||
|             debug(format("temporary root `%1%'") % path); | ||||
|         if (tempRootsClosed.find(*i) != tempRootsClosed.end()) { | ||||
|             debug(format("temporary root `%1%'") % *i); | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         debug(format("dead path `%1%'") % path); | ||||
|         result.insert(path); | ||||
|         debug(format("dead path `%1%'") % *i); | ||||
|         result.insert(*i); | ||||
| 
 | ||||
|         AutoCloseFD fdLock; | ||||
| 
 | ||||
|         if (action == gcDeleteDead) { | ||||
|             printMsg(lvlInfo, format("deleting `%1%'") % path); | ||||
| 
 | ||||
|             /* Only delete a lock file if we can acquire a write lock
 | ||||
|                on it.  That means that it's either stale, or the | ||||
|                process that created it hasn't locked it yet.  In the | ||||
|                latter case the other process will detect that we | ||||
|                deleted the lock, and retry (see pathlocks.cc). */ | ||||
|             if (path.size() >= 5 && string(path, path.size() - 5) == ".lock") { | ||||
|             if (i->size() >= 5 && string(*i, i->size() - 5) == ".lock") { | ||||
| 
 | ||||
|                 fdLock = open(path.c_str(), O_RDWR); | ||||
|                 fdLock = open(i->c_str(), O_RDWR); | ||||
|                 if (fdLock == -1) { | ||||
|                     if (errno == ENOENT) continue; | ||||
|                     throw SysError(format("opening lock file `%1%'") % path); | ||||
|                     throw SysError(format("opening lock file `%1%'") % *i); | ||||
|                 } | ||||
| 
 | ||||
|                 if (!lockFile(fdLock, ltWrite, false)) { | ||||
|                     debug(format("skipping active lock `%1%'") % path); | ||||
|                     debug(format("skipping active lock `%1%'") % *i); | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             printMsg(lvlInfo, format("deleting `%1%'") % *i); | ||||
|              | ||||
|             deleteFromStore(path); | ||||
|             /* Okay, it's safe to delete. */ | ||||
|             deleteFromStore(*i); | ||||
| 
 | ||||
|             if (fdLock != -1) | ||||
|                 /* Write token to stale (deleted) lock file. */ | ||||
|                 writeFull(fdLock, (const unsigned char *) "d", 1); | ||||
|         } | ||||
| 
 | ||||
|         /* Only delete lock files if the path is belongs to doesn't
 | ||||
|            exist and isn't a temporary root and we can acquire an | ||||
|            exclusive lock on it. */ | ||||
|         /* !!! */ | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -311,10 +311,9 @@ void queryReferences(const Path & storePath, PathSet & references) | |||
| 
 | ||||
| void queryReferers(const Path & storePath, PathSet & referers) | ||||
| { | ||||
|     Paths referers2; | ||||
|     if (!isRealisablePath(noTxn, storePath)) | ||||
|         throw Error(format("path `%1%' is not valid") % storePath); | ||||
|     nixDB.queryStrings(noTxn, dbReferers, storePath, referers2); | ||||
|     PathSet referers2 = getReferers(noTxn, storePath); | ||||
|     referers.insert(referers2.begin(), referers2.end()); | ||||
| } | ||||
| 
 | ||||
|  | @ -427,6 +426,8 @@ void registerValidPath(const Transaction & txn, | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Invalidate a path.  The caller is responsible for checking that
 | ||||
|    there are no referers. */ | ||||
| static void invalidatePath(const Path & path, Transaction & txn) | ||||
| { | ||||
|     debug(format("unregistering path `%1%'") % path); | ||||
|  | @ -551,8 +552,11 @@ void deleteFromStore(const Path & _path) | |||
|     assertStorePath(path); | ||||
| 
 | ||||
|     Transaction txn(nixDB); | ||||
|     if (isValidPathTxn(txn, path)) | ||||
|     if (isValidPathTxn(txn, path)) { | ||||
|         if (getReferers(txn, path).size() > 0) | ||||
|             throw Error(format("cannot delete path `%1%' because it is in use") % path); | ||||
|         invalidatePath(path, txn); | ||||
|     } | ||||
|     txn.commit(); | ||||
| 
 | ||||
|     deletePath(path); | ||||
|  |  | |||
|  | @ -407,7 +407,6 @@ AutoCloseFD::operator int() const | |||
| void AutoCloseFD::close() | ||||
| { | ||||
|     if (fd != -1) { | ||||
|         debug(format("closing fd %1%") % fd); | ||||
|         if (::close(fd) == -1) | ||||
|             /* This should never happen. */ | ||||
|             throw SysError("closing file descriptor"); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue