linklockfile.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. from __future__ import absolute_import
  2. import time
  3. import os
  4. from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout,
  5. AlreadyLocked)
  6. class LinkLockFile(LockBase):
  7. """Lock access to a file using atomic property of link(2).
  8. >>> lock = LinkLockFile('somefile')
  9. >>> lock = LinkLockFile('somefile', threaded=False)
  10. """
  11. def acquire(self, timeout=None):
  12. try:
  13. open(self.unique_name, "wb").close()
  14. except IOError:
  15. raise LockFailed("failed to create %s" % self.unique_name)
  16. timeout = timeout if timeout is not None else self.timeout
  17. end_time = time.time()
  18. if timeout is not None and timeout > 0:
  19. end_time += timeout
  20. while True:
  21. # Try and create a hard link to it.
  22. try:
  23. os.link(self.unique_name, self.lock_file)
  24. except OSError:
  25. # Link creation failed. Maybe we've double-locked?
  26. nlinks = os.stat(self.unique_name).st_nlink
  27. if nlinks == 2:
  28. # The original link plus the one I created == 2. We're
  29. # good to go.
  30. return
  31. else:
  32. # Otherwise the lock creation failed.
  33. if timeout is not None and time.time() > end_time:
  34. os.unlink(self.unique_name)
  35. if timeout > 0:
  36. raise LockTimeout("Timeout waiting to acquire"
  37. " lock for %s" %
  38. self.path)
  39. else:
  40. raise AlreadyLocked("%s is already locked" %
  41. self.path)
  42. time.sleep(timeout is not None and timeout / 10 or 0.1)
  43. else:
  44. # Link creation succeeded. We're good to go.
  45. return
  46. def release(self):
  47. if not self.is_locked():
  48. raise NotLocked("%s is not locked" % self.path)
  49. elif not os.path.exists(self.unique_name):
  50. raise NotMyLock("%s is locked, but not by me" % self.path)
  51. os.unlink(self.unique_name)
  52. os.unlink(self.lock_file)
  53. def is_locked(self):
  54. return os.path.exists(self.lock_file)
  55. def i_am_locking(self):
  56. return (self.is_locked() and
  57. os.path.exists(self.unique_name) and
  58. os.stat(self.unique_name).st_nlink == 2)
  59. def break_lock(self):
  60. if os.path.exists(self.lock_file):
  61. os.unlink(self.lock_file)