Revision 76e2f08a lib/locking.py

b/lib/locking.py
782 782
      start = time.time()
783 783
      calc_remaining_timeout = lambda: (start + timeout) - time.time()
784 784

  
785
    want_all = names is None
786

  
787
    if want_all:
788
      # If no names are given acquire the whole set by not letting new names
789
      # being added before we release, and getting the current list of names.
790
      # Some of them may then be deleted later, but we'll cope with this.
791
      #
792
      # We'd like to acquire this lock in a shared way, as it's nice if
793
      # everybody else can use the instances at the same time. If are acquiring
794
      # them exclusively though they won't be able to do this anyway, though,
795
      # so we'll get the list lock exclusively as well in order to be able to
796
      # do add() on the set while owning it.
797
      self.__lock.acquire(shared=shared, timeout=remaining_timeout)
798
      try:
799
        # note we own the set-lock
800
        self._add_owned()
801
        names = self.__names()
802
      except:
803
        # We shouldn't have problems adding the lock to the owners list, but
804
        # if we did we'll try to release this lock and re-raise exception.
805
        # Of course something is going to be really wrong, after this.
806
        self.__lock.release()
807
        raise
808

  
809
      # Re-calculate timeout
810
      remaining_timeout = calc_remaining_timeout()
811

  
812 785
    try:
813
      try:
786
      if names is not None:
814 787
        # Support passing in a single resource to acquire rather than many
815 788
        if isinstance(names, basestring):
816 789
          names = [names]
817 790
        else:
818 791
          names = sorted(names)
819 792

  
820
        acquire_list = []
821
        # First we look the locks up on __lockdict. We have no way of being sure
822
        # they will still be there after, but this makes it a lot faster should
823
        # just one of them be the already wrong
824
        for lname in utils.UniqueSequence(names):
825
          try:
826
            lock = self.__lockdict[lname] # raises KeyError if lock is not there
827
            acquire_list.append((lname, lock))
828
          except KeyError:
829
            if want_all:
830
              # We are acquiring all the set, it doesn't matter if this
831
              # particular element is not there anymore.
832
              continue
833
            else:
834
              raise errors.LockError("Non-existing lock in set (%s)" % lname)
835

  
836
        # This will hold the locknames we effectively acquired.
837
        acquired = set()
838

  
839
        # Now acquire_list contains a sorted list of resources and locks we
840
        # want.  In order to get them we loop on this (private) list and
841
        # acquire() them.  We gave no real guarantee they will still exist till
842
        # this is done but .acquire() itself is safe and will alert us if the
843
        # lock gets deleted.
844
        for (lname, lock) in acquire_list:
845
          if __debug__ and callable(test_notify):
846
            test_notify_fn = lambda: test_notify(lname)
847
          else:
848
            test_notify_fn = None
793
        return self.__acquire_inner(names, False, shared,
794
                                    calc_remaining_timeout, test_notify)
849 795

  
850
          try:
851
            if timeout is not None and remaining_timeout < 0:
852
              raise _AcquireTimeout()
853

  
854
            # raises LockError if the lock was deleted
855
            if not lock.acquire(shared=shared, timeout=remaining_timeout,
856
                                test_notify=test_notify_fn):
857
              # Couldn't get lock or timeout occurred
858
              if timeout is None:
859
                # This shouldn't happen as SharedLock.acquire(timeout=None) is
860
                # blocking.
861
                raise errors.LockError("Failed to get lock %s" % lname)
862

  
863
              raise _AcquireTimeout()
864

  
865
            # Re-calculate timeout
866
            remaining_timeout = calc_remaining_timeout()
867

  
868
            # now the lock cannot be deleted, we have it!
869
            self._add_owned(name=lname)
870
            acquired.add(lname)
871

  
872
          except _AcquireTimeout:
873
            # Release all acquired locks
874
            self._release_and_delete_owned()
875
            raise
876

  
877
          except errors.LockError:
878
            if want_all:
879
              # We are acquiring all the set, it doesn't matter if this
880
              # particular element is not there anymore.
881
              continue
796
      else:
797
        # If no names are given acquire the whole set by not letting new names
798
        # being added before we release, and getting the current list of names.
799
        # Some of them may then be deleted later, but we'll cope with this.
800
        #
801
        # We'd like to acquire this lock in a shared way, as it's nice if
802
        # everybody else can use the instances at the same time. If are
803
        # acquiring them exclusively though they won't be able to do this
804
        # anyway, though, so we'll get the list lock exclusively as well in
805
        # order to be able to do add() on the set while owning it.
806
        if not self.__lock.acquire(shared=shared,
807
                                   timeout=calc_remaining_timeout()):
808
          raise _AcquireTimeout()
809
        try:
810
          # note we own the set-lock
811
          self._add_owned()
812

  
813
          return self.__acquire_inner(self.__names(), True, shared,
814
                                      calc_remaining_timeout, test_notify)
815
        except:
816
          # We shouldn't have problems adding the lock to the owners list, but
817
          # if we did we'll try to release this lock and re-raise exception.
818
          # Of course something is going to be really wrong, after this.
819
          self.__lock.release()
820
          self._del_owned()
821
          raise
882 822

  
883
            self._release_and_delete_owned()
823
    except _AcquireTimeout:
824
      return None
884 825

  
885
            raise errors.LockError("Non-existing lock in set (%s)" % lname)
826
  def __acquire_inner(self, names, want_all, shared, timeout_fn, test_notify):
827
    """
886 828

  
887
          except:
888
            # We shouldn't have problems adding the lock to the owners list, but
889
            # if we did we'll try to release this lock and re-raise exception.
890
            # Of course something is going to be really wrong, after this.
891
            if lock._is_owned():
892
              lock.release()
893
            raise
829
    """
830
    acquire_list = []
894 831

  
895
      except:
896
        # If something went wrong and we had the set-lock let's release it...
832
    # First we look the locks up on __lockdict. We have no way of being sure
833
    # they will still be there after, but this makes it a lot faster should
834
    # just one of them be the already wrong
835
    for lname in utils.UniqueSequence(names):
836
      try:
837
        lock = self.__lockdict[lname] # raises KeyError if lock is not there
838
        acquire_list.append((lname, lock))
839
      except KeyError:
897 840
        if want_all:
898
          self.__lock.release()
899
        raise
841
          # We are acquiring all the set, it doesn't matter if this particular
842
          # element is not there anymore.
843
          continue
900 844

  
901
    except _AcquireTimeout:
902
      if want_all:
903
        self._del_owned()
845
        raise errors.LockError("Non-existing lock in set (%s)" % lname)
904 846

  
905
      return None
847
    # This will hold the locknames we effectively acquired.
848
    acquired = set()
849

  
850
    try:
851
      # Now acquire_list contains a sorted list of resources and locks we
852
      # want.  In order to get them we loop on this (private) list and
853
      # acquire() them.  We gave no real guarantee they will still exist till
854
      # this is done but .acquire() itself is safe and will alert us if the
855
      # lock gets deleted.
856
      for (lname, lock) in acquire_list:
857
        if __debug__ and callable(test_notify):
858
          test_notify_fn = lambda: test_notify(lname)
859
        else:
860
          test_notify_fn = None
861

  
862
        timeout = timeout_fn()
863
        if timeout is not None and timeout < 0:
864
          raise _AcquireTimeout()
865

  
866
        try:
867
          # raises LockError if the lock was deleted
868
          acq_success = lock.acquire(shared=shared, timeout=timeout,
869
                                     test_notify=test_notify_fn)
870
        except errors.LockError:
871
          if want_all:
872
            # We are acquiring all the set, it doesn't matter if this
873
            # particular element is not there anymore.
874
            continue
875

  
876
          raise errors.LockError("Non-existing lock in set (%s)" % lname)
877

  
878
        if not acq_success:
879
          # Couldn't get lock or timeout occurred
880
          if timeout is None:
881
            # This shouldn't happen as SharedLock.acquire(timeout=None) is
882
            # blocking.
883
            raise errors.LockError("Failed to get lock %s" % lname)
884

  
885
          raise _AcquireTimeout()
886

  
887
        try:
888
          # now the lock cannot be deleted, we have it!
889
          self._add_owned(name=lname)
890
          acquired.add(lname)
891

  
892
        except:
893
          # We shouldn't have problems adding the lock to the owners list, but
894
          # if we did we'll try to release this lock and re-raise exception.
895
          # Of course something is going to be really wrong after this.
896
          if lock._is_owned():
897
            lock.release()
898
          raise
899

  
900
    except:
901
      # Release all owned locks
902
      self._release_and_delete_owned()
903
      raise
906 904

  
907 905
    return acquired
908 906

  

Also available in: Unified diff