• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
25 namespace bs2 = boost::signals2;
26 
27 
28 // dummy mutex that detects attempts to recursively lock
29 class test_mutex
30 {
31 public:
test_mutex()32 	test_mutex(): m_locked(false) {}
lock()33 	void lock()
34 	{
35 		BOOST_CHECK(m_locked == false);
36 		m_locked = true;
37 	}
try_lock()38 	bool try_lock()
39 	{
40 		if(m_locked) return false;
41 		lock();
42 		return true;
43 	}
unlock()44 	void unlock()
45 	{
46 		m_locked = false;
47 	}
48 private:
49 	bool m_locked;
50 };
51 
52 using namespace bs2::keywords;
53 typedef bs2::signal_type<void(), mutex_type<test_mutex> >::type Signal;
54 
55 class SelfReference: private boost::noncopyable
56 {
57 public:
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 
SelfReference()64 	SelfReference()
65 	{
66 		m_signal = boost::make_shared<Signal>();
67 	}
68 
~SelfReference()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 
ReleaseSelf()82 	void ReleaseSelf()
83 	{
84 		m_self.reset();
85 	}
86 
DoNothing()87 	static void DoNothing()
88 	{
89 	}
90 
Run()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(Signal::slot_type(&SelfReference::ReleaseSelf, obj.get()).track(obj));
100 			obj->m_conDoNothing = signal->connect(Signal::slot_type(&SelfReference::DoNothing));
101 		}
102 		(*signal)();
103 	}
104 };
105 
BOOST_AUTO_TEST_CASE(test_main)106 BOOST_AUTO_TEST_CASE(test_main)
107 {
108 	SelfReference::Run();
109 }
110