238 lines
6.1 KiB
Python
238 lines
6.1 KiB
Python
|
|
#
|
||
|
|
# Module providing various facilities to other parts of the package
|
||
|
|
#
|
||
|
|
# billiard/util.py
|
||
|
|
#
|
||
|
|
# Copyright (c) 2006-2008, R Oudkerk --- see COPYING.txt
|
||
|
|
# Licensed to PSF under a Contributor Agreement.
|
||
|
|
#
|
||
|
|
|
||
|
|
import sys
|
||
|
|
import errno
|
||
|
|
import functools
|
||
|
|
import atexit
|
||
|
|
|
||
|
|
try:
|
||
|
|
import cffi
|
||
|
|
except ImportError:
|
||
|
|
import ctypes
|
||
|
|
|
||
|
|
try:
|
||
|
|
from subprocess import _args_from_interpreter_flags # noqa
|
||
|
|
except ImportError: # pragma: no cover
|
||
|
|
def _args_from_interpreter_flags(): # noqa
|
||
|
|
"""Return a list of command-line arguments reproducing the current
|
||
|
|
settings in sys.flags and sys.warnoptions."""
|
||
|
|
flag_opt_map = {
|
||
|
|
'debug': 'd',
|
||
|
|
'optimize': 'O',
|
||
|
|
'dont_write_bytecode': 'B',
|
||
|
|
'no_user_site': 's',
|
||
|
|
'no_site': 'S',
|
||
|
|
'ignore_environment': 'E',
|
||
|
|
'verbose': 'v',
|
||
|
|
'bytes_warning': 'b',
|
||
|
|
'hash_randomization': 'R',
|
||
|
|
'py3k_warning': '3',
|
||
|
|
}
|
||
|
|
args = []
|
||
|
|
for flag, opt in flag_opt_map.items():
|
||
|
|
v = getattr(sys.flags, flag)
|
||
|
|
if v > 0:
|
||
|
|
args.append('-' + opt * v)
|
||
|
|
for opt in sys.warnoptions:
|
||
|
|
args.append('-W' + opt)
|
||
|
|
return args
|
||
|
|
|
||
|
|
from multiprocessing.util import ( # noqa
|
||
|
|
_afterfork_registry,
|
||
|
|
_afterfork_counter,
|
||
|
|
_exit_function,
|
||
|
|
_finalizer_registry,
|
||
|
|
_finalizer_counter,
|
||
|
|
Finalize,
|
||
|
|
ForkAwareLocal,
|
||
|
|
ForkAwareThreadLock,
|
||
|
|
get_temp_dir,
|
||
|
|
is_exiting,
|
||
|
|
register_after_fork,
|
||
|
|
_run_after_forkers,
|
||
|
|
_run_finalizers,
|
||
|
|
)
|
||
|
|
|
||
|
|
from .compat import get_errno
|
||
|
|
|
||
|
|
__all__ = [
|
||
|
|
'sub_debug', 'debug', 'info', 'sub_warning', 'get_logger',
|
||
|
|
'log_to_stderr', 'get_temp_dir', 'register_after_fork',
|
||
|
|
'is_exiting', 'Finalize', 'ForkAwareThreadLock', 'ForkAwareLocal',
|
||
|
|
'SUBDEBUG', 'SUBWARNING',
|
||
|
|
]
|
||
|
|
|
||
|
|
|
||
|
|
# Constants from prctl.h
|
||
|
|
PR_GET_PDEATHSIG = 2
|
||
|
|
PR_SET_PDEATHSIG = 1
|
||
|
|
|
||
|
|
#
|
||
|
|
# Logging
|
||
|
|
#
|
||
|
|
|
||
|
|
NOTSET = 0
|
||
|
|
SUBDEBUG = 5
|
||
|
|
DEBUG = 10
|
||
|
|
INFO = 20
|
||
|
|
SUBWARNING = 25
|
||
|
|
WARNING = 30
|
||
|
|
ERROR = 40
|
||
|
|
|
||
|
|
LOGGER_NAME = 'multiprocessing'
|
||
|
|
DEFAULT_LOGGING_FORMAT = '[%(levelname)s/%(processName)s] %(message)s'
|
||
|
|
|
||
|
|
_logger = None
|
||
|
|
_log_to_stderr = False
|
||
|
|
|
||
|
|
|
||
|
|
def sub_debug(msg, *args, **kwargs):
|
||
|
|
if _logger:
|
||
|
|
_logger.log(SUBDEBUG, msg, *args, **kwargs)
|
||
|
|
|
||
|
|
|
||
|
|
def debug(msg, *args, **kwargs):
|
||
|
|
if _logger:
|
||
|
|
_logger.log(DEBUG, msg, *args, **kwargs)
|
||
|
|
|
||
|
|
|
||
|
|
def info(msg, *args, **kwargs):
|
||
|
|
if _logger:
|
||
|
|
_logger.log(INFO, msg, *args, **kwargs)
|
||
|
|
|
||
|
|
|
||
|
|
def sub_warning(msg, *args, **kwargs):
|
||
|
|
if _logger:
|
||
|
|
_logger.log(SUBWARNING, msg, *args, **kwargs)
|
||
|
|
|
||
|
|
def warning(msg, *args, **kwargs):
|
||
|
|
if _logger:
|
||
|
|
_logger.log(WARNING, msg, *args, **kwargs)
|
||
|
|
|
||
|
|
def error(msg, *args, **kwargs):
|
||
|
|
if _logger:
|
||
|
|
_logger.log(ERROR, msg, *args, **kwargs)
|
||
|
|
|
||
|
|
|
||
|
|
def get_logger():
|
||
|
|
'''
|
||
|
|
Returns logger used by multiprocessing
|
||
|
|
'''
|
||
|
|
global _logger
|
||
|
|
import logging
|
||
|
|
|
||
|
|
try:
|
||
|
|
# Python 3.13+
|
||
|
|
acquire, release = logging._prepareFork, logging._afterFork
|
||
|
|
except AttributeError:
|
||
|
|
acquire, release = logging._acquireLock, logging._releaseLock
|
||
|
|
acquire()
|
||
|
|
try:
|
||
|
|
if not _logger:
|
||
|
|
|
||
|
|
_logger = logging.getLogger(LOGGER_NAME)
|
||
|
|
_logger.propagate = 0
|
||
|
|
logging.addLevelName(SUBDEBUG, 'SUBDEBUG')
|
||
|
|
logging.addLevelName(SUBWARNING, 'SUBWARNING')
|
||
|
|
|
||
|
|
# XXX multiprocessing should cleanup before logging
|
||
|
|
if hasattr(atexit, 'unregister'):
|
||
|
|
atexit.unregister(_exit_function)
|
||
|
|
atexit.register(_exit_function)
|
||
|
|
else:
|
||
|
|
atexit._exithandlers.remove((_exit_function, (), {}))
|
||
|
|
atexit._exithandlers.append((_exit_function, (), {}))
|
||
|
|
finally:
|
||
|
|
release()
|
||
|
|
|
||
|
|
return _logger
|
||
|
|
|
||
|
|
|
||
|
|
def log_to_stderr(level=None):
|
||
|
|
'''
|
||
|
|
Turn on logging and add a handler which prints to stderr
|
||
|
|
'''
|
||
|
|
global _log_to_stderr
|
||
|
|
import logging
|
||
|
|
|
||
|
|
logger = get_logger()
|
||
|
|
formatter = logging.Formatter(DEFAULT_LOGGING_FORMAT)
|
||
|
|
handler = logging.StreamHandler()
|
||
|
|
handler.setFormatter(formatter)
|
||
|
|
logger.addHandler(handler)
|
||
|
|
|
||
|
|
if level:
|
||
|
|
logger.setLevel(level)
|
||
|
|
_log_to_stderr = True
|
||
|
|
return _logger
|
||
|
|
|
||
|
|
|
||
|
|
def get_pdeathsig():
|
||
|
|
"""
|
||
|
|
Return the current value of the parent process death signal
|
||
|
|
"""
|
||
|
|
if not sys.platform.startswith('linux'):
|
||
|
|
# currently we support only linux platform.
|
||
|
|
raise OSError()
|
||
|
|
try:
|
||
|
|
if 'cffi' in sys.modules:
|
||
|
|
ffi = cffi.FFI()
|
||
|
|
ffi.cdef("int prctl (int __option, ...);")
|
||
|
|
arg = ffi.new("int *")
|
||
|
|
C = ffi.dlopen(None)
|
||
|
|
C.prctl(PR_GET_PDEATHSIG, arg)
|
||
|
|
return arg[0]
|
||
|
|
else:
|
||
|
|
sig = ctypes.c_int()
|
||
|
|
libc = ctypes.cdll.LoadLibrary("libc.so.6")
|
||
|
|
libc.prctl(PR_GET_PDEATHSIG, ctypes.byref(sig))
|
||
|
|
return sig.value
|
||
|
|
except Exception:
|
||
|
|
raise OSError()
|
||
|
|
|
||
|
|
|
||
|
|
def set_pdeathsig(sig):
|
||
|
|
"""
|
||
|
|
Set the parent process death signal of the calling process to sig
|
||
|
|
(either a signal value in the range 1..maxsig, or 0 to clear).
|
||
|
|
This is the signal that the calling process will get when its parent dies.
|
||
|
|
This value is cleared for the child of a fork(2) and
|
||
|
|
(since Linux 2.4.36 / 2.6.23) when executing a set-user-ID or set-group-ID binary.
|
||
|
|
"""
|
||
|
|
if not sys.platform.startswith('linux'):
|
||
|
|
# currently we support only linux platform.
|
||
|
|
raise OSError("pdeathsig is only supported on linux")
|
||
|
|
try:
|
||
|
|
if 'cffi' in sys.modules:
|
||
|
|
ffi = cffi.FFI()
|
||
|
|
ffi.cdef("int prctl (int __option, ...);")
|
||
|
|
C = ffi.dlopen(None)
|
||
|
|
C.prctl(PR_SET_PDEATHSIG, ffi.cast("int", sig))
|
||
|
|
else:
|
||
|
|
libc = ctypes.cdll.LoadLibrary("libc.so.6")
|
||
|
|
libc.prctl(PR_SET_PDEATHSIG, ctypes.c_int(sig))
|
||
|
|
except Exception as e:
|
||
|
|
raise OSError("An error occured while setting pdeathsig") from e
|
||
|
|
|
||
|
|
def _eintr_retry(func):
|
||
|
|
'''
|
||
|
|
Automatic retry after EINTR.
|
||
|
|
'''
|
||
|
|
|
||
|
|
@functools.wraps(func)
|
||
|
|
def wrapped(*args, **kwargs):
|
||
|
|
while 1:
|
||
|
|
try:
|
||
|
|
return func(*args, **kwargs)
|
||
|
|
except OSError as exc:
|
||
|
|
if get_errno(exc) != errno.EINTR:
|
||
|
|
raise
|
||
|
|
return wrapped
|