deadlock_regression_test.cpp 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  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. // Copyright Frank Mori Hess 2019
  10. // Use, modification and
  11. // distribution is subject to the Boost Software License, Version
  12. // 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  13. // http://www.boost.org/LICENSE_1_0.txt)
  14. // For more information, see http://www.boost.org
  15. #define BOOST_TEST_MODULE signals2 deadlock regression test
  16. #include <boost/test/included/unit_test.hpp>
  17. #include <boost/shared_ptr.hpp>
  18. #include <boost/make_shared.hpp>
  19. #include <boost/signals2/signal.hpp>
  20. #include <boost/signals2/signal_type.hpp>
  21. namespace bs2 = boost::signals2;
  22. // dummy mutex that detects attempts to recursively lock
  23. class test_mutex
  24. {
  25. public:
  26. test_mutex(): m_locked(false) {}
  27. void lock()
  28. {
  29. BOOST_CHECK(m_locked == false);
  30. m_locked = true;
  31. }
  32. bool try_lock()
  33. {
  34. if(m_locked) return false;
  35. lock();
  36. return true;
  37. }
  38. void unlock()
  39. {
  40. m_locked = false;
  41. }
  42. private:
  43. bool m_locked;
  44. };
  45. using namespace bs2::keywords;
  46. typedef bs2::signal_type<void(), mutex_type<test_mutex> >::type Signal;
  47. class SelfReference: private boost::noncopyable
  48. {
  49. public:
  50. boost::shared_ptr<SelfReference> m_self;
  51. boost::shared_ptr<Signal> m_signal;
  52. boost::signals2::connection m_conReleaseSelf;
  53. boost::signals2::connection m_conDoNothing;
  54. SelfReference()
  55. {
  56. m_signal = boost::make_shared<Signal>();
  57. }
  58. ~SelfReference()
  59. {
  60. // the first slot (ReleaseSelf) has been called; now the trackable object (this)
  61. // was released, while the second slot is locked
  62. BOOST_CHECK(!m_conReleaseSelf.connected());
  63. // the second slot is locked, and we enter a recursive (pthread: dead) lock
  64. BOOST_CHECK(m_conDoNothing.connected());
  65. m_conReleaseSelf.disconnect();
  66. m_conDoNothing.disconnect();
  67. // enter recursive (pthread: dead) lock again:
  68. BOOST_CHECK(m_signal->empty());
  69. }
  70. void ReleaseSelf()
  71. {
  72. m_self.reset();
  73. }
  74. static void DoNothing()
  75. {
  76. }
  77. static void Run()
  78. {
  79. boost::shared_ptr<Signal> signal;
  80. {
  81. boost::shared_ptr<SelfReference> obj = boost::make_shared<SelfReference>();
  82. obj->m_self = obj;
  83. signal = obj->m_signal;
  84. obj->m_conReleaseSelf = signal->connect(Signal::slot_type(&SelfReference::ReleaseSelf, obj.get()).track(obj));
  85. obj->m_conDoNothing = signal->connect(Signal::slot_type(&SelfReference::DoNothing));
  86. }
  87. (*signal)();
  88. }
  89. };
  90. BOOST_AUTO_TEST_CASE(test_main)
  91. {
  92. SelfReference::Run();
  93. }