Mooncake Evict: 一次 std::make_pair 让 iter_ 悄悄失效

💡 原文中文,约3500字,阅读约需9分钟。
📝

内容提要

本文讨论了 C++ 中内存池管理的一个 bug,特别是 KeyEvictInfo.iter_ 的迭代器失效问题。作者指出,虽然代码表面上看似安全,但由于使用 std::make_pair 导致的拷贝操作,实际上破坏了迭代器的有效性。这种隐蔽的语义差异使得问题难以察觉,强调了 C++ 语言的复杂性和潜在风险。

🎯

关键要点

  • KeyEvictInfo.iter_ 必须始终指向当前所在 list 的迭代器,破坏这一不变量会导致后续操作出错。

  • 在 SwapOut() 函数中,使用 std::make_pair 导致了 ok_list 和 err_list 的拷贝,而不是移动。

  • std::make_pair 的使用使得两个左值被复制到返回值中,导致 list node 的身份发生变化。

  • 虽然 shared_ptr 指向的对象没有被复制,但 list node 被复制后,iter_ 仍然指向旧的 node,导致迭代器失效。

  • C++ 的语义复杂性使得这种问题不易察觉,尤其是在编译通过的情况下,代码表面看似安全。

  • C++ 的隐式规则和细微差别增加了开发者的心智负担,容易导致错误,尤其是在处理迭代器失效的问题上。

延伸问答

C++ 中 KeyEvictInfo.iter_ 的迭代器失效问题是什么?

KeyEvictInfo.iter_ 必须始终指向当前 list 的迭代器,若破坏这一不变量,后续操作将出错。

为什么 std::make_pair 会导致迭代器失效?

std::make_pair 导致 ok_list 和 err_list 的拷贝,而不是移动,导致 list node 身份变化,迭代器失效。

如何避免 C++ 中的迭代器失效问题?

应确保使用移动语义而非拷贝,避免将左值传递给 std::make_pair,以保持迭代器的有效性。

C++ 的语义复杂性对开发者有什么影响?

C++ 的隐式规则和细微差别增加了开发者的心智负担,容易导致错误,特别是在处理迭代器失效时。

在 SwapOut() 函数中,如何处理 list 的元素?

在 SwapOut() 中,通过 splice 将元素从 pre_evict 移动到 ok_list 或 err_list,而不是使用 erase 和 insert。

为什么 C++ 的 move 语义不够直观?

C++ 的 move 语义常常被隐式规则和模板推导所掩盖,导致开发者误判代码的实际行为。

➡️

继续阅读