utils.py 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. """
  2. Simple lock interface.
  3. """
  4. import os
  5. import time
  6. from contextlib import contextmanager
  7. from dataclasses import dataclass
  8. from pathlib import Path
  9. from fasteners import InterProcessLock
  10. from dataladmetadatamodel.log import logger
  11. PID = os.getpid()
  12. GIT_MAPPER_LOCK_FILE_NAME = "metadata-model-git.lock"
  13. read_write_locked = dict()
  14. @dataclass
  15. class LockState:
  16. counter: int
  17. lock: InterProcessLock
  18. def _get_lock_state(lock_dict: dict, realm: Path) -> LockState:
  19. if realm not in lock_dict:
  20. lock_dict[realm] = LockState(
  21. 0,
  22. InterProcessLock(str(realm / GIT_MAPPER_LOCK_FILE_NAME)))
  23. return lock_dict[realm]
  24. @contextmanager
  25. def locked_backend(realm: Path):
  26. """ lock and unlock the backend given by realm """
  27. lock_backend(realm)
  28. try:
  29. yield None
  30. finally:
  31. unlock_backend(realm)
  32. def lock_backend(realm: Path):
  33. lock_dir = get_lock_dir(realm)
  34. lock_state = _get_lock_state(read_write_locked, lock_dir)
  35. if lock_state.counter == 0:
  36. lock_time = time.time()
  37. lock_state.lock.acquire()
  38. lock_time = time.time() - lock_time
  39. logger.debug(
  40. "process {} locked git backend using file {} in {} seconds".format(
  41. PID,
  42. lock_dir,
  43. int(lock_time)))
  44. lock_state.counter += 1
  45. def unlock_backend(realm: Path):
  46. lock_dir = get_lock_dir(realm, create_directory=False)
  47. assert lock_dir.exists()
  48. lock_state = _get_lock_state(read_write_locked, lock_dir)
  49. assert lock_state.counter > 0
  50. lock_state.counter -= 1
  51. if lock_state.counter == 0:
  52. logger.debug(
  53. "process {} unlocks git backend using file {}".format(PID, lock_dir))
  54. lock_state.lock.release()
  55. def get_lock_dir(realm: Path, create_directory: bool = True) -> Path:
  56. lock_dir = realm / ".datalad" / "locks"
  57. if not lock_dir.exists() and create_directory:
  58. # if exist_ok is False, we might get an error if
  59. # a concurrent mapper tries to lock the same
  60. # dataset.
  61. os.makedirs(lock_dir, exist_ok=True)
  62. return lock_dir