_threadsafety.py 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. from __future__ import division, print_function, absolute_import
  2. import threading
  3. import scipy._lib.decorator
  4. __all__ = ['ReentrancyError', 'ReentrancyLock', 'non_reentrant']
  5. class ReentrancyError(RuntimeError):
  6. pass
  7. class ReentrancyLock(object):
  8. """
  9. Threading lock that raises an exception for reentrant calls.
  10. Calls from different threads are serialized, and nested calls from the
  11. same thread result to an error.
  12. The object can be used as a context manager, or to decorate functions
  13. via the decorate() method.
  14. """
  15. def __init__(self, err_msg):
  16. self._rlock = threading.RLock()
  17. self._entered = False
  18. self._err_msg = err_msg
  19. def __enter__(self):
  20. self._rlock.acquire()
  21. if self._entered:
  22. self._rlock.release()
  23. raise ReentrancyError(self._err_msg)
  24. self._entered = True
  25. def __exit__(self, type, value, traceback):
  26. self._entered = False
  27. self._rlock.release()
  28. def decorate(self, func):
  29. def caller(func, *a, **kw):
  30. with self:
  31. return func(*a, **kw)
  32. return scipy._lib.decorator.decorate(func, caller)
  33. def non_reentrant(err_msg=None):
  34. """
  35. Decorate a function with a threading lock and prevent reentrant calls.
  36. """
  37. def decorator(func):
  38. msg = err_msg
  39. if msg is None:
  40. msg = "%s is not re-entrant" % func.__name__
  41. lock = ReentrancyLock(msg)
  42. return lock.decorate(func)
  43. return decorator