Merged revisions 74063 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/branches/py3k

NB: the news item for r73708 seems to have inadvertently been included in a
different, unrelated merge set (r74055).  I restored it here.

................
  r74063 | alexandre.vassalotti | 2009-07-17 08:07:01 -0400 (Fri, 17 Jul 2009) | 17 lines

  Merged revisions 73694,73708,73738 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r73694 | jesse.noller | 2009-06-29 14:24:26 -0400 (Mon, 29 Jun 2009) | 1 line

    Issue 5740: multiprocessing.connection.* authkey fixes
  ........
    r73708 | jesse.noller | 2009-06-30 13:11:52 -0400 (Tue, 30 Jun 2009) | 1 line

    Resolves issues 5155, 5313, 5331 - bad file descriptor error with processes in processes
  ........
    r73738 | r.david.murray | 2009-06-30 22:49:10 -0400 (Tue, 30 Jun 2009) | 2 lines

    Make punctuation prettier and break up run-on sentence.
  ........
................
This commit is contained in:
R. David Murray 2009-07-29 15:40:30 +00:00
parent 0c40786c73
commit a44c6b325f
5 changed files with 112 additions and 4 deletions

View file

@ -1713,7 +1713,7 @@ authentication* using the :mod:`hmac` module.
generally be omitted since it can usually be inferred from the format of generally be omitted since it can usually be inferred from the format of
*address*. (See :ref:`multiprocessing-address-formats`) *address*. (See :ref:`multiprocessing-address-formats`)
If *authentication* is ``True`` or *authkey* is a string then digest If *authenticate* is ``True`` or *authkey* is a string then digest
authentication is used. The key used for authentication will be either authentication is used. The key used for authentication will be either
*authkey* or ``current_process().authkey)`` if *authkey* is ``None``. *authkey* or ``current_process().authkey)`` if *authkey* is ``None``.
If authentication fails then :exc:`AuthenticationError` is raised. See If authentication fails then :exc:`AuthenticationError` is raised. See
@ -1755,7 +1755,7 @@ authentication* using the :mod:`hmac` module.
If *authkey* is ``None`` and *authenticate* is ``True`` then If *authkey* is ``None`` and *authenticate* is ``True`` then
``current_process().authkey`` is used as the authentication key. If ``current_process().authkey`` is used as the authentication key. If
*authkey* is ``None`` and *authentication* is ``False`` then no *authkey* is ``None`` and *authenticate* is ``False`` then no
authentication is done. If authentication fails then authentication is done. If authentication fails then
:exc:`AuthenticationError` is raised. See :ref:`multiprocessing-auth-keys`. :exc:`AuthenticationError` is raised. See :ref:`multiprocessing-auth-keys`.
@ -2097,6 +2097,38 @@ Explicitly pass resources to child processes
for i in range(10): for i in range(10):
Process(target=f, args=(lock,)).start() Process(target=f, args=(lock,)).start()
Beware replacing sys.stdin with a "file like object"
:mod:`multiprocessing` originally unconditionally called::
os.close(sys.stdin.fileno())
in the :meth:`multiprocessing.Process._bootstrap` method --- this resulted
in issues with processes-in-processes. This has been changed to::
sys.stdin.close()
sys.stdin = open(os.devnull)
Which solves the fundamental issue of processes colliding with each other
resulting in a bad file descriptor error, but introduces a potential danger
to applications which replace :func:`sys.stdin` with a "file-like object"
with output buffering. This danger is that if multiple processes call
:func:`close()` on this file-like object, it could result in the same
data being flushed to the object multiple times, resulting in corruption.
If you write a file-like object and implement your own caching, you can
make it fork-safe by storing the pid whenever you append to the cache,
and discarding the cache when the pid changes. For example::
@property
def cache(self):
pid = os.getpid()
if pid != self._pid:
self._pid = pid
self._cache = []
return self._cache
For more information, see :issue:`5155`, :issue:`5313` and :issue:`5331`
Windows Windows
~~~~~~~ ~~~~~~~

View file

@ -221,7 +221,8 @@ class Process(object):
self._counter = itertools.count(1) self._counter = itertools.count(1)
if sys.stdin is not None: if sys.stdin is not None:
try: try:
os.close(sys.stdin.fileno()) sys.stdin.close()
sys.stdin = open(os.devnull)
except (OSError, ValueError): except (OSError, ValueError):
pass pass
_current_process = self _current_process = self

View file

@ -8,6 +8,7 @@ import unittest
import threading import threading
import queue as pyqueue import queue as pyqueue
import time import time
import io
import sys import sys
import os import os
import gc import gc
@ -1862,7 +1863,74 @@ class TestInitializers(unittest.TestCase):
p.join() p.join()
self.assertEqual(self.ns.test, 1) self.assertEqual(self.ns.test, 1)
testcases_other = [OtherTest, TestInvalidHandle, TestInitializers] #
# Issue 5155, 5313, 5331: Test process in processes
# Verifies os.close(sys.stdin.fileno) vs. sys.stdin.close() behavior
#
def _ThisSubProcess(q):
try:
item = q.get(block=False)
except pyqueue.Empty:
pass
def _TestProcess(q):
queue = multiprocessing.Queue()
subProc = multiprocessing.Process(target=_ThisSubProcess, args=(queue,))
subProc.start()
subProc.join()
def _afunc(x):
return x*x
def pool_in_process():
pool = multiprocessing.Pool(processes=4)
x = pool.map(_afunc, [1, 2, 3, 4, 5, 6, 7])
class _file_like(object):
def __init__(self, delegate):
self._delegate = delegate
self._pid = None
@property
def cache(self):
pid = os.getpid()
# There are no race conditions since fork keeps only the running thread
if pid != self._pid:
self._pid = pid
self._cache = []
return self._cache
def write(self, data):
self.cache.append(data)
def flush(self):
self._delegate.write(''.join(self.cache))
self._cache = []
class TestStdinBadfiledescriptor(unittest.TestCase):
def test_queue_in_process(self):
queue = multiprocessing.Queue()
proc = multiprocessing.Process(target=_TestProcess, args=(queue,))
proc.start()
proc.join()
def test_pool_in_process(self):
p = multiprocessing.Process(target=pool_in_process)
p.start()
p.join()
def test_flushing(self):
sio = io.StringIO()
flike = _file_like(sio)
flike.write('foo')
proc = multiprocessing.Process(target=lambda: flike.flush())
flike.flush()
assert sio.getvalue() == 'foo'
testcases_other = [OtherTest, TestInvalidHandle, TestInitializers,
TestStdinBadfiledescriptor]
# #
# #

View file

@ -45,6 +45,7 @@ Des Barry
Ulf Bartelt Ulf Bartelt
Nick Bastin Nick Bastin
Jeff Bauer Jeff Bauer
Mike Bayer
Michael R Bax Michael R Bax
Anthony Baxter Anthony Baxter
Samuel L. Bayer Samuel L. Bayer
@ -184,6 +185,7 @@ Cesar Douady
Dean Draayer Dean Draayer
John DuBois John DuBois
Paul Dubois Paul Dubois
Graham Dumpleton
Quinn Dunkan Quinn Dunkan
Robin Dunn Robin Dunn
Luke Dunstan Luke Dunstan
@ -556,6 +558,7 @@ Steven Pemberton
Santiago Peresón Santiago Peresón
Mark Perrego Mark Perrego
Trevor Perrin Trevor Perrin
Gabriel de Perthuis
Tim Peters Tim Peters
Benjamin Peterson Benjamin Peterson
Chris Petrilli Chris Petrilli

View file

@ -42,6 +42,10 @@ C-API
Library Library
------- -------
- Issues #5155, #5313, #5331: multiprocessing.Process._bootstrap was
unconditionally calling "os.close(sys.stdin.fileno())" resulting in file
descriptor errors
- Issue #1424152: Fix for http.client, urllib.request to support SSL while - Issue #1424152: Fix for http.client, urllib.request to support SSL while
working through proxy. Original patch by Christopher Li, changes made by working through proxy. Original patch by Christopher Li, changes made by
Senthil Kumaran Senthil Kumaran