added reentrancy checking

This commit is contained in:
joaquintides
2023-07-24 18:29:30 +02:00
parent bd24dfd284
commit f965298154
4 changed files with 212 additions and 7 deletions

View File

@ -20,6 +20,7 @@
#include <boost/mp11/tuple.hpp>
#include <boost/static_assert.hpp>
#include <boost/unordered/detail/foa/core.hpp>
#include <boost/unordered/detail/foa/reentrancy_check.hpp>
#include <boost/unordered/detail/foa/rw_spinlock.hpp>
#include <boost/unordered/detail/foa/tuple_rotate_right.hpp>
#include <cstddef>
@ -837,9 +838,10 @@ public:
private:
using mutex_type=rw_spinlock;
using multimutex_type=multimutex<mutex_type,128>; // TODO: adapt 128 to the machine
using shared_lock_guard=shared_lock<mutex_type>;
using exclusive_lock_guard=lock_guard<multimutex_type>;
using exclusive_bilock_guard=scoped_bilock<multimutex_type>;
using shared_lock_guard=reentrancy_checked<shared_lock<mutex_type>>;
using exclusive_lock_guard=reentrancy_checked<lock_guard<multimutex_type>>;
using exclusive_bilock_guard=
reentrancy_checked<reentrancy_checked<scoped_bilock<multimutex_type>>>;
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<typename Hash2,typename Pred2>
@ -878,7 +880,7 @@ private:
const concurrent_table& x,
const concurrent_table<TypePolicy,Hash2,Pred2,Allocator>& y)
{
return {x.mutexes,y.mutexes};
return {&x,&y,x.mutexes,y.mutexes};
}
/* Tag-dispatched shared/exclusive group access */

View File

@ -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 <boost/assert.hpp>
#include <utility>
#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/config.hpp> /* 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<typename LockGuard>
struct reentrancy_checked
{
template<typename... Args>
reentrancy_checked(const void* px,Args&&... args):
tr{px},lck{std::forward<Args>(args)...}{}
void unlock()
{
lck.unlock();
tr.clear();
}
entry_trace tr;
LockGuard lck;
};
#else
template<typename LockGuard>
struct reentrancy_checked
{
template<typename... Args>
reentrancy_checked(const void*,Args&&... args):
lck{std::forward<Args>(args)...}{}
void unlock(){lck.unlock();}
LockGuard lck;
};
#endif
} /* namespace foa */
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif

View File

@ -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)

View File

@ -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 <boost/unordered/concurrent_flat_map.hpp>
#include <boost/core/lightweight_test.hpp>
template<typename F>
void detect_reentrancy(F f)
{
reentrancy_detected = false;
try {
f();
}
catch(int) {}
BOOST_TEST(reentrancy_detected);
}
int main()
{
using map = boost::concurrent_flat_map<int, int>;
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();
}