def deferUntilLocked(self, timeout=None):
        """
        Wait until we acquire this lock.  This method is not safe for
        concurrent use.

        @type timeout: L{float} or L{int}
        @param timeout: the number of seconds after which to time out if the
            lock has not been acquired.

        @return: a L{Deferred} which will callback when the lock is acquired, or
            errback with a L{TimeoutError} after timing out or an
            L{AlreadyTryingToLockError} if the L{deferUntilLocked} has already
            been called and not successfully locked the file.
        """
        if self._tryLockCall is not None:
            return fail(
                AlreadyTryingToLockError(
                    "deferUntilLocked isn't safe for concurrent use."))

        def _cancelLock(reason):
            """
            Cancel a L{DeferredFilesystemLock.deferUntilLocked} call.

            @type reason: L{failure.Failure}
            @param reason: The reason why the call is cancelled.
            """
            self._tryLockCall.cancel()
            self._tryLockCall = None
            if self._timeoutCall is not None and self._timeoutCall.active():
                self._timeoutCall.cancel()
                self._timeoutCall = None

            if self.lock():
                d.callback(None)
            else:
                d.errback(reason)

        d = Deferred(lambda deferred: _cancelLock(CancelledError()))

        def _tryLock():
            if self.lock():
                if self._timeoutCall is not None:
                    self._timeoutCall.cancel()
                    self._timeoutCall = None

                self._tryLockCall = None

                d.callback(None)
            else:
                if timeout is not None and self._timeoutCall is None:
                    reason = failure.Failure(TimeoutError(
                        "Timed out acquiring lock: %s after %fs" % (
                            self.name,
                            timeout)))
                    self._timeoutCall = self._scheduler.callLater(
                        timeout, _cancelLock, reason)

                self._tryLockCall = self._scheduler.callLater(
                    self._interval, _tryLock)

        _tryLock()

        return d

        Wait until we acquire this lock.  This method is not safe for
        concurrent use.

        @type timeout: L{float} or L{int}
        @param timeout: the number of seconds after which to time out if the
            lock has not been acquired.

        @return: a L{Deferred} which will callback when the lock is acquired, or
            errback with a L{TimeoutError} after timing out or an
            L{AlreadyTryingToLockError} if the L{deferUntilLocked} has already
            been called and not successfully locked the file.
        
stackoverflow/venv/lib/python3.6/site-packages/twisted/internet/defer.py