#3971·dash

[BUG] Dash._setup_server() 中的竞争条件:保护的标志在其所保护的工作之前就已被设置

作者: a-tram创建于 2026年9月3日更新于 2026年9月10日
标签bugP2size: 5

Dash._setup_server() 运行作为一个 before_request 钩子,并且意在每个进程执行一次。它在执行保护该标志的工作之前设置其保护标志:

python
def _setup_server(self):                                    # dash.py:1702
    if self._got_first_request["setup_server"]:
        return                                              # <-- thread B 在这里离开
    self._got_first_request["setup_server"] = True          # <-- 标志在执行工作之前设置
    ...
    _validate.validate_layout(self.layout, self._layout_value())   # 1724
    self._generate_scripts_html()                                  # 1726, 填充 registered_paths
    ...
    self.callback_map[k] = _callback.GLOBAL_CALLBACK_MAP.pop(k)    # 1737, 填充 callback_map

这是一个 TOCTOU。在任何 WSGI 服务器上,该服务器在单个进程中同时处理多个请求 - gunicorn -k gthread --threads N,waitress,werkzeug 使用 threaded=True,uWSGI 使用线程 - 一个第二个线程可以在第一个线程仍然处于 _setup_server 中时进入,找到已经设置的标志,立即返回,然后读取第一个线程尚未完成写入的状态。它只影响 进程启动后的第一次页面加载,并且刷新使其消失,这就是为什么很容易将其视为缓存或网络故障。下面的两个症状均由进一步链接的脚本可靠地重现。