1# 2# Module providing various facilities to other parts of the package 3# 4# multiprocessing/util.py 5# 6# Copyright (c) 2006-2008, R Oudkerk 7# All rights reserved. 8# 9# Redistribution and use in source and binary forms, with or without 10# modification, are permitted provided that the following conditions 11# are met: 12# 13# 1. Redistributions of source code must retain the above copyright 14# notice, this list of conditions and the following disclaimer. 15# 2. Redistributions in binary form must reproduce the above copyright 16# notice, this list of conditions and the following disclaimer in the 17# documentation and/or other materials provided with the distribution. 18# 3. Neither the name of author nor the names of any contributors may be 19# used to endorse or promote products derived from this software 20# without specific prior written permission. 21# 22# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND 23# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32# SUCH DAMAGE. 33# 34 35import os 36import itertools 37import weakref 38import atexit 39import threading # we want threading to install it's 40 # cleanup function before multiprocessing does 41from subprocess import _args_from_interpreter_flags 42 43from multiprocessing.process import current_process, active_children 44 45__all__ = [ 46 'sub_debug', 'debug', 'info', 'sub_warning', 'get_logger', 47 'log_to_stderr', 'get_temp_dir', 'register_after_fork', 48 'is_exiting', 'Finalize', 'ForkAwareThreadLock', 'ForkAwareLocal', 49 'SUBDEBUG', 'SUBWARNING', 50 ] 51 52# 53# Logging 54# 55 56NOTSET = 0 57SUBDEBUG = 5 58DEBUG = 10 59INFO = 20 60SUBWARNING = 25 61 62LOGGER_NAME = 'multiprocessing' 63DEFAULT_LOGGING_FORMAT = '[%(levelname)s/%(processName)s] %(message)s' 64 65_logger = None 66_log_to_stderr = False 67 68def sub_debug(msg, *args): 69 if _logger: 70 _logger.log(SUBDEBUG, msg, *args) 71 72def debug(msg, *args): 73 if _logger: 74 _logger.log(DEBUG, msg, *args) 75 76def info(msg, *args): 77 if _logger: 78 _logger.log(INFO, msg, *args) 79 80def sub_warning(msg, *args): 81 if _logger: 82 _logger.log(SUBWARNING, msg, *args) 83 84def get_logger(): 85 ''' 86 Returns logger used by multiprocessing 87 ''' 88 global _logger 89 import logging, atexit 90 91 logging._acquireLock() 92 try: 93 if not _logger: 94 95 _logger = logging.getLogger(LOGGER_NAME) 96 _logger.propagate = 0 97 logging.addLevelName(SUBDEBUG, 'SUBDEBUG') 98 logging.addLevelName(SUBWARNING, 'SUBWARNING') 99 100 # XXX multiprocessing should cleanup before logging 101 if hasattr(atexit, 'unregister'): 102 atexit.unregister(_exit_function) 103 atexit.register(_exit_function) 104 else: 105 atexit._exithandlers.remove((_exit_function, (), {})) 106 atexit._exithandlers.append((_exit_function, (), {})) 107 108 finally: 109 logging._releaseLock() 110 111 return _logger 112 113def log_to_stderr(level=None): 114 ''' 115 Turn on logging and add a handler which prints to stderr 116 ''' 117 global _log_to_stderr 118 import logging 119 120 logger = get_logger() 121 formatter = logging.Formatter(DEFAULT_LOGGING_FORMAT) 122 handler = logging.StreamHandler() 123 handler.setFormatter(formatter) 124 logger.addHandler(handler) 125 126 if level: 127 logger.setLevel(level) 128 _log_to_stderr = True 129 return _logger 130 131# 132# Function returning a temp directory which will be removed on exit 133# 134 135def get_temp_dir(): 136 # get name of a temp directory which will be automatically cleaned up 137 if current_process()._tempdir is None: 138 import shutil, tempfile 139 tempdir = tempfile.mkdtemp(prefix='pymp-') 140 info('created temp directory %s', tempdir) 141 Finalize(None, shutil.rmtree, args=[tempdir], exitpriority=-100) 142 current_process()._tempdir = tempdir 143 return current_process()._tempdir 144 145# 146# Support for reinitialization of objects when bootstrapping a child process 147# 148 149_afterfork_registry = weakref.WeakValueDictionary() 150_afterfork_counter = itertools.count() 151 152def _run_after_forkers(): 153 items = list(_afterfork_registry.items()) 154 items.sort() 155 for (index, ident, func), obj in items: 156 try: 157 func(obj) 158 except Exception, e: 159 info('after forker raised exception %s', e) 160 161def register_after_fork(obj, func): 162 _afterfork_registry[(_afterfork_counter.next(), id(obj), func)] = obj 163 164# 165# Finalization using weakrefs 166# 167 168_finalizer_registry = {} 169_finalizer_counter = itertools.count() 170 171 172class Finalize(object): 173 ''' 174 Class which supports object finalization using weakrefs 175 ''' 176 def __init__(self, obj, callback, args=(), kwargs=None, exitpriority=None): 177 assert exitpriority is None or type(exitpriority) is int 178 179 if obj is not None: 180 self._weakref = weakref.ref(obj, self) 181 else: 182 assert exitpriority is not None 183 184 self._callback = callback 185 self._args = args 186 self._kwargs = kwargs or {} 187 self._key = (exitpriority, _finalizer_counter.next()) 188 self._pid = os.getpid() 189 190 _finalizer_registry[self._key] = self 191 192 def __call__(self, wr=None): 193 ''' 194 Run the callback unless it has already been called or cancelled 195 ''' 196 try: 197 del _finalizer_registry[self._key] 198 except KeyError: 199 sub_debug('finalizer no longer registered') 200 else: 201 if self._pid != os.getpid(): 202 sub_debug('finalizer ignored because different process') 203 res = None 204 else: 205 sub_debug('finalizer calling %s with args %s and kwargs %s', 206 self._callback, self._args, self._kwargs) 207 res = self._callback(*self._args, **self._kwargs) 208 self._weakref = self._callback = self._args = \ 209 self._kwargs = self._key = None 210 return res 211 212 def cancel(self): 213 ''' 214 Cancel finalization of the object 215 ''' 216 try: 217 del _finalizer_registry[self._key] 218 except KeyError: 219 pass 220 else: 221 self._weakref = self._callback = self._args = \ 222 self._kwargs = self._key = None 223 224 def still_active(self): 225 ''' 226 Return whether this finalizer is still waiting to invoke callback 227 ''' 228 return self._key in _finalizer_registry 229 230 def __repr__(self): 231 try: 232 obj = self._weakref() 233 except (AttributeError, TypeError): 234 obj = None 235 236 if obj is None: 237 return '<Finalize object, dead>' 238 239 x = '<Finalize object, callback=%s' % \ 240 getattr(self._callback, '__name__', self._callback) 241 if self._args: 242 x += ', args=' + str(self._args) 243 if self._kwargs: 244 x += ', kwargs=' + str(self._kwargs) 245 if self._key[0] is not None: 246 x += ', exitprority=' + str(self._key[0]) 247 return x + '>' 248 249 250def _run_finalizers(minpriority=None): 251 ''' 252 Run all finalizers whose exit priority is not None and at least minpriority 253 254 Finalizers with highest priority are called first; finalizers with 255 the same priority will be called in reverse order of creation. 256 ''' 257 if _finalizer_registry is None: 258 # This function may be called after this module's globals are 259 # destroyed. See the _exit_function function in this module for more 260 # notes. 261 return 262 263 if minpriority is None: 264 f = lambda p : p[0][0] is not None 265 else: 266 f = lambda p : p[0][0] is not None and p[0][0] >= minpriority 267 268 items = [x for x in _finalizer_registry.items() if f(x)] 269 items.sort(reverse=True) 270 271 for key, finalizer in items: 272 sub_debug('calling %s', finalizer) 273 try: 274 finalizer() 275 except Exception: 276 import traceback 277 traceback.print_exc() 278 279 if minpriority is None: 280 _finalizer_registry.clear() 281 282# 283# Clean up on exit 284# 285 286def is_exiting(): 287 ''' 288 Returns true if the process is shutting down 289 ''' 290 return _exiting or _exiting is None 291 292_exiting = False 293 294def _exit_function(info=info, debug=debug, _run_finalizers=_run_finalizers, 295 active_children=active_children, 296 current_process=current_process): 297 # NB: we hold on to references to functions in the arglist due to the 298 # situation described below, where this function is called after this 299 # module's globals are destroyed. 300 301 global _exiting 302 303 info('process shutting down') 304 debug('running all "atexit" finalizers with priority >= 0') 305 _run_finalizers(0) 306 307 if current_process() is not None: 308 # NB: we check if the current process is None here because if 309 # it's None, any call to ``active_children()`` will throw an 310 # AttributeError (active_children winds up trying to get 311 # attributes from util._current_process). This happens in a 312 # variety of shutdown circumstances that are not well-understood 313 # because module-scope variables are not apparently supposed to 314 # be destroyed until after this function is called. However, 315 # they are indeed destroyed before this function is called. See 316 # issues 9775 and 15881. Also related: 4106, 9205, and 9207. 317 318 for p in active_children(): 319 if p._daemonic: 320 info('calling terminate() for daemon %s', p.name) 321 p._popen.terminate() 322 323 for p in active_children(): 324 info('calling join() for process %s', p.name) 325 p.join() 326 327 debug('running the remaining "atexit" finalizers') 328 _run_finalizers() 329 330atexit.register(_exit_function) 331 332# 333# Some fork aware types 334# 335 336class ForkAwareThreadLock(object): 337 def __init__(self): 338 self._reset() 339 register_after_fork(self, ForkAwareThreadLock._reset) 340 341 def _reset(self): 342 self._lock = threading.Lock() 343 self.acquire = self._lock.acquire 344 self.release = self._lock.release 345 346class ForkAwareLocal(threading.local): 347 def __init__(self): 348 register_after_fork(self, lambda obj : obj.__dict__.clear()) 349 def __reduce__(self): 350 return type(self), () 351