* Concurrent GC on Cygwin.
This commit is contained in:
		
							parent
							
								
									cc51f9c539
								
							
						
					
					
						commit
						dbf6d7e783
					
				
					 3 changed files with 146 additions and 73 deletions
				
			
		|  | @ -11,6 +11,11 @@ | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
| 
 | 
 | ||||||
|  | #ifdef __CYGWIN__ | ||||||
|  | #include <windows.h> | ||||||
|  | #include <sys/cygwin.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| static string gcLockName = "gc.lock"; | static string gcLockName = "gc.lock"; | ||||||
| static string tempRootsDir = "temproots"; | static string tempRootsDir = "temproots"; | ||||||
|  | @ -108,10 +113,6 @@ static AutoCloseFD fdTempRoots; | ||||||
| 
 | 
 | ||||||
| void addTempRoot(const Path & path) | void addTempRoot(const Path & path) | ||||||
| { | { | ||||||
| #ifdef __CYGWIN__ |  | ||||||
|     return; |  | ||||||
| #endif	 |  | ||||||
|      |  | ||||||
|     /* Create the temporary roots file for this process. */ |     /* Create the temporary roots file for this process. */ | ||||||
|     if (fdTempRoots == -1) { |     if (fdTempRoots == -1) { | ||||||
| 
 | 
 | ||||||
|  | @ -124,12 +125,24 @@ void addTempRoot(const Path & path) | ||||||
| 
 | 
 | ||||||
|             AutoCloseFD fdGCLock = openGCLock(ltRead); |             AutoCloseFD fdGCLock = openGCLock(ltRead); | ||||||
|              |              | ||||||
|             fdTempRoots = open(fnTempRoots.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600); |             if (pathExists(fnTempRoots)) | ||||||
|             if (fdTempRoots == -1) |                 /* It *must* be stale, since there can be no two
 | ||||||
|                 throw SysError(format("opening temporary roots file `%1%'") % fnTempRoots); |                    processes with the same pid. */ | ||||||
|  |                 deletePath(fnTempRoots); | ||||||
|  | 
 | ||||||
|  | 	    fdTempRoots = openLockFile(fnTempRoots, true); | ||||||
| 
 | 
 | ||||||
|             fdGCLock.close(); |             fdGCLock.close(); | ||||||
|        |        | ||||||
|  | 	    /* Note that on Cygwin a lot of the following complexity
 | ||||||
|  | 	       is unnecessary, since we cannot delete open lock | ||||||
|  | 	       files.  If we have the lock file open, then it's valid; | ||||||
|  | 	       if we can delete it, then it wasn't in use any more.  | ||||||
|  | 
 | ||||||
|  | 	       Also note that on Cygwin we cannot "upgrade" a lock | ||||||
|  | 	       from a read lock to a write lock. */ | ||||||
|  | 
 | ||||||
|  | #ifndef __CYGWIN__ | ||||||
|             debug(format("acquiring read lock on `%1%'") % fnTempRoots); |             debug(format("acquiring read lock on `%1%'") % fnTempRoots); | ||||||
|             lockFile(fdTempRoots, ltRead, true); |             lockFile(fdTempRoots, ltRead, true); | ||||||
| 
 | 
 | ||||||
|  | @ -143,6 +156,10 @@ void addTempRoot(const Path & path) | ||||||
|             /* The garbage collector deleted this file before we could
 |             /* The garbage collector deleted this file before we could
 | ||||||
|                get a lock.  (It won't delete the file after we get a |                get a lock.  (It won't delete the file after we get a | ||||||
|                lock.)  Try again. */ |                lock.)  Try again. */ | ||||||
|  | 
 | ||||||
|  | #else | ||||||
|  |             break; | ||||||
|  | #endif | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  | @ -155,9 +172,14 @@ void addTempRoot(const Path & path) | ||||||
|     string s = path + '\0'; |     string s = path + '\0'; | ||||||
|     writeFull(fdTempRoots, (const unsigned char *) s.c_str(), s.size()); |     writeFull(fdTempRoots, (const unsigned char *) s.c_str(), s.size()); | ||||||
| 
 | 
 | ||||||
|  | #ifndef __CYGWIN__ | ||||||
|     /* Downgrade to a read lock. */ |     /* Downgrade to a read lock. */ | ||||||
|     debug(format("downgrading to read lock on `%1%'") % fnTempRoots); |     debug(format("downgrading to read lock on `%1%'") % fnTempRoots); | ||||||
|     lockFile(fdTempRoots, ltRead, true); |     lockFile(fdTempRoots, ltRead, true); | ||||||
|  | #else | ||||||
|  |     debug(format("releasing write lock on `%1%'") % fnTempRoots); | ||||||
|  |     lockFile(fdTempRoots, ltNone, true); | ||||||
|  | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -176,10 +198,6 @@ typedef list<FDPtr> FDs; | ||||||
| 
 | 
 | ||||||
| static void readTempRoots(PathSet & tempRoots, FDs & fds) | static void readTempRoots(PathSet & tempRoots, FDs & fds) | ||||||
| { | { | ||||||
| #ifdef __CYGWIN__ |  | ||||||
|     return; |  | ||||||
| #endif |  | ||||||
|      |  | ||||||
|     /* Read the `temproots' directory for per-process temporary root
 |     /* Read the `temproots' directory for per-process temporary root
 | ||||||
|        files. */ |        files. */ | ||||||
|     Strings tempRootFiles = readDirectory( |     Strings tempRootFiles = readDirectory( | ||||||
|  | @ -192,6 +210,18 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds) | ||||||
| 
 | 
 | ||||||
|         debug(format("reading temporary root file `%1%'") % path); |         debug(format("reading temporary root file `%1%'") % path); | ||||||
| 
 | 
 | ||||||
|  | #ifdef __CYGWIN__ | ||||||
|  | 	/* On Cygwin we just try to delete the lock file. */ | ||||||
|  | 	char win32Path[MAX_PATH]; | ||||||
|  | 	cygwin_conv_to_full_win32_path(path.c_str(), win32Path); | ||||||
|  | 	if (DeleteFile(win32Path)) { | ||||||
|  |             printMsg(lvlError, format("removed stale temporary roots file `%1%'") | ||||||
|  |                 % path); | ||||||
|  |             continue; | ||||||
|  |         } else | ||||||
|  |             debug(format("delete of `%1%' failed: %2%") % path % GetLastError()); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|         FDPtr fd(new AutoCloseFD(open(path.c_str(), O_RDWR, 0666))); |         FDPtr fd(new AutoCloseFD(open(path.c_str(), O_RDWR, 0666))); | ||||||
|         if (*fd == -1) { |         if (*fd == -1) { | ||||||
|             /* It's okay if the file has disappeared. */ |             /* It's okay if the file has disappeared. */ | ||||||
|  | @ -199,6 +229,11 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds) | ||||||
|             throw SysError(format("opening temporary roots file `%1%'") % path); |             throw SysError(format("opening temporary roots file `%1%'") % path); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         /* This should work, but doesn't, for some reason. */ | ||||||
|  |         //FDPtr fd(new AutoCloseFD(openLockFile(path, false)));
 | ||||||
|  |         //if (*fd == -1) continue;
 | ||||||
|  | 
 | ||||||
|  | #ifndef __CYGWIN__ | ||||||
|         /* Try to acquire a write lock without blocking.  This can
 |         /* Try to acquire a write lock without blocking.  This can
 | ||||||
|            only succeed if the owning process has died.  In that case |            only succeed if the owning process has died.  In that case | ||||||
|            we don't care about its temporary roots. */ |            we don't care about its temporary roots. */ | ||||||
|  | @ -209,6 +244,7 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds) | ||||||
|             writeFull(*fd, (const unsigned char *) "d", 1); |             writeFull(*fd, (const unsigned char *) "d", 1); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|         /* Acquire a read lock.  This will prevent the owning process
 |         /* Acquire a read lock.  This will prevent the owning process
 | ||||||
|            from upgrading to a write lock, therefore it will block in |            from upgrading to a write lock, therefore it will block in | ||||||
|  | @ -448,28 +484,24 @@ void collectGarbage(GCAction action, const PathSet & pathsToDelete, | ||||||
|         debug(format("dead path `%1%'") % *i); |         debug(format("dead path `%1%'") % *i); | ||||||
|         result.insert(*i); |         result.insert(*i); | ||||||
| 
 | 
 | ||||||
|         AutoCloseFD fdLock; |  | ||||||
| 
 |  | ||||||
|         if (action == gcDeleteDead || action == gcDeleteSpecific) { |         if (action == gcDeleteDead || action == gcDeleteSpecific) { | ||||||
| 
 | 
 | ||||||
|  | #ifndef __CYGWIN__ | ||||||
|  |             AutoCloseFD fdLock; | ||||||
|  | 
 | ||||||
|             /* Only delete a lock file if we can acquire a write lock
 |             /* Only delete a lock file if we can acquire a write lock
 | ||||||
|                on it.  That means that it's either stale, or the |                on it.  That means that it's either stale, or the | ||||||
|                process that created it hasn't locked it yet.  In the |                process that created it hasn't locked it yet.  In the | ||||||
|                latter case the other process will detect that we |                latter case the other process will detect that we | ||||||
|                deleted the lock, and retry (see pathlocks.cc). */ |                deleted the lock, and retry (see pathlocks.cc). */ | ||||||
|             if (i->size() >= 5 && string(*i, i->size() - 5) == ".lock") { |             if (i->size() >= 5 && string(*i, i->size() - 5) == ".lock") { | ||||||
| 
 |                 fdLock = openLockFile(*i, false); | ||||||
|                 fdLock = open(i->c_str(), O_RDWR); |                 if (fdLock != -1 && !lockFile(fdLock, ltWrite, false)) { | ||||||
|                 if (fdLock == -1) { |  | ||||||
|                     if (errno == ENOENT) continue; |  | ||||||
|                     throw SysError(format("opening lock file `%1%'") % *i); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if (!lockFile(fdLock, ltWrite, false)) { |  | ||||||
|                     debug(format("skipping active lock `%1%'") % *i); |                     debug(format("skipping active lock `%1%'") % *i); | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|             printMsg(lvlInfo, format("deleting `%1%'") % *i); |             printMsg(lvlInfo, format("deleting `%1%'") % *i); | ||||||
|              |              | ||||||
|  | @ -478,9 +510,11 @@ void collectGarbage(GCAction action, const PathSet & pathsToDelete, | ||||||
|             deleteFromStore(*i, freed); |             deleteFromStore(*i, freed); | ||||||
|             bytesFreed += freed; |             bytesFreed += freed; | ||||||
| 
 | 
 | ||||||
|  | #ifndef __CYGWIN__ | ||||||
|             if (fdLock != -1) |             if (fdLock != -1) | ||||||
|                 /* Write token to stale (deleted) lock file. */ |                 /* Write token to stale (deleted) lock file. */ | ||||||
|                 writeFull(fdLock, (const unsigned char *) "d", 1); |                 writeFull(fdLock, (const unsigned char *) "d", 1); | ||||||
|  | #endif | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,6 +12,79 @@ | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | int openLockFile(const Path & path, bool create) | ||||||
|  | { | ||||||
|  |     AutoCloseFD fd; | ||||||
|  | 
 | ||||||
|  | #ifdef __CYGWIN__ | ||||||
|  |     /* On Cygwin we have to open the lock file without "DELETE"
 | ||||||
|  |        sharing mode; otherwise Windows will allow open lock files to | ||||||
|  |        be deleted (which is almost but not quite what Unix does). */ | ||||||
|  |     char win32Path[MAX_PATH + 1]; | ||||||
|  |     cygwin_conv_to_full_win32_path(path.c_str(), win32Path); | ||||||
|  | 
 | ||||||
|  |     SECURITY_ATTRIBUTES sa; /* required, otherwise inexplicably bad shit happens */ | ||||||
|  |     sa.nLength = sizeof sa; | ||||||
|  |     sa.lpSecurityDescriptor = 0; | ||||||
|  |     sa.bInheritHandle = TRUE; | ||||||
|  |     HANDLE h = CreateFile(win32Path, GENERIC_READ | GENERIC_WRITE, | ||||||
|  |         FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,  | ||||||
|  |         (create ? OPEN_ALWAYS : OPEN_EXISTING),  | ||||||
|  |         FILE_ATTRIBUTE_NORMAL, 0); | ||||||
|  |     if (h == INVALID_HANDLE_VALUE) { | ||||||
|  |         if (create || GetLastError() != ERROR_FILE_NOT_FOUND) | ||||||
|  |             throw Error(format("opening lock file `%1%'") % path); | ||||||
|  |         fd = -1; | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |         fd = cygwin_attach_handle_to_fd((char *) path.c_str(), -1, h, 1, O_RDWR); | ||||||
|  | #else         | ||||||
|  |     fd = open(path.c_str(), O_RDWR | (create ? O_CREAT : 0), 0666); | ||||||
|  |     if (fd == -1 && (create || errno != ENOENT)) | ||||||
|  |         throw SysError(format("opening lock file `%1%'") % path); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     return fd.borrow(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void deleteLockFilePreClose(const Path & path, int fd) | ||||||
|  | { | ||||||
|  | #ifndef __CYGWIN__ | ||||||
|  |     /* Get rid of the lock file.  Have to be careful not to introduce
 | ||||||
|  |        races. */ | ||||||
|  |     /* On Unix, write a (meaningless) token to the file to indicate to
 | ||||||
|  |        other processes waiting on this lock that the lock is stale | ||||||
|  |        (deleted). */ | ||||||
|  |     unlink(path.c_str()); | ||||||
|  |     writeFull(fd, (const unsigned char *) "d", 1); | ||||||
|  |     /* Note that the result of unlink() is ignored; removing the lock
 | ||||||
|  |        file is an optimisation, not a necessity. */ | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void deleteLockFilePostClose(const Path & path) | ||||||
|  | { | ||||||
|  | #ifdef __CYGWIN__ | ||||||
|  |     /* On Windows, just try to delete the lock file.  This will fail
 | ||||||
|  |        if anybody still has the file open.  We cannot use unlink() | ||||||
|  |        here, because Cygwin emulates Unix semantics of allowing an | ||||||
|  |        open file to be deleted (but fakes it - the file isn't actually | ||||||
|  |        deleted until later, so a file with the same name cannot be | ||||||
|  |        created in the meantime). */ | ||||||
|  |     char win32Path[MAX_PATH + 1]; | ||||||
|  |     cygwin_conv_to_full_win32_path(path.c_str(), win32Path); | ||||||
|  |     if (DeleteFile(win32Path)) | ||||||
|  |         debug(format("delete of `%1%' succeeded") % path.c_str()); | ||||||
|  |     else | ||||||
|  |         /* Not an error: probably means that the lock is still opened
 | ||||||
|  |            by someone else. */ | ||||||
|  |         debug(format("delete of `%1%' failed: %2%") % path.c_str() % GetLastError()); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| bool lockFile(int fd, LockType lockType, bool wait) | bool lockFile(int fd, LockType lockType, bool wait) | ||||||
| { | { | ||||||
|     struct flock lock; |     struct flock lock; | ||||||
|  | @ -94,26 +167,7 @@ void PathLocks::lockPaths(const PathSet & _paths, const string & waitMsg) | ||||||
|         while (1) { |         while (1) { | ||||||
| 
 | 
 | ||||||
|             /* Open/create the lock file. */ |             /* Open/create the lock file. */ | ||||||
| #ifdef __CYGWIN__ | 	    fd = openLockFile(lockPath, true); | ||||||
| 	    char win32Path[MAX_PATH]; |  | ||||||
| 	    cygwin_conv_to_full_win32_path(lockPath.c_str(), win32Path); |  | ||||||
| 
 |  | ||||||
| 	    SECURITY_ATTRIBUTES sa; |  | ||||||
| 	    sa.nLength = sizeof sa; |  | ||||||
| 	    sa.lpSecurityDescriptor = 0; |  | ||||||
| 	    sa.bInheritHandle = TRUE; |  | ||||||
| 	    HANDLE h = CreateFile(win32Path, GENERIC_READ,  |  | ||||||
| 	        FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_ALWAYS,  |  | ||||||
|                 FILE_ATTRIBUTE_NORMAL, 0); |  | ||||||
| 	    if (h == INVALID_HANDLE_VALUE) |  | ||||||
| 		throw Error(format("opening lock file `%1%'") % lockPath); |  | ||||||
| 
 |  | ||||||
| 	    fd = cygwin_attach_handle_to_fd((char *) lockPath.c_str(), -1, h, 1, O_RDWR); |  | ||||||
| #else         |  | ||||||
|             fd = open(lockPath.c_str(), O_WRONLY | O_CREAT, 0666); |  | ||||||
|             if (fd == -1) |  | ||||||
|                 throw SysError(format("opening lock file `%1%'") % lockPath); |  | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
|             /* Acquire an exclusive lock. */ |             /* Acquire an exclusive lock. */ | ||||||
|             if (!lockFile(fd, ltWrite, false)) { |             if (!lockFile(fd, ltWrite, false)) { | ||||||
|  | @ -148,40 +202,15 @@ void PathLocks::lockPaths(const PathSet & _paths, const string & waitMsg) | ||||||
| PathLocks::~PathLocks() | PathLocks::~PathLocks() | ||||||
| { | { | ||||||
|     for (list<FDPair>::iterator i = fds.begin(); i != fds.end(); i++) { |     for (list<FDPair>::iterator i = fds.begin(); i != fds.end(); i++) { | ||||||
| #ifndef __CYGWIN__ |         if (deletePaths) deleteLockFilePreClose(i->second, i->first); | ||||||
|         if (deletePaths) { | 
 | ||||||
| 	    /* Get rid of the lock file.  Have to be careful not to
 |  | ||||||
| 	       introduce races. */ |  | ||||||
|             /* On Unix, write a (meaningless) token to the file to
 |  | ||||||
|                indicate to other processes waiting on this lock that |  | ||||||
|                the lock is stale (deleted). */ |  | ||||||
|             unlink(i->second.c_str()); |  | ||||||
|             writeFull(i->first, (const unsigned char *) "d", 1); |  | ||||||
|             /* Note that the result of unlink() is ignored; removing
 |  | ||||||
|                the lock file is an optimisation, not a necessity. */ |  | ||||||
|         } |  | ||||||
| #endif |  | ||||||
|         lockedPaths.erase(i->second); |         lockedPaths.erase(i->second); | ||||||
|         if (close(i->first) == -1) |         if (close(i->first) == -1) | ||||||
|             printMsg(lvlError, |             printMsg(lvlError, | ||||||
|                 format("error (ignored): cannot close lock file on `%1%'") % i->second); |                 format("error (ignored): cannot close lock file on `%1%'") % i->second); | ||||||
| #ifdef __CYGWIN__ | 
 | ||||||
| 	if (deletePaths) { | 	if (deletePaths) deleteLockFilePostClose(i->second); | ||||||
| 	    /* On Windows, just try to delete the lock file.  This
 | 
 | ||||||
| 	       will fail if anybody still has the file open.  We |  | ||||||
| 	       cannot use unlink() here, because Cygwin emulates Unix |  | ||||||
| 	       semantics of allowing an open file to be deleted (but |  | ||||||
| 	       fakes it - the file isn't actually deleted until later, |  | ||||||
| 	       so a file with the same name cannot be created in the |  | ||||||
| 	       meantime). */ |  | ||||||
| 	    char win32Path[MAX_PATH]; |  | ||||||
| 	    cygwin_conv_to_full_win32_path(i->second.c_str(), win32Path); |  | ||||||
| 	    if (DeleteFile(win32Path)) |  | ||||||
| 		debug(format("delete of `%1%' succeeded") % i->second.c_str()); |  | ||||||
| 	    else |  | ||||||
| 		debug(format("delete of `%1%' failed: %2%") % i->second.c_str() % GetLastError()); |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
|         debug(format("lock released on `%1%'") % i->second); |         debug(format("lock released on `%1%'") % i->second); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4,6 +4,16 @@ | ||||||
| #include "util.hh" | #include "util.hh" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /* Open (possibly create) a lock file and return the file descriptor.
 | ||||||
|  |    -1 is returned if create is false and the lock could not be opened | ||||||
|  |    because it doesn't exist.  Any other error throws an exception. */ | ||||||
|  | int openLockFile(const Path & path, bool create); | ||||||
|  | 
 | ||||||
|  | /* Delete an open lock file.  Both must be called to be fully portable
 | ||||||
|  |    between Unix and Windows. */ | ||||||
|  | void deleteLockFilePreClose(const Path & path, int fd); | ||||||
|  | void deleteLockFilePostClose(const Path & path); | ||||||
|  | 
 | ||||||
| typedef enum LockType { ltRead, ltWrite, ltNone }; | typedef enum LockType { ltRead, ltWrite, ltNone }; | ||||||
| 
 | 
 | ||||||
| bool lockFile(int fd, LockType lockType, bool wait); | bool lockFile(int fd, LockType lockType, bool wait); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue