~ubuntu-branches/ubuntu/maverick/python3.1/maverick

« back to all changes in this revision

Viewing changes to Lib/multiprocessing/process.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2009-03-23 00:01:27 UTC
  • Revision ID: james.westby@ubuntu.com-20090323000127-5fstfxju4ufrhthq
Tags: upstream-3.1~a1+20090322
ImportĀ upstreamĀ versionĀ 3.1~a1+20090322

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
15
import os
 
16
import sys
 
17
import signal
 
18
import itertools
 
19
 
 
20
#
 
21
#
 
22
#
 
23
 
 
24
try:
 
25
    ORIGINAL_DIR = os.path.abspath(os.getcwd())
 
26
except OSError:
 
27
    ORIGINAL_DIR = None
 
28
 
 
29
#
 
30
# Public functions
 
31
#
 
32
 
 
33
def current_process():
 
34
    '''
 
35
    Return process object representing the current process
 
36
    '''
 
37
    return _current_process
 
38
 
 
39
def 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
 
 
50
def _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
 
 
60
class 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 = next(_current_process._counter)
 
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
            if sys.stdin is not None:
 
223
                try:
 
224
                    os.close(sys.stdin.fileno())
 
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 as 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
 
 
259
class 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
 
 
273
class _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()
 
287
del _MainProcess
 
288
 
 
289
#
 
290
# Give names to some return codes
 
291
#
 
292
 
 
293
_exitcode_to_name = {}
 
294
 
 
295
for name, signum in list(signal.__dict__.items()):
 
296
    if name[:3]=='SIG' and '_' not in name:
 
297
        _exitcode_to_name[-signum] = name