python / python/cpython

Race condition in test_traceback_when_child_process_terminates_abruptly

未關閉
#143,620 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

tests type-bug
主要語言
Python
星號
77.2k
分支
35.9k
PR 合併指標
PR 指標待擷取

描述

Bug report

Bug description:

I have been observing that on my local build of main branch when I run the test suite, I occasionally am getting a failed test. Failure here:

0:01:56 load avg: 2.52 [ 39/498] test.test_concurrent_futures.test_interpreter_pool passed

0:01:56 load avg: 2.52 [ 40/498] test.test_concurrent_futures.test_process_pool

test test.test_concurrent_futures.test_process_pool failed -- Traceback (most recent call last):

  File "/Users/a12k/opt/cpython/Lib/test/test_concurrent_futures/test_process_pool.py", line 119, in test_traceback_when_child_process_terminates_abruptly

    self.assertIsInstance(cause, futures.process._RemoteTraceback)

    ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

AssertionError: None is not an instance of <class 'concurrent.futures.process._RemoteTraceback'>

0:02:08 load avg: 2.97 [ 40/498/1] test.test_concurrent_futures.test_process_pool failed (1 failure)
0:02:08 load avg: 2.97 [ 41/498/1] test.test_concurrent_futures.test_shutdown

I tried a few different ways of repro-ing this deterministically, mostly letting it and the few tests that preceded it run for an hour (./python.exe -m test -v -F test.test_concurrent_futures.test_process_pool, or ./python -m test -v -F test_concurrent_futures.test_deadlock test_concurrent_futures.test_interpreter_pool test_concurrent_futures.test_process_pool) until it failed.

I ended up forcing the fail in Lib/concurrent/futures/process.py which is as follows (insert at line 486 right after errors = [] all the way until # Mark pending tasks as failed.):

            if any(fn == os._exit for fn in [w.fn for w in self.pending_work_items.values()]):
                print("~~~ ARTIFICAL DELAY ~~~")
                for p in list(self.processes.values()):
                    # set exit code to None to simulate it not ready yet
                    object.__setattr__(p, "_exitcode", None)

            for p in self.processes.values():
                if p.exitcode is not None and p.exitcode != 0:
                    errors.append(f"Process {p.pid} terminated abruptly "
                                  f"with exit code {p.exitcode}")
            if errors:
                cause_str = "\n".join(errors)

        if cause_str and any(fn == os._exit for fn in [w.fn for w in self.pending_work_items.values()]):
            print("~~~ ARTIFICAL DELAY ~~~ Waiting to set __cause__ for 3 seconds")
            def delayed_set_cause():
                import time
                time.sleep(3)
                print("~~~ ARTIFICAL DELAY COMPLETE ~~~ setting __cause__")
                nonlocal bpe
                bpe.__cause__ = _RemoteTraceback(f"\n'''\n{cause_str}'''")

            # Set cause after delay
            threading.Thread(target=delayed_set_cause, daemon=True).start()
        elif cause_str:
            bpe.__cause__ = _RemoteTraceback(f"\n'''\n{cause_str}'''")

Basically forcing the race condition, setting the cause to None and delaying it. Not sure if this was all necessary, but that's how I was able to deterministically get it to continually fail.

I updated the test to account for this race condition by waiting for __cause__ to be populated and now the test passes. PR incoming for review.

CPython versions tested on:

CPython main branch

Operating systems tested on:

macOS

Linked PRs
  • gh-143621

貢獻指南

開啟貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

研究方向

從 Lib/test/test_concurrent_futures/test_process_pool.py 中的 test_traceback_when_child_process_terminates_abruptly 開始,並檢查 Lib/concurrent/futures/process.py 中相關的處理邏輯。反覆執行目標程序池測試,以重現間歇性失敗。當測試能在沒有人工延遲的情況下可靠地觀察到預期的 _RemoteTraceback 時,即表示完成。

由索引模型根據 Issue 內容生成。

評估

技術堆疊
python
領域
testing-qa
Issue 類型
缺陷
難度
4/5
預估耗時
3-5 天
活躍度
停滯
描述清晰度
描述清楚
新手友好度
15/100

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。