code: def try_lock_informatively(
      lock, purpose=None, timeouts=(5, 60, 240), proceed_unlocked=False
  ):
      """Try to acquire lock (while blocking) multiple times while logging INFO messages on failure
  
      Primary use case is for operations which are user-visible and thus should not lock
      indefinetely or for long period of times (so user would just Ctrl-C if no update is provided)
      without "feedback".
  
      Parameters
      ----------
      lock: fasteners._InterProcessLock
      purpose: str, optional
      timeouts: tuple or list, optional
      proceed_unlocked: bool, optional
      """
      purpose = " to " + str(purpose) if purpose else ""
  
      # could be bytes, making formatting trickier
      lock_path = ensure_unicode(lock.path)
  
      def get_pids_msg():
          try:
              pids = get_open_files(lock_path)
              if pids:
                  proc = pids[lock_path]
                  return f"Check following process: PID={proc.pid} CWD={proc.cwd()} CMDLINE={proc.cmdline()}."
              else:
                  return "Stale lock? I found no processes using it."
          except Exception as exc:
              lgr.debug(
                  "Failed to get a list of processes which 'posses' the file %s: %s",
                  lock_path,
                  CapturedException(exc),
              )
              return "Another process is using it (failed to determine one)?"
  
      lgr.debug(
          "Acquiring a currently %s lock%s. If stalls - check which process holds %s",
          ("existing" if lock.exists() else "absent"),
          purpose,
          lock_path,
      )
  
      was_locked = (
          False  # name of var the same as of within fasteners.try_lock
      )
      assert timeouts  # we expect non-empty timeouts
      try:
          for trial, timeout in enumerate(timeouts):
              was_locked = lock.acquire(blocking=True, timeout=timeout)
              if not was_locked:
                  if trial < len(timeouts) - 1:
                      msg = (
                          " Will try again and wait for up to %4g seconds."
                          % (timeouts[trial + 1],)
                      )
                  else:  # It was the last attempt
                      if proceed_unlocked:
                          msg = " Will proceed without locking."
                      else:
                          msg = ""
                  lgr.info(
                      "Failed to acquire lock%s at %s in %4g seconds. %s%s",
                      purpose,
                      lock_path,
                      timeout,
                      get_pids_msg(),
                      msg,
                  )
              else:
                  yield True
                  return
          else:
              assert not was_locked
              if proceed_unlocked:
                  yield False
              else:
                  raise RuntimeError(
                      "Failed to acquire lock%s at %s in %d attempts.%s"
                      % (
                          purpose,
                          lock_path,
                          len(timeouts),
                          get_pids_msg(),
                      )
                  )
      finally:
          if was_locked:
              lock.release()
  
file_path: datalad/datalad/support/locking.py
imports: from fasteners import InterProcessLock, try_lock
  from contextlib import contextmanager
  from .path import exists
  from ..utils import ensure_unicode, get_open_files, unlink
  from datalad.support.exceptions import CapturedException
  import logging
label_desc: Use image node_id 3 for calling a global function with example usage: try_lock_informatively(lock, purpose, timeouts, proceed_unlocked) and returns: str, proc, str, proc, str, proc, str, str, str