summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Torborg <storborg@gmail.com>2018-07-14 18:56:50 -0700
committerMartin Braun <martin.braun@ettus.com>2020-01-05 16:07:47 -0800
commitb0a26eeb4d8a4e0233f30e7137ec4c13391ca2b1 (patch)
tree625ceabd8dbd7f5e5061309ce6e7a3fe543f1181
parent0c579fba83d55e2ee4acc8362a1be2beae86ad56 (diff)
Add a top_block parameter to control exception handling
This restores past behavior where the scheduler catches exceptions raised in block threads, allowing flowgraphs to continue running after the failure of an individual block. It also adds optional new behavior, selected by setting catch_exceptions=False to the top block, which causes exceptions to not be caught. In this mode of operation, a std::terminate handler can be installed to print a stack trace before the flowgraph exits.
-rw-r--r--gnuradio-runtime/include/gnuradio/thread/thread_body_wrapper.h31
-rw-r--r--gnuradio-runtime/include/gnuradio/top_block.h8
-rw-r--r--gnuradio-runtime/lib/scheduler.cc6
-rw-r--r--gnuradio-runtime/lib/scheduler.h2
-rw-r--r--gnuradio-runtime/lib/scheduler_tpb.cc15
-rw-r--r--gnuradio-runtime/lib/scheduler_tpb.h6
-rw-r--r--gnuradio-runtime/lib/top_block.cc8
-rw-r--r--gnuradio-runtime/lib/top_block_impl.cc22
-rw-r--r--gnuradio-runtime/lib/top_block_impl.h3
-rw-r--r--gnuradio-runtime/python/gnuradio/gr/qa_uncaught_exception.py54
-rw-r--r--gnuradio-runtime/python/gnuradio/gr/top_block.py4
-rw-r--r--gnuradio-runtime/swig/top_block.i4
12 files changed, 106 insertions, 57 deletions
diff --git a/gnuradio-runtime/include/gnuradio/thread/thread_body_wrapper.h b/gnuradio-runtime/include/gnuradio/thread/thread_body_wrapper.h
index c25aad9af4..22e5aa3015 100644
--- a/gnuradio-runtime/include/gnuradio/thread/thread_body_wrapper.h
+++ b/gnuradio-runtime/include/gnuradio/thread/thread_body_wrapper.h
@@ -38,9 +38,13 @@ class thread_body_wrapper
private:
F d_f;
std::string d_name;
+ bool d_catch_exceptions;
public:
- explicit thread_body_wrapper(F f, const std::string& name = "") : d_f(f), d_name(name)
+ explicit thread_body_wrapper(F f,
+ const std::string& name = "",
+ bool catch_exceptions = true)
+ : d_f(f), d_name(name), d_catch_exceptions(catch_exceptions)
{
}
@@ -48,14 +52,25 @@ public:
{
mask_signals();
- try {
- d_f();
- }
- catch(boost::thread_interrupted const &)
- {
+ if (d_catch_exceptions) {
+ try {
+ d_f();
+ } catch (boost::thread_interrupted const&) {
+ } catch (std::exception const& e) {
+ std::cerr << "thread[" << d_name << "]: " << e.what() << std::endl;
+ } catch (...) {
+ std::cerr << "thread[" << d_name << "]: "
+ << "caught unrecognized exception\n";
+ }
+
+ } else {
+ try {
+ d_f();
+ } catch (boost::thread_interrupted const&) {
+ }
}
- }
- };
+ }
+};
} /* namespace thread */
} /* namespace gr */
diff --git a/gnuradio-runtime/include/gnuradio/top_block.h b/gnuradio-runtime/include/gnuradio/top_block.h
index d0e62d1aaf..a097a65f42 100644
--- a/gnuradio-runtime/include/gnuradio/top_block.h
+++ b/gnuradio-runtime/include/gnuradio/top_block.h
@@ -30,7 +30,8 @@ namespace gr {
class top_block_impl;
-GR_RUNTIME_API top_block_sptr make_top_block(const std::string& name);
+GR_RUNTIME_API top_block_sptr make_top_block(const std::string& name,
+ bool catch_exceptions = true);
/*!
*\brief Top-level hierarchical block representing a flowgraph
@@ -39,12 +40,13 @@ GR_RUNTIME_API top_block_sptr make_top_block(const std::string& name);
class GR_RUNTIME_API top_block : public hier_block2
{
private:
- friend GR_RUNTIME_API top_block_sptr make_top_block(const std::string& name);
+ friend GR_RUNTIME_API top_block_sptr make_top_block(const std::string& name,
+ bool catch_exceptions);
top_block_impl* d_impl;
protected:
- top_block(const std::string& name);
+ top_block(const std::string& name, bool catch_exceptions = true);
public:
~top_block();
diff --git a/gnuradio-runtime/lib/scheduler.cc b/gnuradio-runtime/lib/scheduler.cc
index f054e4c753..b0e1f67dff 100644
--- a/gnuradio-runtime/lib/scheduler.cc
+++ b/gnuradio-runtime/lib/scheduler.cc
@@ -27,7 +27,11 @@
namespace gr {
-scheduler::scheduler(flat_flowgraph_sptr ffg, int max_noutput_items) {}
+scheduler::scheduler(flat_flowgraph_sptr ffg,
+ int max_noutput_items,
+ bool catch_exceptions)
+{
+}
scheduler::~scheduler() {}
diff --git a/gnuradio-runtime/lib/scheduler.h b/gnuradio-runtime/lib/scheduler.h
index 8041771f3c..e76b2ac005 100644
--- a/gnuradio-runtime/lib/scheduler.h
+++ b/gnuradio-runtime/lib/scheduler.h
@@ -48,7 +48,7 @@ public:
* The scheduler will continue running until all blocks
* report that they are done or the stop method is called.
*/
- scheduler(flat_flowgraph_sptr ffg, int max_noutput_items);
+ scheduler(flat_flowgraph_sptr ffg, int max_noutput_items, bool catch_exceptions);
virtual ~scheduler();
diff --git a/gnuradio-runtime/lib/scheduler_tpb.cc b/gnuradio-runtime/lib/scheduler_tpb.cc
index a255aa4946..7e240d7f61 100644
--- a/gnuradio-runtime/lib/scheduler_tpb.cc
+++ b/gnuradio-runtime/lib/scheduler_tpb.cc
@@ -51,13 +51,16 @@ public:
}
};
-scheduler_sptr scheduler_tpb::make(flat_flowgraph_sptr ffg, int max_noutput_items)
+scheduler_sptr
+scheduler_tpb::make(flat_flowgraph_sptr ffg, int max_noutput_items, bool catch_exceptions)
{
- return scheduler_sptr(new scheduler_tpb(ffg, max_noutput_items));
+ return scheduler_sptr(new scheduler_tpb(ffg, max_noutput_items, catch_exceptions));
}
-scheduler_tpb::scheduler_tpb(flat_flowgraph_sptr ffg, int max_noutput_items)
- : scheduler(ffg, max_noutput_items)
+scheduler_tpb::scheduler_tpb(flat_flowgraph_sptr ffg,
+ int max_noutput_items,
+ bool catch_exceptions)
+ : scheduler(ffg, max_noutput_items, catch_exceptions)
{
int block_max_noutput_items;
@@ -91,7 +94,9 @@ scheduler_tpb::scheduler_tpb(flat_flowgraph_sptr ffg, int max_noutput_items)
block_max_noutput_items = max_noutput_items;
}
d_threads.create_thread(thread::thread_body_wrapper<tpb_container>(
- tpb_container(blocks[i], block_max_noutput_items, start_sync), name.str()));
+ tpb_container(blocks[i], block_max_noutput_items, start_sync),
+ name.str(),
+ catch_exceptions));
}
start_sync->wait();
}
diff --git a/gnuradio-runtime/lib/scheduler_tpb.h b/gnuradio-runtime/lib/scheduler_tpb.h
index 152b11c8f3..35e8c557da 100644
--- a/gnuradio-runtime/lib/scheduler_tpb.h
+++ b/gnuradio-runtime/lib/scheduler_tpb.h
@@ -42,10 +42,12 @@ protected:
* The scheduler will continue running until all blocks until they
* report that they are done or the stop method is called.
*/
- scheduler_tpb(flat_flowgraph_sptr ffg, int max_noutput_items);
+ scheduler_tpb(flat_flowgraph_sptr ffg, int max_noutput_items, bool catch_exceptions);
public:
- static scheduler_sptr make(flat_flowgraph_sptr ffg, int max_noutput_items = 100000);
+ static scheduler_sptr make(flat_flowgraph_sptr ffg,
+ int max_noutput_items = 100000,
+ bool catch_exceptions = true);
~scheduler_tpb();
diff --git a/gnuradio-runtime/lib/top_block.cc b/gnuradio-runtime/lib/top_block.cc
index 7485124bac..3e81d71951 100644
--- a/gnuradio-runtime/lib/top_block.cc
+++ b/gnuradio-runtime/lib/top_block.cc
@@ -33,15 +33,15 @@
#include <iostream>
namespace gr {
-top_block_sptr make_top_block(const std::string& name)
+top_block_sptr make_top_block(const std::string& name, bool catch_exceptions)
{
- return gnuradio::get_initial_sptr(new top_block(name));
+ return gnuradio::get_initial_sptr(new top_block(name, catch_exceptions));
}
-top_block::top_block(const std::string& name)
+top_block::top_block(const std::string& name, bool catch_exceptions)
: hier_block2(name, io_signature::make(0, 0, 0), io_signature::make(0, 0, 0))
{
- d_impl = new top_block_impl(this);
+ d_impl = new top_block_impl(this, catch_exceptions);
}
top_block::~top_block()
diff --git a/gnuradio-runtime/lib/top_block_impl.cc b/gnuradio-runtime/lib/top_block_impl.cc
index 16acb3826d..79cb4088fd 100644
--- a/gnuradio-runtime/lib/top_block_impl.cc
+++ b/gnuradio-runtime/lib/top_block_impl.cc
@@ -40,7 +40,9 @@ namespace gr {
#define GR_TOP_BLOCK_IMPL_DEBUG 0
-typedef scheduler_sptr (*scheduler_maker)(flat_flowgraph_sptr ffg, int max_noutput_items);
+typedef scheduler_sptr (*scheduler_maker)(flat_flowgraph_sptr ffg,
+ int max_noutput_items,
+ bool catch_exceptions);
static struct scheduler_table {
const char* name;
@@ -49,7 +51,8 @@ static struct scheduler_table {
{ "TPB", scheduler_tpb::make } // first entry is default
};
-static scheduler_sptr make_scheduler(flat_flowgraph_sptr ffg, int max_noutput_items)
+static scheduler_sptr
+make_scheduler(flat_flowgraph_sptr ffg, int max_noutput_items, bool catch_exceptions)
{
static scheduler_maker factory = 0;
@@ -72,11 +75,16 @@ static scheduler_sptr make_scheduler(flat_flowgraph_sptr ffg, int max_noutput_it
}
}
}
- return factory(ffg, max_noutput_items);
+ return factory(ffg, max_noutput_items, catch_exceptions);
}
-top_block_impl::top_block_impl(top_block* owner)
- : d_owner(owner), d_ffg(), d_state(IDLE), d_lock_count(0), d_retry_wait(false)
+top_block_impl::top_block_impl(top_block* owner, bool catch_exceptions = true)
+ : d_owner(owner),
+ d_ffg(),
+ d_state(IDLE),
+ d_lock_count(0),
+ d_retry_wait(false),
+ d_catch_exceptions(catch_exceptions)
{
}
@@ -115,7 +123,7 @@ void top_block_impl::start(int max_noutput_items)
p->get_bool("PerfCounters", "export", false))
d_ffg->enable_pc_rpc();
- d_scheduler = make_scheduler(d_ffg, d_max_noutput_items);
+ d_scheduler = make_scheduler(d_ffg, d_max_noutput_items, d_catch_exceptions);
d_state = RUNNING;
}
@@ -195,7 +203,7 @@ void top_block_impl::restart()
d_ffg = new_ffg;
// Create a new scheduler to execute it
- d_scheduler = make_scheduler(d_ffg, d_max_noutput_items);
+ d_scheduler = make_scheduler(d_ffg, d_max_noutput_items, d_catch_exceptions);
d_retry_wait = true;
}
diff --git a/gnuradio-runtime/lib/top_block_impl.h b/gnuradio-runtime/lib/top_block_impl.h
index 84611b3f85..888b0fe0ab 100644
--- a/gnuradio-runtime/lib/top_block_impl.h
+++ b/gnuradio-runtime/lib/top_block_impl.h
@@ -39,7 +39,7 @@ namespace gr {
class GR_RUNTIME_API top_block_impl
{
public:
- top_block_impl(top_block* owner);
+ top_block_impl(top_block* owner, bool catch_exceptions);
~top_block_impl();
// Create and start scheduler threads
@@ -85,6 +85,7 @@ protected:
bool d_retry_wait;
boost::condition_variable d_lock_cond;
int d_max_noutput_items;
+ bool d_catch_exceptions;
private:
void restart();
diff --git a/gnuradio-runtime/python/gnuradio/gr/qa_uncaught_exception.py b/gnuradio-runtime/python/gnuradio/gr/qa_uncaught_exception.py
index 18799d152d..a098d78684 100644
--- a/gnuradio-runtime/python/gnuradio/gr/qa_uncaught_exception.py
+++ b/gnuradio-runtime/python/gnuradio/gr/qa_uncaught_exception.py
@@ -46,38 +46,50 @@ class except_block(gr.sync_block):
return len(output_items[0])
-class test_uncaught_exception(gr_unittest.TestCase):
+def process_func(catch_exceptions):
+ tb = gr.top_block(catch_exceptions=catch_exceptions)
+ # some test data
+ src_data = [complex(x, x + 1) for x in range(65536)]
+ src = blocks.vector_source_c(src_data)
+ src.set_repeat(True)
- def test_exception_throw(self):
- # Test to ensure that throwing an exception causes the
- # process running top_block to exit
+ e_block_1 = except_block(False)
+ e_block_2 = except_block(True)
- def process_func():
- tb = gr.top_block()
- # some test data
- src_data = [complex(x, x + 1) for x in range(65536)]
- src = blocks.vector_source_c(src_data)
- src.set_repeat(True)
+ sink_1 = blocks.null_sink(gr.sizeof_gr_complex)
+ sink_2 = blocks.null_sink(gr.sizeof_gr_complex)
- e_block_1 = except_block(False)
- e_block_2 = except_block(True)
+ tb.connect(src, e_block_1)
+ tb.connect(src, e_block_2)
+ tb.connect(e_block_1, sink_1)
+ tb.connect(e_block_2, sink_2)
+ tb.run()
- sink_1 = blocks.null_sink(gr.sizeof_gr_complex)
- sink_2 = blocks.null_sink(gr.sizeof_gr_complex)
- tb.connect(src, e_block_1)
- tb.connect(src, e_block_2)
- tb.connect(e_block_1, sink_1)
- tb.connect(e_block_2, sink_2)
- tb.run()
+class test_uncaught_exception(gr_unittest.TestCase):
- p = Process(target=process_func)
+ def test_exception_throw_uncaught(self):
+ # Test to ensure that throwing an exception causes the
+ # process running top_block to exit
+ p = Process(target=process_func, args=(False,))
p.daemon = True
p.start()
- p.join(1)
+ p.join(0.5)
exit_code = p.exitcode
self.assertIsNotNone(
exit_code, "exception did not cause flowgraph exit")
+ def test_exception_throw_caught(self):
+ # Test to ensure that throwing an exception does not cause the process
+ # running top_block to exit (in catch_exceptions mode)
+ p = Process(target=process_func, args=(True,))
+ p.daemon = True
+ p.start()
+ p.join(0.5)
+ exit_code = p.exitcode
+ self.assertIsNone(
+ exit_code, "exception caused flowgraph exit")
+
+
if __name__ == '__main__':
gr_unittest.run(test_uncaught_exception, "test_uncaught_exception.xml")
diff --git a/gnuradio-runtime/python/gnuradio/gr/top_block.py b/gnuradio-runtime/python/gnuradio/gr/top_block.py
index 87c3a81852..2b949ff398 100644
--- a/gnuradio-runtime/python/gnuradio/gr/top_block.py
+++ b/gnuradio-runtime/python/gnuradio/gr/top_block.py
@@ -96,12 +96,12 @@ class top_block(hier_block2):
python subclassing.
"""
- def __init__(self, name="top_block"):
+ def __init__(self, name="top_block", catch_exceptions=True):
"""
Create a top block with a given name.
"""
# not calling hier_block2.__init__, we set our own _impl
- self._impl = top_block_swig(name)
+ self._impl = top_block_swig(name, catch_exceptions)
self.handle_sigint = True
def start(self, max_noutput_items=10000000):
diff --git a/gnuradio-runtime/swig/top_block.i b/gnuradio-runtime/swig/top_block.i
index 6d0632a9a7..8cfe759336 100644
--- a/gnuradio-runtime/swig/top_block.i
+++ b/gnuradio-runtime/swig/top_block.i
@@ -26,13 +26,13 @@ namespace gr {
// Hack to have a Python shim implementation of gr.top_block
// that instantiates one of these and passes through calls
%rename(top_block_swig) make_top_block;
- gr::top_block_sptr make_top_block(const std::string name)
+ gr::top_block_sptr make_top_block(const std::string name, bool catch_exceptions)
throw (std::logic_error);
class top_block : public gr::hier_block2
{
private:
- top_block(const std::string &name);
+ top_block(const std::string &name, bool catch_exceptions=true);
public:
~top_block();