[libcxx-commits] [libcxx] [libc++] Prevent std::terminate in promise<void> destructor race (PR #221258)
via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Sep 17 05:45:24 PDT 2026
https://github.com/Eamon2009 updated https://github.com/llvm/llvm-project/pull/221258
>From ec8c1e7e48491b91d67c8f15fd52c00f2a9a997b Mon Sep 17 00:00:00 2001
From: Eamon <eamon112009 at gmail.com>
Date: Fri, 4 Sep 2026 21:06:08 +0530
Subject: [PATCH 1/2] [libc++] Prevent std::terminate in promise<void>
destructor race
promise<void>::~promise() calls set_exception() to inject a broken_promise
when the promise is destroyed before the shared state is ready. That call
can throw (e.g. promise_already_satisfied if a race occurs, or bad_alloc
from make_exception_ptr). Because destructors are implicitly noexcept,
an escaping exception triggers std::terminate.
Wrap the set_exception call in a try/catch block so the destructor is
truly noexcept.
---
libcxx/src/future.cpp | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/libcxx/src/future.cpp b/libcxx/src/future.cpp
index 284b68828c7fd..a0e59732b4cd1 100644
--- a/libcxx/src/future.cpp
+++ b/libcxx/src/future.cpp
@@ -144,8 +144,12 @@ promise<void>::promise() : __state_(new __assoc_sub_state) {}
promise<void>::~promise() {
if (__state_) {
#if _LIBCPP_HAS_EXCEPTIONS
- if (!__state_->__has_value() && __state_->use_count() > 1)
- __state_->set_exception(make_exception_ptr(future_error(future_errc::broken_promise)));
+ if (!__state_->__has_value() && __state_->use_count() > 1) {
+ try {
+ __state_->set_exception(make_exception_ptr(future_error(future_errc::broken_promise)));
+ } catch (...) {
+ }
+ }
#endif // _LIBCPP_HAS_EXCEPTIONS
__state_->__release_shared();
}
>From 4cb300ce28d3fcb89e07955729a378f1e6b47357 Mon Sep 17 00:00:00 2001
From: Eamon <eamon112009 at gmail.com>
Date: Thu, 17 Sep 2026 18:15:13 +0530
Subject: [PATCH 2/2] [libc++] Prevent std::terminate in promise<void>
destructor race
promise<void>::~promise() calls set_exception() to inject a broken_promise
when the promise is destroyed before the shared state is ready. That call
can throw (e.g. promise_already_satisfied if a race occurs, or bad_alloc
from make_exception_ptr). Because destructors are implicitly noexcept,
an escaping exception triggers std::terminate.
Wrap the set_exception call in a try/catch block so the destructor is
truly noexcept.
Signed-off-by: Eamon Sippy
<eamon112009 at gmail.com>
---
libcxx/src/future.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/src/future.cpp b/libcxx/src/future.cpp
index a0e59732b4cd1..bab958260d3ca 100644
--- a/libcxx/src/future.cpp
+++ b/libcxx/src/future.cpp
@@ -28,6 +28,7 @@ string __future_error_category::message(int ev) const {
switch (static_cast<future_errc>(ev)) {
case future_errc(0): // For backwards compatibility with C++11 (LWG 2056)
case future_errc::broken_promise:
+
return string("The associated promise has been destructed prior "
"to the associated state becoming ready.");
case future_errc::future_already_retrieved:
More information about the libcxx-commits
mailing list