123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- # -*- coding: utf-8 -*-
- """
- This module contains utilities added by billiard, to keep
- "non-core" functionality out of ``.util``."""
- from __future__ import absolute_import
-
- import os
- import signal
- import sys
-
- import pickle as pypickle
- try:
- import cPickle as cpickle
- except ImportError: # pragma: no cover
- cpickle = None # noqa
-
- from .exceptions import RestartFreqExceeded
- from .five import monotonic
-
- if sys.version_info < (2, 6): # pragma: no cover
- # cPickle does not use absolute_imports
- pickle = pypickle
- pickle_load = pypickle.load
- pickle_loads = pypickle.loads
- else:
- pickle = cpickle or pypickle
- pickle_load = pickle.load
- pickle_loads = pickle.loads
-
- # cPickle.loads does not support buffer() objects,
- # but we can just create a StringIO and use load.
- if sys.version_info[0] == 3:
- from io import BytesIO
- else:
- try:
- from cStringIO import StringIO as BytesIO # noqa
- except ImportError:
- from StringIO import StringIO as BytesIO # noqa
-
- EX_SOFTWARE = 70
-
- TERMSIGS_DEFAULT = (
- 'SIGHUP',
- 'SIGQUIT',
- 'SIGTERM',
- 'SIGUSR1',
- 'SIGUSR2'
- )
-
- TERMSIGS_FULL = (
- 'SIGHUP',
- 'SIGQUIT',
- 'SIGTRAP',
- 'SIGABRT',
- 'SIGEMT',
- 'SIGSYS',
- 'SIGPIPE',
- 'SIGALRM',
- 'SIGTERM',
- 'SIGXCPU',
- 'SIGXFSZ',
- 'SIGVTALRM',
- 'SIGPROF',
- 'SIGUSR1',
- 'SIGUSR2',
- )
-
- #: set by signal handlers just before calling exit.
- #: if this is true after the sighandler returns it means that something
- #: went wrong while terminating the process, and :func:`os._exit`
- #: must be called ASAP.
- _should_have_exited = [False]
-
-
- def pickle_loads(s, load=pickle_load):
- # used to support buffer objects
- return load(BytesIO(s))
-
-
- def maybe_setsignal(signum, handler):
- try:
- signal.signal(signum, handler)
- except (OSError, AttributeError, ValueError, RuntimeError):
- pass
-
-
- def _shutdown_cleanup(signum, frame):
- # we will exit here so if the signal is received a second time
- # we can be sure that something is very wrong and we may be in
- # a crashing loop.
- if _should_have_exited[0]:
- os._exit(EX_SOFTWARE)
- maybe_setsignal(signum, signal.SIG_DFL)
- _should_have_exited[0] = True
- sys.exit(-(256 - signum))
-
-
- def reset_signals(handler=_shutdown_cleanup, full=False):
- for sig in TERMSIGS_FULL if full else TERMSIGS_DEFAULT:
- try:
- signum = getattr(signal, sig)
- except AttributeError:
- pass
- else:
- current = signal.getsignal(signum)
- if current is not None and current != signal.SIG_IGN:
- maybe_setsignal(signum, handler)
-
-
- class restart_state(object):
- RestartFreqExceeded = RestartFreqExceeded
-
- def __init__(self, maxR, maxT):
- self.maxR, self.maxT = maxR, maxT
- self.R, self.T = 0, None
-
- def step(self, now=None):
- now = monotonic() if now is None else now
- R = self.R
- if self.T and now - self.T >= self.maxT:
- # maxT passed, reset counter and time passed.
- self.T, self.R = now, 0
- elif self.maxR and self.R >= self.maxR:
- # verify that R has a value as the result handler
- # resets this when a job is accepted. If a job is accepted
- # the startup probably went fine (startup restart burst
- # protection)
- if self.R: # pragma: no cover
- self.R = 0 # reset in case someone catches the error
- raise self.RestartFreqExceeded("%r in %rs" % (R, self.maxT))
- # first run sets T
- if self.T is None:
- self.T = now
- self.R += 1
|