python / python/cpython

Potential Deadlock in _Py_qsbr_reserve

未关闭
#148,953 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

interpreter-core topic-free-threading type-crash
主要语言
Python
星标
77.2k
派生
35.9k
PR 合并指标
PR 指标待抓取

描述

Crash report

What happened?

Hello

I don't have a good unit test for it. I'm running a server under free-threading 3.14.3t build. On a larger QPS, I see a rapid unstoppable RAM increase, while CPU load is okay.

Based on a dump of active threads, I suspect a deadlock somewhere around _Py_qsbr_reserve and Stop the World:

E.g.

  • This thread successfully initiated the Stop The World event and is waiting for all other threads to acknowledge and pause (park).
    • stop_the_world indicates it is the one trying to stop everything.
    • PyEvent_WaitTimed shows it is sitting there waiting for a signal that everyone has stopped.
    do_futex_wait
    __new_sem_wait_slow
    _PySemaphore_Wait
    _PyParkingLot_Park
    PyEvent_WaitTimed
    stop_the_world
    type_set_abstractmethods
    type_setattro
    PyObject_SetAttr
    _abc__abc_init
    ... etc
    
  • This thread also needed to stop the world (to grow an internal array) but got blocked because anotheer already held the master lock.
    • _Py_qsbr_reserve shows it was trying to reserve space in the memory management system.
    • _PyMutex_LockTimed shows it is blocked waiting for a lock inside stop_the_world. This is the lock held by Thread 1.
    do_futex_wait
    __new_sem_wait_slow
    _PySemaphore_Wait
    _PyParkingLot_Park
    _PyMutex_LockTimed
    stop_the_world
    _Py_qsbr_reserve
    PyGILState_Ensure
    ... etc
    

I suspect a modification of _Py_qsbr_reserve could help, the server thread dump does not complain about waiting on Stop the World after this modification:

Py_ssize_t
_Py_qsbr_reserve(PyInterpreterState *interp)
{
    struct _qsbr_shared *shared = &interp->qsbr;

    PyMutex_Lock(&shared->mutex);
    // Try allocating from our internal freelist
    struct _qsbr_thread_state *qsbr = qsbr_allocate(shared);

    while (qsbr == NULL) {
        // Unlock before stopping the world to avoid deadlocks.
        // If we hold shared->mutex while waiting for the world to stop,
        // we might block a thread that needs to acquire shared->mutex to park.
        PyMutex_Unlock(&shared->mutex);
        _PyEval_StopTheWorld(interp);
        PyMutex_Lock(&shared->mutex);

        // Try allocating again, as another thread might have grown the array
        // or freed an entry while we were waiting.
        qsbr = qsbr_allocate(shared);
        if (qsbr != NULL) {
            _PyEval_StartTheWorld(interp);
            break;
        }

        // Still NULL, we must grow it
        if (grow_thread_array(shared) == 0) {
            qsbr = qsbr_allocate(shared);
        } else {
            // Failed to grow array (e.g. OOM). Break to avoid infinite loop.
            _PyEval_StartTheWorld(interp);
            break;
        }
        _PyEval_StartTheWorld(interp);
    }

    // Return an index rather than the pointer because the array may be
    // resized and the pointer invalidated.
    Py_ssize_t index = -1;
    if (qsbr != NULL) {
        index = (struct _qsbr_pad *)qsbr - shared->array;
    }
    PyMutex_Unlock(&shared->mutex);
    return index;
}

Similar issues in the past:

CPython versions tested on:

3.14

Operating systems tested on:

Linux

Output from running 'python -VV' on the command line:

3.14.3 (free-threading)

Linked PRs
  • gh-149100

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

首先检查链接的 PR gh-149100,然后根据报告的线程堆栈跟踪 _Py_qsbr_reserve 和 stop_the_world。该 Issue 未提供指定的源文件、单元测试或可靠的复现程序;要完成这项工作,需要确认死锁机制,并在 free-threading 构建中验证修复方案。

由索引模型根据 Issue 内容生成。

评估

技术栈
c
领域
operating-systems
Issue 类型
缺陷
难度
5/5
预计耗时
一周以上
活跃度
停滞
描述清晰度
需要澄清
新手友好度
25/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。