Пример #1
0
 def test_LockDir_acquired_success(self):
     # the LockDir.lock_acquired hook fires when a lock is acquired.
     LockDir.hooks.install_named_hook('lock_acquired',
                                      self.record_hook, 'record_hook')
     ld = self.get_lock()
     ld.create()
     self.assertEqual([], self._calls)
     result = ld.attempt_lock()
     lock_path = ld.transport.abspath(ld.path)
     self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
     ld.unlock()
     self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
Пример #2
0
    def force_break_corrupt(self, corrupt_info_lines):
        """Release a lock that has been corrupted.

        This is very similar to force_break, it except it doesn't assume that
        self.peek() can work.

        :param corrupt_info_lines: the lines of the corrupted info file, used
            to check that the lock hasn't changed between reading the (corrupt)
            info file and calling force_break_corrupt.
        """
        # XXX: this copes with unparseable info files, but what about missing
        # info files?  Or missing lock dirs?
        self._check_not_locked()
        tmpname = '%s/broken.%s.tmp' % (self.path, rand_chars(20))
        self.transport.rename(self._held_dir, tmpname)
        # check that we actually broke the right lock, not someone else;
        # there's a small race window between checking it and doing the
        # rename.
        broken_info_path = tmpname + self.__INFO_NAME
        broken_content = self.transport.get_bytes(broken_info_path)
        broken_lines = osutils.split_lines(broken_content)
        if broken_lines != corrupt_info_lines:
            raise LockBreakMismatch(self, broken_lines, corrupt_info_lines)
        self.transport.delete(broken_info_path)
        self.transport.rmdir(tmpname)
        result = lock.LockResult(self.transport.abspath(self.path))
        for hook in self.hooks['lock_broken']:
            hook(result)
Пример #3
0
 def test_LockDir_broken_success(self):
     # the LockDir.lock_broken hook fires when a lock is broken.
     ld = self.get_lock()
     ld.create()
     ld2 = self.get_lock()
     result = ld.attempt_lock()
     LockDir.hooks.install_named_hook('lock_broken',
                                      self.record_hook, 'record_hook')
     ld2.force_break(ld2.peek())
     lock_path = ld.transport.abspath(ld.path)
     self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
Пример #4
0
    def attempt_lock(self):
        """Take the lock; fail if it's already held.

        If you wish to block until the lock can be obtained, call wait_lock()
        instead.

        :return: The lock token.
        :raises LockContention: if the lock is held by someone else.
        """
        if self._fake_read_lock:
            raise LockContention(self)
        result = self._attempt_lock()
        hook_result = lock.LockResult(self.transport.abspath(self.path),
                                      self.nonce)
        for hook in self.hooks['lock_acquired']:
            hook(hook_result)
        return result
Пример #5
0
    def force_break(self, dead_holder_info):
        """Release a lock held by another process.

        WARNING: This should only be used when the other process is dead; if
        it still thinks it has the lock there will be two concurrent writers.
        In general the user's approval should be sought for lock breaks.

        After the lock is broken it will not be held by any process.
        It is possible that another process may sneak in and take the
        lock before the breaking process acquires it.

        :param dead_holder_info:
            Must be the result of a previous LockDir.peek() call; this is used
            to check that it's still held by the same process that the user
            decided was dead.  If this is not the current holder,
            LockBreakMismatch is raised.

        :returns: LockResult for the broken lock.
        """
        if not isinstance(dead_holder_info, LockHeldInfo):
            raise ValueError("dead_holder_info: %r" % dead_holder_info)
        self._check_not_locked()
        current_info = self.peek()
        if current_info is None:
            # must have been recently released
            return
        if current_info != dead_holder_info:
            raise LockBreakMismatch(self, current_info, dead_holder_info)
        tmpname = '%s/broken.%s.tmp' % (self.path, rand_chars(20))
        self.transport.rename(self._held_dir, tmpname)
        # check that we actually broke the right lock, not someone else;
        # there's a small race window between checking it and doing the
        # rename.
        broken_info_path = tmpname + self.__INFO_NAME
        broken_info = self._read_info_file(broken_info_path)
        if broken_info != dead_holder_info:
            raise LockBreakMismatch(self, broken_info, dead_holder_info)
        self.transport.delete(broken_info_path)
        self.transport.rmdir(tmpname)
        result = lock.LockResult(self.transport.abspath(self.path),
                                 current_info.get('nonce'))
        for hook in self.hooks['lock_broken']:
            hook(result)
        return result
Пример #6
0
 def unlock(self):
     """Release a held lock
     """
     if self._fake_read_lock:
         self._fake_read_lock = False
         return
     if not self._lock_held:
         return lock.cant_unlock_not_held(self)
     if self._locked_via_token:
         self._locked_via_token = False
         self._lock_held = False
     else:
         old_nonce = self.nonce
         # rename before deleting, because we can't atomically remove the
         # whole tree
         start_time = time.time()
         self._trace("unlocking")
         tmpname = '%s/releasing.%s.tmp' % (self.path, rand_chars(20))
         # gotta own it to unlock
         self.confirm()
         self.transport.rename(self._held_dir, tmpname)
         self._lock_held = False
         self.transport.delete(tmpname + self.__INFO_NAME)
         try:
             self.transport.rmdir(tmpname)
         except DirectoryNotEmpty, e:
             # There might have been junk left over by a rename that moved
             # another locker within the 'held' directory.  do a slower
             # deletion where we list the directory and remove everything
             # within it.
             #
             # Maybe this should be broader to allow for ftp servers with
             # non-specific error messages?
             self._trace(
                 "doing recursive deletion of non-empty directory "
                 "%s", tmpname)
             self.transport.delete_tree(tmpname)
         self._trace("... unlock succeeded after %dms",
                     (time.time() - start_time) * 1000)
         result = lock.LockResult(self.transport.abspath(self.path),
                                  old_nonce)
         for hook in self.hooks['lock_released']:
             hook(result)