PageRenderTime 28ms CodeModel.GetById 11ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/Lib/multiprocessing/process.py

http://unladen-swallow.googlecode.com/
Python | 297 lines | 203 code | 30 blank | 64 comment | 27 complexity | f52a6e19667503d1d4d3bdc7ccb87262 MD5 | raw file
  1#
  2# Module providing the `Process` class which emulates `threading.Thread`
  3#
  4# multiprocessing/process.py
  5#
  6# Copyright (c) 2006-2008, R Oudkerk --- see COPYING.txt
  7#
  8
  9__all__ = ['Process', 'current_process', 'active_children']
 10
 11#
 12# Imports
 13#
 14
 15import os
 16import sys
 17import signal
 18import itertools
 19
 20#
 21#
 22#
 23
 24try:
 25    ORIGINAL_DIR = os.path.abspath(os.getcwd())
 26except OSError:
 27    ORIGINAL_DIR = None
 28
 29#
 30# Public functions
 31#
 32
 33def current_process():
 34    '''
 35    Return process object representing the current process
 36    '''
 37    return _current_process
 38
 39def active_children():
 40    '''
 41    Return list of process objects corresponding to live child processes
 42    '''
 43    _cleanup()
 44    return list(_current_process._children)
 45
 46#
 47#
 48#
 49
 50def _cleanup():
 51    # check for processes which have finished
 52    for p in list(_current_process._children):
 53        if p._popen.poll() is not None:
 54            _current_process._children.discard(p)
 55
 56#
 57# The `Process` class
 58#
 59
 60class Process(object):
 61    '''
 62    Process objects represent activity that is run in a separate process
 63
 64    The class is analagous to `threading.Thread`
 65    '''
 66    _Popen = None
 67
 68    def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
 69        assert group is None, 'group argument must be None for now'
 70        count = _current_process._counter.next()
 71        self._identity = _current_process._identity + (count,)
 72        self._authkey = _current_process._authkey
 73        self._daemonic = _current_process._daemonic
 74        self._tempdir = _current_process._tempdir
 75        self._parent_pid = os.getpid()
 76        self._popen = None
 77        self._target = target
 78        self._args = tuple(args)
 79        self._kwargs = dict(kwargs)
 80        self._name = name or type(self).__name__ + '-' + \
 81                     ':'.join(str(i) for i in self._identity)
 82
 83    def run(self):
 84        '''
 85        Method to be run in sub-process; can be overridden in sub-class
 86        '''
 87        if self._target:
 88            self._target(*self._args, **self._kwargs)
 89
 90    def start(self):
 91        '''
 92        Start child process
 93        '''
 94        assert self._popen is None, 'cannot start a process twice'
 95        assert self._parent_pid == os.getpid(), \
 96               'can only start a process object created by current process'
 97        assert not _current_process._daemonic, \
 98               'daemonic processes are not allowed to have children'
 99        _cleanup()
100        if self._Popen is not None:
101            Popen = self._Popen
102        else:
103            from .forking import Popen
104        self._popen = Popen(self)
105        _current_process._children.add(self)
106
107    def terminate(self):
108        '''
109        Terminate process; sends SIGTERM signal or uses TerminateProcess()
110        '''
111        self._popen.terminate()
112
113    def join(self, timeout=None):
114        '''
115        Wait until child process terminates
116        '''
117        assert self._parent_pid == os.getpid(), 'can only join a child process'
118        assert self._popen is not None, 'can only join a started process'
119        res = self._popen.wait(timeout)
120        if res is not None:
121            _current_process._children.discard(self)
122
123    def is_alive(self):
124        '''
125        Return whether process is alive
126        '''
127        if self is _current_process:
128            return True
129        assert self._parent_pid == os.getpid(), 'can only test a child process'
130        if self._popen is None:
131            return False
132        self._popen.poll()
133        return self._popen.returncode is None
134
135    @property
136    def name(self):
137        return self._name
138
139    @name.setter
140    def name(self, name):
141        assert isinstance(name, str), 'name must be a string'
142        self._name = name
143
144    @property
145    def daemon(self):
146        '''
147        Return whether process is a daemon
148        '''
149        return self._daemonic
150
151    @daemon.setter
152    def daemon(self, daemonic):
153        '''
154        Set whether process is a daemon
155        '''
156        assert self._popen is None, 'process has already started'
157        self._daemonic = daemonic
158
159    @property
160    def authkey(self):
161        return self._authkey
162
163    @authkey.setter
164    def authkey(self, authkey):
165        '''
166        Set authorization key of process
167        '''
168        self._authkey = AuthenticationString(authkey)
169
170    @property
171    def exitcode(self):
172        '''
173        Return exit code of process or `None` if it has yet to stop
174        '''
175        if self._popen is None:
176            return self._popen
177        return self._popen.poll()
178
179    @property
180    def ident(self):
181        '''
182        Return indentifier (PID) of process or `None` if it has yet to start
183        '''
184        if self is _current_process:
185            return os.getpid()
186        else:
187            return self._popen and self._popen.pid
188
189    pid = ident
190
191    def __repr__(self):
192        if self is _current_process:
193            status = 'started'
194        elif self._parent_pid != os.getpid():
195            status = 'unknown'
196        elif self._popen is None:
197            status = 'initial'
198        else:
199            if self._popen.poll() is not None:
200                status = self.exitcode
201            else:
202                status = 'started'
203
204        if type(status) is int:
205            if status == 0:
206                status = 'stopped'
207            else:
208                status = 'stopped[%s]' % _exitcode_to_name.get(status, status)
209
210        return '<%s(%s, %s%s)>' % (type(self).__name__, self._name,
211                                   status, self._daemonic and ' daemon' or '')
212
213    ##
214
215    def _bootstrap(self):
216        from . import util
217        global _current_process
218
219        try:
220            self._children = set()
221            self._counter = itertools.count(1)
222            try:
223                sys.stdin.close()
224                sys.stdin = open(os.devnull)
225            except (OSError, ValueError):
226                pass
227            _current_process = self
228            util._finalizer_registry.clear()
229            util._run_after_forkers()
230            util.info('child process calling self.run()')
231            try:
232                self.run()
233                exitcode = 0
234            finally:
235                util._exit_function()
236        except SystemExit, e:
237            if not e.args:
238                exitcode = 1
239            elif type(e.args[0]) is int:
240                exitcode = e.args[0]
241            else:
242                sys.stderr.write(e.args[0] + '\n')
243                sys.stderr.flush()
244                exitcode = 1
245        except:
246            exitcode = 1
247            import traceback
248            sys.stderr.write('Process %s:\n' % self.name)
249            sys.stderr.flush()
250            traceback.print_exc()
251
252        util.info('process exiting with exitcode %d' % exitcode)
253        return exitcode
254
255#
256# We subclass bytes to avoid accidental transmission of auth keys over network
257#
258
259class AuthenticationString(bytes):
260    def __reduce__(self):
261        from .forking import Popen
262        if not Popen.thread_is_spawning():
263            raise TypeError(
264                'Pickling an AuthenticationString object is '
265                'disallowed for security reasons'
266                )
267        return AuthenticationString, (bytes(self),)
268
269#
270# Create object representing the main process
271#
272
273class _MainProcess(Process):
274
275    def __init__(self):
276        self._identity = ()
277        self._daemonic = False
278        self._name = 'MainProcess'
279        self._parent_pid = None
280        self._popen = None
281        self._counter = itertools.count(1)
282        self._children = set()
283        self._authkey = AuthenticationString(os.urandom(32))
284        self._tempdir = None
285
286_current_process = _MainProcess()
287del _MainProcess
288
289#
290# Give names to some return codes
291#
292
293_exitcode_to_name = {}
294
295for name, signum in signal.__dict__.items():
296    if name[:3]=='SIG' and '_' not in name:
297        _exitcode_to_name[-signum] = name