1// Signals2 library
2//
3// Regression test based on bug report from Arian Alin Radu.
4// The problem was that tracked objects could be released
5// while holding the signal mutex during signal invocation.
6// This could result in a recursive
7// lock attempt if the tracked object manipulates the signal
8// in its destructor.
9
10// Copyright Frank Mori Hess 2019
11// Use, modification and
12// distribution is subject to the Boost Software License, Version
13// 1.0. (See accompanying file LICENSE_1_0.txt or copy at
14// http://www.boost.org/LICENSE_1_0.txt)
15
16// For more information, see http://www.boost.org
17
18#define BOOST_TEST_MODULE signals2 deadlock regression test
19#include <boost/test/included/unit_test.hpp>
20#include <boost/shared_ptr.hpp>
21#include <boost/make_shared.hpp>
22#include <boost/signals2/signal.hpp>
23#include <boost/signals2/signal_type.hpp>
24
25namespace bs2 = boost::signals2;
26
27
28// dummy mutex that detects attempts to recursively lock
29class test_mutex
30{
31public:
32 test_mutex(): m_locked(false) {}
33 void lock()
34 {
35 BOOST_CHECK(m_locked == false);
36 m_locked = true;
37 }
38 bool try_lock()
39 {
40 if(m_locked) return false;
41 lock();
42 return true;
43 }
44 void unlock()
45 {
46 m_locked = false;
47 }
48private:
49 bool m_locked;
50};
51
52using namespace bs2::keywords;
53typedef bs2::signal_type<void(), mutex_type<test_mutex> >::type Signal;
54
55class SelfReference: private boost::noncopyable
56{
57public:
58 boost::shared_ptr<SelfReference> m_self;
59 boost::shared_ptr<Signal> m_signal;
60
61 boost::signals2::connection m_conReleaseSelf;
62 boost::signals2::connection m_conDoNothing;
63
64 SelfReference()
65 {
66 m_signal = boost::make_shared<Signal>();
67 }
68
69 ~SelfReference()
70 {
71 // the first slot (ReleaseSelf) has been called; now the trackable object (this)
72 // was released, while the second slot is locked
73 BOOST_CHECK(!m_conReleaseSelf.connected());
74 // the second slot is locked, and we enter a recursive (pthread: dead) lock
75 BOOST_CHECK(m_conDoNothing.connected());
76 m_conReleaseSelf.disconnect();
77 m_conDoNothing.disconnect();
78 // enter recursive (pthread: dead) lock again:
79 BOOST_CHECK(m_signal->empty());
80 }
81
82 void ReleaseSelf()
83 {
84 m_self.reset();
85 }
86
87 static void DoNothing()
88 {
89 }
90
91 static void Run()
92 {
93 boost::shared_ptr<Signal> signal;
94 {
95 boost::shared_ptr<SelfReference> obj = boost::make_shared<SelfReference>();
96 obj->m_self = obj;
97 signal = obj->m_signal;
98
99 obj->m_conReleaseSelf = signal->connect(slot: Signal::slot_type(&SelfReference::ReleaseSelf, obj.get()).track(tracked: obj));
100 obj->m_conDoNothing = signal->connect(slot: Signal::slot_type(&SelfReference::DoNothing));
101 }
102 (*signal)();
103 }
104};
105
106BOOST_AUTO_TEST_CASE(test_main)
107{
108 SelfReference::Run();
109}
110

source code of boost/libs/signals2/test/deadlock_regression_test.cpp