LIST_APPEND and SET_ADD opcodes safety in Free Threading
まだ誰も着手していません。
- 主要言語
- Python
- スター
- 77.2k
- フォーク
- 35.9k
- PR マージ指標
- PR 指標を取得中
説明
Hi,
While reviewing PR gh-152273, I moved _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so); check to set_add_entry_takeref() to check for bugs. I discovered that SET_ADD opcode calls _PySet_AddTakeRef() without getting the set critical section. That's surprising and IMO it would deserve adding a comment to explain why it's safe to omit locking here.
LIST_APPEND has a similar design (also omit locking).
The following code uses the evil gc.get_objects() function to call set.add() in a different thread while the main thread is building a set:
import dis
import gc
import threading
GLOBAL_SET = None
def worker():
EVENT.wait()
obj = GLOBAL_SET
for i in range(100):
# Call set.add() which uses the critical section
obj.add(i)
MARKER = b"MARKER".decode()
EVENT = threading.Event()
class EvilHash:
def __init__(self, hash_value):
self.hash_value = hash_value
def __hash__(self):
global GLOBAL_SET, EVENT
if GLOBAL_SET is None:
# Invoke the evil gc.get_objects()!
for obj in gc.get_objects():
if isinstance(obj, set) and MARKER in obj:
GLOBAL_SET = obj
EVENT.set()
return self.hash_value
def __repr__(self):
return f"EvilHash({self.hash_value})"
def func():
build_set = {
# Use *list so following items are added by SET_ADD opcode
*[MARKER],
# Added by SET_ADD which calls _PySet_AddTakeRef()
# without the critical section
EvilHash(0), EvilHash(1), EvilHash(2), EvilHash(3), EvilHash(4),
EvilHash(5)}
print(build_set)
print("Length:", len(build_set))
if len(build_set) != 107:
raise Exception("race condition!")
thread = threading.Thread(target=worker)
thread.start()
func()
thread.join()
#dis.dis(func)
I expected the code to fail randomly, but so far I failed to trigger a race condition on Free Threading. Running the code on Python built with --with-thread-sanitizer doesn't show any warning.
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
まず、提供されたスレッド付きの再現プログラムを動作確認に使い、SET_ADD と LIST_APPEND を _PySet_AddTakeRef() および set_add_entry_takeref() まで追跡します。thread-sanitized build を含む free-threaded build で実行し、critical section を省略しても安全かどうかを判断します。安全性の根拠が確立され、必要な説明または修正がすべて網羅されれば完了です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- python
- 領域
- backend
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 静か
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 48/100