]> granicus.if.org Git - python/commitdiff
Issue #23309: Avoid a deadlock at shutdown if a daemon thread is aborted
authorAntoine Pitrou <solipsis@pitrou.net>
Mon, 13 Apr 2015 17:48:19 +0000 (19:48 +0200)
committerAntoine Pitrou <solipsis@pitrou.net>
Mon, 13 Apr 2015 17:48:19 +0000 (19:48 +0200)
while it is holding a lock to a buffered I/O object, and the main thread
tries to use the same I/O object (typically stdout or stderr).  A fatal
error is emitted instead.

1  2 
Lib/test/script_helper.py
Lib/test/test_io.py
Lib/test/test_script_helper.py
Misc/NEWS
Modules/_io/bufferedio.c

index 8743dba79e5f9d7593650515b3b91fbb1135e91a,b31fc40013e0959805a4614e93a5a3ce96cfa464..7ac19bf8ede7129e9637d8940f2d6c37c33d426f
@@@ -50,9 -51,13 +51,13 @@@ def interpreter_requires_environment()
      return __cached_interp_requires_environment
  
  
+ _PythonRunResult = collections.namedtuple("_PythonRunResult",
+                                           ("rc", "out", "err"))
  # Executing the interpreter in a subprocess
- def _assert_python(expected_success, *args, **env_vars):
+ def run_python_until_end(*args, **env_vars):
 -    env_required = _interpreter_requires_environment()
 +    env_required = interpreter_requires_environment()
      if '__isolated' in env_vars:
          isolated = env_vars.pop('__isolated')
      else:
          p.stderr.close()
      rc = p.returncode
      err = strip_python_stderr(err)
-     if (rc and expected_success) or (not rc and not expected_success):
+     return _PythonRunResult(rc, out, err), cmd_line
+ def _assert_python(expected_success, *args, **env_vars):
+     res, cmd_line = run_python_until_end(*args, **env_vars)
+     if (res.rc and expected_success) or (not res.rc and not expected_success):
 -        raise AssertionError(
 -            "Process return code is %d, command line was: %r, "
 -            "stderr follows:\n%s" % (res.rc, cmd_line,
 -                                     res.err.decode('ascii', 'ignore')))
 +        # Limit to 80 lines to ASCII characters
 +        maxlen = 80 * 100
++        out, err = res.out, res.err
 +        if len(out) > maxlen:
 +            out = b'(... truncated stdout ...)' + out[-maxlen:]
 +        if len(err) > maxlen:
 +            err = b'(... truncated stderr ...)' + err[-maxlen:]
 +        out = out.decode('ascii', 'replace').rstrip()
 +        err = err.decode('ascii', 'replace').rstrip()
 +        raise AssertionError("Process return code is %d\n"
 +                             "command line: %r\n"
 +                             "\n"
 +                             "stdout:\n"
 +                             "---\n"
 +                             "%s\n"
 +                             "---\n"
 +                             "\n"
 +                             "stderr:\n"
 +                             "---\n"
 +                             "%s\n"
 +                             "---"
-                              % (rc, cmd_line,
++                             % (res.rc, cmd_line,
 +                                out,
 +                                err))
-     return rc, out, err
+     return res
  
  def assert_python_ok(*args, **env_vars):
      """
Simple merge
index 7f5d65456ce8f792010f568b7409be3e410c83bd,372d6a79ae9fea5436ed50026177f148e3d692d8..86945301c832715339460b57d5936edb71317a4e
mode 100644,100755..100644
@@@ -8,28 -8,29 +8,29 @@@ from unittest import moc
  
  
  class TestScriptHelper(unittest.TestCase):
-     def test_assert_python_expect_success(self):
-         t = script_helper._assert_python(True, '-c', 'import sys; sys.exit(0)')
+     def test_assert_python_ok(self):
+         t = script_helper.assert_python_ok('-c', 'import sys; sys.exit(0)')
          self.assertEqual(0, t[0], 'return code was not 0')
  
-     def test_assert_python_expect_failure(self):
+     def test_assert_python_failure(self):
          # I didn't import the sys module so this child will fail.
-         rc, out, err = script_helper._assert_python(False, '-c', 'sys.exit(0)')
+         rc, out, err = script_helper.assert_python_failure('-c', 'sys.exit(0)')
          self.assertNotEqual(0, rc, 'return code should not be 0')
  
-     def test_assert_python_raises_expect_success(self):
+     def test_assert_python_ok_raises(self):
          # I didn't import the sys module so this child will fail.
          with self.assertRaises(AssertionError) as error_context:
-             script_helper._assert_python(True, '-c', 'sys.exit(0)')
+             script_helper.assert_python_ok('-c', 'sys.exit(0)')
          error_msg = str(error_context.exception)
 -        self.assertIn('command line was:', error_msg)
 +        self.assertIn('command line:', error_msg)
          self.assertIn('sys.exit(0)', error_msg, msg='unexpected command line')
  
-     def test_assert_python_raises_expect_failure(self):
+     def test_assert_python_failure_raises(self):
          with self.assertRaises(AssertionError) as error_context:
-             script_helper._assert_python(False, '-c', 'import sys; sys.exit(0)')
+             script_helper.assert_python_failure('-c', 'import sys; sys.exit(0)')
          error_msg = str(error_context.exception)
 -        self.assertIn('Process return code is 0,', error_msg)
 +        self.assertIn('Process return code is 0\n', error_msg)
          self.assertIn('import sys; sys.exit(0)', error_msg,
                        msg='unexpected command line.')
  
diff --cc Misc/NEWS
Simple merge
Simple merge