summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2023-12-18 12:37:32 -0800
committerGravatar Copybara-Service <copybara-worker@google.com>2023-12-18 12:38:18 -0800
commit8900d7c496afb0fb0e0a737d001a46828bc446dd (patch)
treec89237017baee1e83f54a7f09ff7c4ddf9bbe664
parentd22aa4df3f530872d4e11e3906c9c6e002deefca (diff)
Mutex: Fix Condition pointer-to-member cast to respect const qualifier
Previously, `absl::Condition` incorrectly used the same (non-`const`) pointer-to-method type when wrapping both `const` and non-`const` methods. Unfortunately, this is undefined behavior according to `[expr.reinterpret.cast]` in the C++ standard: > The effect of calling a function through a pointer to a function type that is > not the same as the type used in the definition of the function is undefined. This fixes the UB. PiperOrigin-RevId: 591981682 Change-Id: Iaca955346699417232383d3a1800ea9b82ea5761
-rw-r--r--absl/synchronization/mutex.h14
-rw-r--r--absl/synchronization/mutex_test.cc9
2 files changed, 16 insertions, 7 deletions
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h
index 157b7050..85e8d0e0 100644
--- a/absl/synchronization/mutex.h
+++ b/absl/synchronization/mutex.h
@@ -848,7 +848,7 @@ class Condition {
static bool CallVoidPtrFunction(const Condition*);
template <typename T>
static bool CastAndCallFunction(const Condition* c);
- template <typename T>
+ template <typename T, typename ConditionMethodPtr>
static bool CastAndCallMethod(const Condition* c);
// Helper methods for storing, validating, and reading callback arguments.
@@ -1080,12 +1080,12 @@ inline void Mutex::Dtor() {}
inline CondVar::CondVar() : cv_(0) {}
// static
-template <typename T>
+template <typename T, typename ConditionMethodPtr>
bool Condition::CastAndCallMethod(const Condition* c) {
T* object = static_cast<T*>(c->arg_);
- bool (T::*method_pointer)();
- c->ReadCallback(&method_pointer);
- return (object->*method_pointer)();
+ ConditionMethodPtr condition_method_pointer;
+ c->ReadCallback(&condition_method_pointer);
+ return (object->*condition_method_pointer)();
}
// static
@@ -1115,7 +1115,7 @@ inline Condition::Condition(bool (*func)(T*),
template <typename T>
inline Condition::Condition(T* object,
bool (absl::internal::identity<T>::type::*method)())
- : eval_(&CastAndCallMethod<T>), arg_(object) {
+ : eval_(&CastAndCallMethod<T, decltype(method)>), arg_(object) {
static_assert(sizeof(&method) <= sizeof(callback_),
"An overlarge method pointer was passed to Condition.");
StoreCallback(method);
@@ -1125,7 +1125,7 @@ template <typename T>
inline Condition::Condition(const T* object,
bool (absl::internal::identity<T>::type::*method)()
const)
- : eval_(&CastAndCallMethod<T>),
+ : eval_(&CastAndCallMethod<const T, decltype(method)>),
arg_(reinterpret_cast<void*>(const_cast<T*>(object))) {
StoreCallback(method);
}
diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc
index 35b43334..a8d75827 100644
--- a/absl/synchronization/mutex_test.cc
+++ b/absl/synchronization/mutex_test.cc
@@ -977,6 +977,15 @@ TEST(Mutex, FunctionPointerConditionWithDerivedToBaseConversion) {
const Derived *>::value));
}
+struct Constable {
+ bool WotsAllThisThen() const { return true; }
+};
+
+TEST(Mutex, FunctionPointerConditionWithConstMethod) {
+ const Constable chapman;
+ EXPECT_TRUE(absl::Condition(&chapman, &Constable::WotsAllThisThen).Eval());
+}
+
struct True {
template <class... Args>
bool operator()(Args...) const {