[BUG]: Document MSVC `std::mutex` ABI hazard with older `msvcp140.dll` and `_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR`
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 2/5
- 预计耗时
- 1-3 小时
- 新手友好度
- 68/100
- Issue 类型
- 文档
- 描述清晰度
- 基本清楚
- 活跃度
- 冷清
调研方向
从 Windows 或编译文档开始,审阅此 issue 中建议的 MSVC std::mutex 兼容性文本。记录针对受影响扩展目标的 _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR workaround,包括 CMake 和 setuptools 示例,并说明如何使用 llvm-objdump 进行验证;当指南涵盖风险、权衡以及预期的导入表符号时即可完成。
由索引模型根据 Issue 内容生成。
描述
Required prerequisites
- Make sure you've read the documentation. Your issue may be addressed there.
- Search the issue tracker and Discussions to verify that this hasn't already been reported. +1 or comment there if it has.
- Consider asking first in the Gitter chat room or in a Discussion.
What version (or hash if on master) of pybind11 are you using?
3.0.4
Problem description
We recently debugged a real downstream Windows crash where a pybind11-based extension was built with newer MSVC STL headers, but at runtime its std::mutex lock path resolved to an older msvcp140.dll that had been preloaded by another Python wheel.
Microsoft documents _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR as the compatibility workaround for this newer-toolset / older-redistributable mismatch. It would be useful for pybind11’s Windows build documentation to mention this for projects distributing MSVC-built binary wheels.
The hazard
With newer VS 2022 MSVC STL headers, std::mutex can use a constexpr zero-initialized layout. The constructor may emit no runtime _Mtx_init_in_situ call; later lock operations call _Mtx_lock from msvcp140.dll.
If another wheel in the same Python process has already loaded an older msvcp140.dll under the normal DLL name, the Windows loader may bind the extension’s _Mtx_lock import to that older runtime. The older runtime may expect the pre-constexpr mutex layout, so it can interpret the newer zero-initialized object incorrectly and crash with an access violation.
In the observed case, the first affected lock happened inside pybind11 import-time internals initialization:
PYBIND11_MODULE_PYINIT
-> pybind11::detail::ensure_internals()
-> pybind11::detail::get_internals()
-> internals_pp_manager::create_pp_content_once()
-> std::lock_guard<std::mutex> lock(pp_set_mutex_)
But the issue class is broader than that one lock: any std::mutex compiled into the extension can be affected if construction and locking are interpreted by incompatible MSVC STL runtime layouts.
Real-world incident
- Incident: metaopt/optree#278
- Downstream fix: metaopt/optree#279
The crash dump showed:
msvcp140!mtx_do_lock+0x74
optree._C!PyInit__C+0xa098
optree._C+0x4e29
optree._C!PyInit__C+0x3d
The faulting address was inside pyarrow’s bundled msvcp140.dll 14.28.29334.0. The affected wheel’s import table showed _Mtx_lock / _Mtx_unlock, but not _Mtx_init_in_situ.
After defining _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR for the extension target, the built .pyd import table included:
_Mtx_lock
_Mtx_init_in_situ
_Mtx_unlock
The original reporter confirmed that the patched wheel imports cleanly in the same environment without the previous import-order workaround.
Suggested documentation text
A note like this could live in the Windows / compiling docs:
MSVC
std::mutexcompatibility with oldermsvcp140.dllWindows extension modules built with newer MSVC STL headers and dynamic CRT linking may be loaded into Python processes where another wheel has already loaded an older
msvcp140.dll.If the extension uses
std::mutex, this can produce an MSVC STL mutex ABI mismatch: the extension’s mutex object is constructed using the newer header layout, but_Mtx_lockresolves at runtime to an oldermsvcp140.dllimplementation that expects the older layout. One symptom is an access violation inmsvcp140!mtx_do_lockduring module import.Microsoft provides
_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTORas a compatibility workaround. Projects affected by this can define it for their extension target:if(MSVC) target_compile_definitions(your_module PRIVATE _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR) endif()For setuptools:
import sys from setuptools import Extension ext = Extension( 'your_module', sources=[...], define_macros=( [('_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR', None)] if sys.platform == 'win32' else [] ), )You can verify that the macro affected the build by inspecting the
.pydimport table:llvm-objdump -p your_module.cp*-win_amd64.pyd | grep _Mtx_Expected output includes
_Mtx_init_in_situalongside_Mtx_lock/_Mtx_unlock._Mtx_destroy_in_situis not expected.Trade-offs: mutex construction is no longer a constexpr no-op, and
constinit std::mutexpatterns are disabled inside that target. For typical extension modules this is usually negligible, but projects should apply the macro at the extension-target level rather than globally.
Reproducible example code
Is this a regression? Put the last known working version here if it is.
Not a regression
- 主要语言
- C++
- 星标
- 18k
- 派生
- 2.3k
- 平均合并
- 5 天 17 小时
- 30 天内合并 PR
- 8
环境准备
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
pybind/pybind11 的其他 Issue
-
triage
难度 1/5 1 小时以内 新手友好度 78/100
维护者通常 1 天内回复
-
triage
难度 1/5 1 小时以内 新手友好度 62/100
pybind/pybind11#5604 · 1 条评论 ·
维护者通常 1 天内回复
-
triage
难度 2/5 1-3 小时 新手友好度 62/100
维护者通常 1 天内回复
-
docs
难度 2/5 1-3 小时 新手友好度 60/100
维护者通常 1 天内回复
-
难度 3/5 1-2 天 新手友好度 55/100
维护者通常 1 天内回复
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 78/100
hyprwm/aquamarine#426 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 62/100
amnezia-vpn/amnezia-client#3222 ·
维护者通常 2 天内回复
-
难度 2/5 1-3 小时 新手友好度 86/100
valkey-io/valkey-search#1465 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 78/100
KhronosGroup/Vulkan-Tutorial#524 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 88/100
microsoft/onnxruntime-genai#2633 ·
维护者通常 1 天内回复