No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 

79 líneas
2.4 KiB

  1. # -*- coding: utf-8 -*-
  2. from __future__ import (absolute_import, division, print_function,
  3. unicode_literals)
  4. import signal
  5. class BaseTimeoutException(Exception):
  6. """Base exception for timeouts."""
  7. pass
  8. class JobTimeoutException(BaseTimeoutException):
  9. """Raised when a job takes longer to complete than the allowed maximum
  10. timeout value.
  11. """
  12. pass
  13. class HorseMonitorTimeoutException(BaseTimeoutException):
  14. """Raised when waiting for a horse exiting takes longer than the maximum
  15. timeout value.
  16. """
  17. pass
  18. class BaseDeathPenalty(object):
  19. """Base class to setup job timeouts."""
  20. def __init__(self, timeout, exception=JobTimeoutException, **kwargs):
  21. self._timeout = timeout
  22. self._exception = exception
  23. def __enter__(self):
  24. self.setup_death_penalty()
  25. def __exit__(self, type, value, traceback):
  26. # Always cancel immediately, since we're done
  27. try:
  28. self.cancel_death_penalty()
  29. except BaseTimeoutException:
  30. # Weird case: we're done with the with body, but now the alarm is
  31. # fired. We may safely ignore this situation and consider the
  32. # body done.
  33. pass
  34. # __exit__ may return True to supress further exception handling. We
  35. # don't want to suppress any exceptions here, since all errors should
  36. # just pass through, BaseTimeoutException being handled normally to the
  37. # invoking context.
  38. return False
  39. def setup_death_penalty(self):
  40. raise NotImplementedError()
  41. def cancel_death_penalty(self):
  42. raise NotImplementedError()
  43. class UnixSignalDeathPenalty(BaseDeathPenalty):
  44. def handle_death_penalty(self, signum, frame):
  45. raise self._exception('Task exceeded maximum timeout value '
  46. '({0} seconds)'.format(self._timeout))
  47. def setup_death_penalty(self):
  48. """Sets up an alarm signal and a signal handler that raises
  49. an exception after the timeout amount (expressed in seconds).
  50. """
  51. signal.signal(signal.SIGALRM, self.handle_death_penalty)
  52. signal.alarm(self._timeout)
  53. def cancel_death_penalty(self):
  54. """Removes the death penalty alarm and puts back the system into
  55. default signal handling.
  56. """
  57. signal.alarm(0)
  58. signal.signal(signal.SIGALRM, signal.SIG_DFL)