LTP GCOV extension - code coverage report
Current view: directory - src/libstore - pathlocks.cc
Test: app.info
Date: 2006-10-11 Instrumented lines: 76
Code covered: 82.9 % Executed lines: 63

       1                 : #include "pathlocks.hh"
       2                 : #include "util.hh"
       3                 : 
       4                 : #include <cerrno>
       5                 : 
       6                 : #include <sys/types.h>
       7                 : #include <sys/stat.h>
       8                 : #include <fcntl.h>
       9                 : 
      10                 : #ifdef __CYGWIN__
      11                 : #include <windows.h>
      12                 : #include <sys/cygwin.h>
      13                 : #endif
      14                 : 
      15                 : 
      16                 : namespace nix {
      17                 : 
      18                 : 
      19                 : int openLockFile(const Path & path, bool create)
      20             357 : {
      21             357 :     AutoCloseFD fd;
      22                 : 
      23                 : #ifdef __CYGWIN__
      24                 :     /* On Cygwin we have to open the lock file without "DELETE"
      25                 :        sharing mode; otherwise Windows will allow open lock files to
      26                 :        be deleted (which is almost but not quite what Unix does). */
      27                 :     char win32Path[MAX_PATH + 1];
      28                 :     cygwin_conv_to_full_win32_path(path.c_str(), win32Path);
      29                 : 
      30                 :     SECURITY_ATTRIBUTES sa; /* required, otherwise inexplicably bad shit happens */
      31                 :     sa.nLength = sizeof sa;
      32                 :     sa.lpSecurityDescriptor = 0;
      33                 :     sa.bInheritHandle = TRUE;
      34                 :     HANDLE h = CreateFile(win32Path, GENERIC_READ | GENERIC_WRITE,
      35                 :         FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, 
      36                 :         (create ? OPEN_ALWAYS : OPEN_EXISTING), 
      37                 :         FILE_ATTRIBUTE_NORMAL, 0);
      38                 :     if (h == INVALID_HANDLE_VALUE) {
      39                 :         if (create || GetLastError() != ERROR_FILE_NOT_FOUND)
      40                 :             throw Error(format("opening lock file `%1%'") % path);
      41                 :         fd = -1;
      42                 :     }
      43                 :     else
      44                 :         fd = cygwin_attach_handle_to_fd((char *) path.c_str(), -1, h, 1, O_RDWR);
      45                 : #else        
      46             357 :     fd = open(path.c_str(), O_RDWR | (create ? O_CREAT : 0), 0666);
      47             357 :     if (fd == -1 && (create || errno != ENOENT))
      48               0 :         throw SysError(format("opening lock file `%1%'") % path);
      49                 : #endif
      50                 : 
      51             357 :     return fd.borrow();
      52                 : }
      53                 : 
      54                 : 
      55                 : void deleteLockFilePreClose(const Path & path, int fd)
      56             249 : {
      57                 : #ifndef __CYGWIN__
      58                 :     /* Get rid of the lock file.  Have to be careful not to introduce
      59                 :        races. */
      60                 :     /* On Unix, write a (meaningless) token to the file to indicate to
      61                 :        other processes waiting on this lock that the lock is stale
      62                 :        (deleted). */
      63             249 :     unlink(path.c_str());
      64             249 :     writeFull(fd, (const unsigned char *) "d", 1);
      65                 :     /* Note that the result of unlink() is ignored; removing the lock
      66                 :        file is an optimisation, not a necessity. */
      67                 : #endif
      68                 : }
      69                 : 
      70                 : 
      71                 : void deleteLockFilePostClose(const Path & path)
      72             249 : {
      73                 : #ifdef __CYGWIN__
      74                 :     /* On Windows, just try to delete the lock file.  This will fail
      75                 :        if anybody still has the file open.  We cannot use unlink()
      76                 :        here, because Cygwin emulates Unix semantics of allowing an
      77                 :        open file to be deleted (but fakes it - the file isn't actually
      78                 :        deleted until later, so a file with the same name cannot be
      79                 :        created in the meantime). */
      80                 :     char win32Path[MAX_PATH + 1];
      81                 :     cygwin_conv_to_full_win32_path(path.c_str(), win32Path);
      82                 :     if (DeleteFile(win32Path))
      83                 :         debug(format("delete of `%1%' succeeded") % path.c_str());
      84                 :     else
      85                 :         /* Not an error: probably means that the lock is still opened
      86                 :            by someone else. */
      87                 :         debug(format("delete of `%1%' failed: %2%") % path.c_str() % GetLastError());
      88                 : #endif
      89                 : }
      90                 : 
      91                 : 
      92                 : bool lockFile(int fd, LockType lockType, bool wait)
      93            1558 : {
      94            1558 :     struct flock lock;
      95            1558 :     if (lockType == ltRead) lock.l_type = F_RDLCK;
      96             854 :     else if (lockType == ltWrite) lock.l_type = F_WRLCK;
      97               0 :     else if (lockType == ltNone) lock.l_type = F_UNLCK;
      98               0 :     else abort();
      99            1558 :     lock.l_whence = SEEK_SET;
     100            1558 :     lock.l_start = 0;
     101            1558 :     lock.l_len = 0; /* entire file */
     102                 : 
     103            1558 :     if (wait) {
     104            1165 :         while (fcntl(fd, F_SETLKW, &lock) != 0) {
     105               0 :             checkInterrupt();
     106               0 :             if (errno != EINTR)
     107               0 :                 throw SysError(format("acquiring/releasing lock"));
     108                 :         }
     109                 :     } else {
     110             393 :         while (fcntl(fd, F_SETLK, &lock) != 0) {
     111              27 :             checkInterrupt();
     112              27 :             if (errno == EACCES || errno == EAGAIN) return false;
     113               0 :             if (errno != EINTR) 
     114               0 :                 throw SysError(format("acquiring/releasing lock"));
     115                 :         }
     116                 :     }
     117                 : 
     118            1531 :     return true;
     119                 : }
     120                 : 
     121                 : 
     122                 : /* This enables us to check whether are not already holding a lock on
     123                 :    a file ourselves.  POSIX locks (fcntl) suck in this respect: if we
     124                 :    close a descriptor, the previous lock will be closed as well.  And
     125                 :    there is no way to query whether we already have a lock (F_GETLK
     126                 :    only works on locks held by other processes). */
     127             345 : static StringSet lockedPaths; /* !!! not thread-safe */
     128                 : 
     129                 : 
     130                 : PathLocks::PathLocks()
     131             155 :     : deletePaths(false)
     132             725 : {
     133                 : }
     134                 : 
     135                 : 
     136                 : PathLocks::PathLocks(const PathSet & paths, const string & waitMsg)
     137             130 :     : deletePaths(false)
     138             130 : {
     139             130 :     lockPaths(paths, waitMsg);
     140                 : }
     141                 : 
     142                 : 
     143                 : void PathLocks::lockPaths(const PathSet & _paths, const string & waitMsg)
     144             253 : {
     145                 :     /* May be called only once! */
     146             253 :     assert(fds.empty());
     147                 :     
     148                 :     /* Note that `fds' is built incrementally so that the destructor
     149                 :        will only release those locks that we have already acquired. */
     150                 : 
     151                 :     /* Sort the paths.  This assures that locks are always acquired in
     152                 :        the same order, thus preventing deadlocks. */
     153             253 :     Paths paths(_paths.begin(), _paths.end());
     154             253 :     paths.sort();
     155                 :     
     156                 :     /* Acquire the lock for each path. */
     157             506 :     for (Paths::iterator i = paths.begin(); i != paths.end(); i++) {
     158             253 :         checkInterrupt();
     159             253 :         Path path = *i;
     160             253 :         Path lockPath = path + ".lock";
     161                 : 
     162             253 :         debug(format("locking path `%1%'") % path);
     163                 : 
     164             253 :         if (lockedPaths.find(lockPath) != lockedPaths.end()) {
     165               0 :             debug(format("already holding lock on `%1%'") % lockPath);
     166               0 :             continue;
     167                 :         }
     168                 : 
     169             253 :         AutoCloseFD fd;
     170                 :         
     171             277 :         while (1) {
     172                 : 
     173                 :             /* Open/create the lock file. */
     174             277 :             fd = openLockFile(lockPath, true);
     175                 : 
     176                 :             /* Acquire an exclusive lock. */
     177             277 :             if (!lockFile(fd, ltWrite, false)) {
     178              24 :                 if (waitMsg != "") printMsg(lvlError, waitMsg);
     179              24 :                 lockFile(fd, ltWrite, true);
     180                 :             }
     181                 : 
     182             277 :             debug(format("lock acquired on `%1%'") % lockPath);
     183                 : 
     184                 :             /* Check that the lock file hasn't become stale (i.e.,
     185                 :                hasn't been unlinked). */
     186             277 :             struct stat st;
     187             277 :             if (fstat(fd, &st) == -1)
     188               0 :                 throw SysError(format("statting lock file `%1%'") % lockPath);
     189             277 :             if (st.st_size != 0)
     190                 :                 /* This lock file has been unlinked, so we're holding
     191                 :                    a lock on a deleted file.  This means that other
     192                 :                    processes may create and acquire a lock on
     193                 :                    `lockPath', and proceed.  So we must retry. */
     194              24 :                 debug(format("open lock file `%1%' has become stale") % lockPath);
     195                 :             else
     196             253 :                 break;
     197                 :         }
     198                 : 
     199                 :         /* Use borrow so that the descriptor isn't closed. */
     200            1771 :         fds.push_back(FDPair(fd.borrow(), lockPath));
     201             253 :         lockedPaths.insert(lockPath);
     202                 :     }
     203                 : }
     204                 : 
     205                 : 
     206                 : PathLocks::~PathLocks()
     207             570 : {
     208             538 :     for (list<FDPair>::iterator i = fds.begin(); i != fds.end(); i++) {
     209             253 :         if (deletePaths) deleteLockFilePreClose(i->second, i->first);
     210                 : 
     211             253 :         lockedPaths.erase(i->second);
     212             253 :         if (close(i->first) == -1)
     213               0 :             printMsg(lvlError,
     214                 :                 format("error (ignored): cannot close lock file on `%1%'") % i->second);
     215                 : 
     216             253 :         if (deletePaths) deleteLockFilePostClose(i->second);
     217                 : 
     218             253 :         debug(format("lock released on `%1%'") % i->second);
     219                 :     }
     220                 : }
     221                 : 
     222                 : 
     223                 : void PathLocks::setDeletion(bool deletePaths)
     224             249 : {
     225             249 :     this->deletePaths = deletePaths;
     226                 : }
     227                 : 
     228                 :  
     229             345 : }

Generated by: LTP GCOV extension version 1.1