PageRenderTime 36ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/module/thread/os_lock.py

https://bitbucket.org/pypy/pypy/
Python | 163 lines | 126 code | 13 blank | 24 comment | 16 complexity | 0c2a2c3355daa0aad3bcd02aa99ade05 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. """
  2. Python locks, based on true threading locks provided by the OS.
  3. """
  4. import time
  5. from rpython.rlib import rthread
  6. from pypy.module.thread.error import wrap_thread_error
  7. from pypy.interpreter.baseobjspace import W_Root
  8. from pypy.interpreter.gateway import interp2app, unwrap_spec
  9. from pypy.interpreter.typedef import TypeDef
  10. from pypy.interpreter.error import oefmt
  11. from rpython.rlib.rarithmetic import r_longlong, ovfcheck_float_to_longlong
  12. RPY_LOCK_FAILURE, RPY_LOCK_ACQUIRED, RPY_LOCK_INTR = range(3)
  13. def parse_acquire_args(space, blocking, timeout):
  14. if not blocking and timeout != -1.0:
  15. raise oefmt(space.w_ValueError,
  16. "can't specify a timeout for a non-blocking call")
  17. if timeout < 0.0 and timeout != -1.0:
  18. raise oefmt(space.w_ValueError,
  19. "timeout value must be strictly positive")
  20. if not blocking:
  21. microseconds = 0
  22. elif timeout == -1.0:
  23. microseconds = -1
  24. else:
  25. timeout *= 1e6
  26. try:
  27. microseconds = ovfcheck_float_to_longlong(timeout)
  28. except OverflowError:
  29. raise oefmt(space.w_OverflowError, "timeout value is too large")
  30. return microseconds
  31. def acquire_timed(space, lock, microseconds):
  32. """Helper to acquire an interruptible lock with a timeout."""
  33. endtime = (time.time() * 1e6) + microseconds
  34. while True:
  35. result = lock.acquire_timed(microseconds)
  36. if result == RPY_LOCK_INTR:
  37. # Run signal handlers if we were interrupted
  38. space.getexecutioncontext().checksignals()
  39. if microseconds >= 0:
  40. microseconds = r_longlong((endtime - (time.time() * 1e6))
  41. + 0.999)
  42. # Check for negative values, since those mean block
  43. # forever
  44. if microseconds <= 0:
  45. result = RPY_LOCK_FAILURE
  46. if result != RPY_LOCK_INTR:
  47. break
  48. return result
  49. class Lock(W_Root):
  50. "A box around an interp-level lock object."
  51. _immutable_fields_ = ["lock"]
  52. def __init__(self, space):
  53. self.space = space
  54. try:
  55. self.lock = rthread.allocate_lock()
  56. except rthread.error:
  57. raise wrap_thread_error(space, "out of resources")
  58. @unwrap_spec(blocking=int)
  59. def descr_lock_acquire(self, space, blocking=1):
  60. """Lock the lock. With the default argument of True, this blocks
  61. if the lock is already locked (even by the same thread), waiting for
  62. another thread to release the lock, and returns True once the lock is
  63. acquired. With an argument of False, this will always return immediately
  64. and the return value reflects whether the lock is acquired.
  65. The blocking operation is not interruptible."""
  66. mylock = self.lock
  67. result = mylock.acquire(bool(blocking))
  68. return space.newbool(result)
  69. @unwrap_spec(blocking=int, timeout=float)
  70. def descr_lock_py3k_acquire(self, space, blocking=1, timeout=-1.0):
  71. """(Backport of a Python 3 API for PyPy. This version takes
  72. a timeout argument and handles signals, like Ctrl-C.)
  73. Lock the lock. Without argument, this blocks if the lock is already
  74. locked (even by the same thread), waiting for another thread to release
  75. the lock, and return None once the lock is acquired.
  76. With an argument, this will only block if the argument is true,
  77. and the return value reflects whether the lock is acquired.
  78. The blocking operation is interruptible."""
  79. microseconds = parse_acquire_args(space, blocking, timeout)
  80. result = acquire_timed(space, self.lock, microseconds)
  81. return space.newbool(result == RPY_LOCK_ACQUIRED)
  82. def descr_lock_release(self, space):
  83. """Release the lock, allowing another thread that is blocked waiting for
  84. the lock to acquire the lock. The lock must be in the locked state,
  85. but it needn't be locked by the same thread that unlocks it."""
  86. try:
  87. self.lock.release()
  88. except rthread.error:
  89. raise wrap_thread_error(space, "release unlocked lock")
  90. def descr_lock_locked(self, space):
  91. """Return whether the lock is in the locked state."""
  92. if self.lock.acquire(False):
  93. self.lock.release()
  94. return space.w_False
  95. else:
  96. return space.w_True
  97. def descr__enter__(self, space):
  98. self.descr_lock_acquire(space)
  99. return self
  100. def descr__exit__(self, space, __args__):
  101. self.descr_lock_release(space)
  102. def __enter__(self):
  103. self.descr_lock_acquire(self.space)
  104. return self
  105. def __exit__(self, *args):
  106. self.descr_lock_release(self.space)
  107. descr_acquire = interp2app(Lock.descr_lock_acquire)
  108. descr_release = interp2app(Lock.descr_lock_release)
  109. descr_locked = interp2app(Lock.descr_lock_locked)
  110. descr__enter__ = interp2app(Lock.descr__enter__)
  111. descr__exit__ = interp2app(Lock.descr__exit__)
  112. descr_py3k_acquire = interp2app(Lock.descr_lock_py3k_acquire)
  113. Lock.typedef = TypeDef("thread.lock",
  114. __doc__ = """\
  115. A lock object is a synchronization primitive. To create a lock,
  116. call the thread.allocate_lock() function. Methods are:
  117. acquire() -- lock the lock, possibly blocking until it can be obtained
  118. release() -- unlock of the lock
  119. locked() -- test whether the lock is currently locked
  120. A lock is not owned by the thread that locked it; another thread may
  121. unlock it. A thread attempting to lock a lock that it has already locked
  122. will block until another thread unlocks it. Deadlocks may ensue.""",
  123. acquire = descr_acquire,
  124. _py3k_acquire = descr_py3k_acquire,
  125. release = descr_release,
  126. locked = descr_locked,
  127. __enter__ = descr__enter__,
  128. __exit__ = descr__exit__,
  129. # Obsolete synonyms
  130. acquire_lock = descr_acquire,
  131. release_lock = descr_release,
  132. locked_lock = descr_locked,
  133. )
  134. def allocate_lock(space):
  135. """Create a new lock object. (allocate() is an obsolete synonym.)
  136. See LockType.__doc__ for information about locks."""
  137. return space.wrap(Lock(space))