From 11973c64437683cc99c48eae9eb4db8234f1ac42 Mon Sep 17 00:00:00 2001
From: Sylvain Munaut <tnt@246tNt.com>
Date: Wed, 27 Jan 2016 11:24:19 +0100
Subject: gnuradio-runtime: Fix sptr magic when exception thrown in hier2
 constructor

Fixes #528

Previously, if an exception is thrown in constructor of a hier_block2
subclass, then :
 - The hier_block2 base destructor _will_ be called
 - The actual object is destroyed
 - But the initial sptr would be left in the static map and point to
   an invalid object
 - Whatever connection() were made might have an invalid sptr ref as well

So to fix this:
 - In the hier_block2 destructor, we explicitely disconnect() everything
 - In the base hier_block2 destructor, we call a new 'cancel sptr' method
   that will check if this object is still in the static map or not
   - If it's not: Then this is a legit call to the destructor by shared_ptr
     and everything is fine
   - If it's: Then there was an isue and the object is already being
     destroyed and we need to make sure shared_ptr doesn't try to do it
     again. We do this using a special 'disarmable' custom deleter.

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
---
 gnuradio-runtime/lib/sptr_magic.cc | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

(limited to 'gnuradio-runtime/lib/sptr_magic.cc')

diff --git a/gnuradio-runtime/lib/sptr_magic.cc b/gnuradio-runtime/lib/sptr_magic.cc
index 70596abb05..e5e83722fc 100644
--- a/gnuradio-runtime/lib/sptr_magic.cc
+++ b/gnuradio-runtime/lib/sptr_magic.cc
@@ -36,14 +36,47 @@ namespace gnuradio {
   typedef std::map<gr::basic_block*, gr::basic_block_sptr> sptr_map;
   static sptr_map s_map;
 
+  struct disarmable_deleter
+  {
+    bool armed;
+
+    disarmable_deleter()
+    {
+      armed = true;
+    }
+
+    void operator()(void *p) const
+    {
+      if (armed)
+        delete static_cast<gr::basic_block *>(p);
+    }
+
+    void disarm()
+    {
+      armed = false;
+    }
+  };
+
   void
   detail::sptr_magic::create_and_stash_initial_sptr(gr::hier_block2 *p)
   {
-    gr::basic_block_sptr sptr(p);
+    gr::basic_block_sptr sptr(p, disarmable_deleter());
     gr::thread::scoped_lock guard(s_mutex);
     s_map.insert(sptr_map::value_type(static_cast<gr::basic_block *>(p), sptr));
   }
 
+  void
+  detail::sptr_magic::cancel_initial_sptr(gr::hier_block2 *p)
+  {
+    gr::thread::scoped_lock guard(s_mutex);
+    sptr_map::iterator pos = s_map.find(static_cast<gr::basic_block *>(p));
+    if(pos == s_map.end())
+      return; /* Not in the map, nothing to do */
+    gr::basic_block_sptr sptr = pos->second;
+    s_map.erase(pos);
+    boost::get_deleter<disarmable_deleter, gr::basic_block>(sptr)->disarm();
+  }
+
   gr::basic_block_sptr
   detail::sptr_magic::fetch_initial_sptr(gr::basic_block *p)
   {
-- 
cgit v1.2.3