123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- // Signals2 library
- //
- // Regression test based on bug report from Arian Alin Radu.
- // The problem was that tracked objects could be released
- // while holding the signal mutex during signal invocation.
- // This could result in a recursive
- // lock attempt if the tracked object manipulates the signal
- // in its destructor.
- // Copyright Frank Mori Hess 2019
- // Use, modification and
- // distribution is subject to 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)
- // For more information, see http://www.boost.org
- #define BOOST_TEST_MODULE signals2 deadlock regression test
- #include <boost/test/included/unit_test.hpp>
- #include <boost/shared_ptr.hpp>
- #include <boost/make_shared.hpp>
- #include <boost/signals2/signal.hpp>
- #include <boost/signals2/signal_type.hpp>
- namespace bs2 = boost::signals2;
- // dummy mutex that detects attempts to recursively lock
- class test_mutex
- {
- public:
- test_mutex(): m_locked(false) {}
- void lock()
- {
- BOOST_CHECK(m_locked == false);
- m_locked = true;
- }
- bool try_lock()
- {
- if(m_locked) return false;
- lock();
- return true;
- }
- void unlock()
- {
- m_locked = false;
- }
- private:
- bool m_locked;
- };
- using namespace bs2::keywords;
- typedef bs2::signal_type<void(), mutex_type<test_mutex> >::type Signal;
- class SelfReference: private boost::noncopyable
- {
- public:
- boost::shared_ptr<SelfReference> m_self;
- boost::shared_ptr<Signal> m_signal;
- boost::signals2::connection m_conReleaseSelf;
- boost::signals2::connection m_conDoNothing;
- SelfReference()
- {
- m_signal = boost::make_shared<Signal>();
- }
- ~SelfReference()
- {
- // the first slot (ReleaseSelf) has been called; now the trackable object (this)
- // was released, while the second slot is locked
- BOOST_CHECK(!m_conReleaseSelf.connected());
- // the second slot is locked, and we enter a recursive (pthread: dead) lock
- BOOST_CHECK(m_conDoNothing.connected());
- m_conReleaseSelf.disconnect();
- m_conDoNothing.disconnect();
- // enter recursive (pthread: dead) lock again:
- BOOST_CHECK(m_signal->empty());
- }
- void ReleaseSelf()
- {
- m_self.reset();
- }
- static void DoNothing()
- {
- }
- static void Run()
- {
- boost::shared_ptr<Signal> signal;
- {
- boost::shared_ptr<SelfReference> obj = boost::make_shared<SelfReference>();
- obj->m_self = obj;
- signal = obj->m_signal;
- obj->m_conReleaseSelf = signal->connect(Signal::slot_type(&SelfReference::ReleaseSelf, obj.get()).track(obj));
- obj->m_conDoNothing = signal->connect(Signal::slot_type(&SelfReference::DoNothing));
- }
- (*signal)();
- }
- };
- BOOST_AUTO_TEST_CASE(test_main)
- {
- SelfReference::Run();
- }
|