From f965298154f38bd0f02778bee64932364847b4a3 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Mon, 24 Jul 2023 18:29:30 +0200 Subject: [PATCH] added reentrancy checking --- .../unordered/detail/foa/concurrent_table.hpp | 16 ++- .../unordered/detail/foa/reentrancy_check.hpp | 134 ++++++++++++++++++ test/Jamfile.v2 | 1 + test/cfoa/reentrancy_check_test.cpp | 68 +++++++++ 4 files changed, 212 insertions(+), 7 deletions(-) create mode 100644 include/boost/unordered/detail/foa/reentrancy_check.hpp create mode 100644 test/cfoa/reentrancy_check_test.cpp diff --git a/include/boost/unordered/detail/foa/concurrent_table.hpp b/include/boost/unordered/detail/foa/concurrent_table.hpp index 0f1f1145..b79f70c6 100644 --- a/include/boost/unordered/detail/foa/concurrent_table.hpp +++ b/include/boost/unordered/detail/foa/concurrent_table.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -837,9 +838,10 @@ public: private: using mutex_type=rw_spinlock; using multimutex_type=multimutex; // TODO: adapt 128 to the machine - using shared_lock_guard=shared_lock; - using exclusive_lock_guard=lock_guard; - using exclusive_bilock_guard=scoped_bilock; + using shared_lock_guard=reentrancy_checked>; + using exclusive_lock_guard=reentrancy_checked>; + using exclusive_bilock_guard= + reentrancy_checked>>; using group_shared_lock_guard=typename group_access::shared_lock_guard; using group_exclusive_lock_guard=typename group_access::exclusive_lock_guard; using group_insert_counter_type=typename group_access::insert_counter_type; @@ -859,18 +861,18 @@ private: { thread_local auto id=(++thread_counter)%mutexes.size(); - return shared_lock_guard{mutexes[id]}; + return shared_lock_guard{this,mutexes[id]}; } inline exclusive_lock_guard exclusive_access()const { - return exclusive_lock_guard{mutexes}; + return exclusive_lock_guard{this,mutexes}; } static inline exclusive_bilock_guard exclusive_access( const concurrent_table& x,const concurrent_table& y) { - return {x.mutexes,y.mutexes}; + return {&x,&y,x.mutexes,y.mutexes}; } template @@ -878,7 +880,7 @@ private: const concurrent_table& x, const concurrent_table& y) { - return {x.mutexes,y.mutexes}; + return {&x,&y,x.mutexes,y.mutexes}; } /* Tag-dispatched shared/exclusive group access */ diff --git a/include/boost/unordered/detail/foa/reentrancy_check.hpp b/include/boost/unordered/detail/foa/reentrancy_check.hpp new file mode 100644 index 00000000..289d161c --- /dev/null +++ b/include/boost/unordered/detail/foa/reentrancy_check.hpp @@ -0,0 +1,134 @@ +/* Copyright 2023 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_DETAIL_FOA_REENTRANCY_CHECK_HPP +#define BOOST_UNORDERED_DETAIL_FOA_REENTRANCY_CHECK_HPP + +#include +#include + +#if !defined(BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK) +#if defined(BOOST_UNORDERED_ENABLE_REENTRANCY_CHECK_HANDLER)|| \ + (defined(BOOST_UNORDERED_ENABLE_REENTRANCY_CHECK_DEBUG_HANDLER)&& \ + !defined(NDEBUG))|| \ + !defined(BOOST_ASSERT_IS_VOID) +#define BOOST_UNORDERED_REENTRANCY_CHECK +#endif +#endif + +#if defined(BOOST_UNORDERED_REENTRANCY_CHECK) +#if defined(BOOST_UNORDERED_ENABLE_REENTRANCY_CHECK_HANDLER)|| \ + defined(BOOST_UNORDERED_ENABLE_REENTRANCY_CHECK_DEBUG_HANDLER) + +#include /* BOOST_LIKELY */ + +namespace boost +{ + void boost_unordered_reentrancy_check_failed(); +} + +#define BOOST_UNORDERED_REENTRANCY_CHECK_ASSERT_MSG(expr,msg) \ +(BOOST_LIKELY(!!(expr))?((void)0): \ + ::boost::boost_unordered_reentrancy_check_failed()) + +#else + +#define BOOST_UNORDERED_REENTRANCY_CHECK_ASSERT_MSG(expr,msg) \ +BOOST_ASSERT_MSG(expr,msg) + +#endif +#endif + +namespace boost{ +namespace unordered{ +namespace detail{ +namespace foa{ + +#if defined(BOOST_UNORDERED_REENTRANCY_CHECK) + +class entry_trace +{ +public: + entry_trace(const void* px_):px{px_} + { + BOOST_ASSERT(px!=nullptr); + BOOST_UNORDERED_REENTRANCY_CHECK_ASSERT_MSG( + !find(px),"reentrancy not allowed"); + header()=this; + } + + entry_trace(const entry_trace&)=delete; + entry_trace& operator=(const entry_trace&)=delete; + ~entry_trace(){clear();} + + void clear() + { + if(px){ + header()=next; + px=nullptr; + } + } + +private: + static entry_trace*& header() + { + thread_local entry_trace *pe=nullptr; + return pe; + } + + static bool find(const void* px) + { + for(auto pe=header();pe;pe=pe->next){ + if(pe->px==px)return true; + } + return false; + } + + const void *px; + entry_trace *next=header(); +}; + +template +struct reentrancy_checked +{ + template + reentrancy_checked(const void* px,Args&&... args): + tr{px},lck{std::forward(args)...}{} + + void unlock() + { + lck.unlock(); + tr.clear(); + } + + entry_trace tr; + LockGuard lck; +}; + +#else + +template +struct reentrancy_checked +{ + template + reentrancy_checked(const void*,Args&&... args): + lck{std::forward(args)...}{} + + void unlock(){lck.unlock();} + + LockGuard lck; +}; + +#endif + +} /* namespace foa */ +} /* namespace detail */ +} /* namespace unordered */ +} /* namespace boost */ + +#endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e25f1083..dda83b68 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -202,6 +202,7 @@ local CFOA_TESTS = rw_spinlock_test6 rw_spinlock_test7 rw_spinlock_test8 + reentrancy_check_test ; for local test in $(CFOA_TESTS) diff --git a/test/cfoa/reentrancy_check_test.cpp b/test/cfoa/reentrancy_check_test.cpp new file mode 100644 index 00000000..09377459 --- /dev/null +++ b/test/cfoa/reentrancy_check_test.cpp @@ -0,0 +1,68 @@ +// Copyright 2023 Joaquin M Lopez Munoz +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#define BOOST_UNORDERED_ENABLE_REENTRANCY_CHECK_HANDLER + +static bool reentrancy_detected = false; + +namespace boost { + // Caveat lector: a proper handler should terminate as it may be executed + // within a noexcept function. + + void boost_unordered_reentrancy_check_failed() + { + reentrancy_detected = true; + throw 0; + } +} + +#include +#include + +template +void detect_reentrancy(F f) +{ + reentrancy_detected = false; + try { + f(); + } + catch(int) {} + BOOST_TEST(reentrancy_detected); +} + +int main() +{ + using map = boost::concurrent_flat_map; + using value_type = typename map::value_type; + + map m1, m2; + m1.emplace(0, 0); + m2.emplace(1, 0); + + detect_reentrancy([&] { + m1.visit_all([&](value_type&) { (void)m1.contains(0); }); + }); + + detect_reentrancy([&] { + m1.visit_all([&](value_type&) { m1.rehash(0); }); + }); + + detect_reentrancy([&] { + m1.visit_all([&](value_type&) { + m2.visit_all([&](value_type&) { + m1=m2; + }); + }); + }); + + detect_reentrancy([&] { + m1.visit_all([&](value_type&) { + m2.visit_all([&](value_type&) { + m2=m1; + }); + }); + }); + + return boost::report_errors(); +}