from gnuradio_swig_python import top_block_swig, \
top_block_wait_unlocked, top_block_run_unlocked
+#import gnuradio.gr.gr_threading as _threading
+import gr_threading as _threading
+
+
+#
+# There is no problem that can't be solved with an additional
+# level of indirection...
+#
+# This kludge allows ^C to interrupt top_block.run and top_block.wait
+#
+# The problem that we are working around is that Python only services
+# signals (e.g., KeyboardInterrupt) in its main thread. If the main
+# thread is blocked in our C++ version of wait, even though Python's
+# SIGINT handler fires, and even though there may be other python
+# threads running, no one will know. Thus instead of directly waiting
+# in the thread that calls wait (which is likely to be the Python main
+# thread), we create a separate thread that does the blocking wait,
+# and then use the thread that called wait to do a slow poll of an
+# event queue. That thread, which is executing "wait" below is
+# interruptable, and if it sees a KeyboardInterrupt, executes a stop
+# on the top_block, then goes back to waiting for it to complete.
+# This ensures that the unlocked wait that was in progress (in the
+# _top_block_waiter thread) can complete, release its mutex and back
+# out. If we don't do that, we are never able to clean up, and nasty
+# things occur like leaving the USRP transmitter sending a carrier.
+#
+# See also top_block.wait (below), which uses this class to implement
+# the interruptable wait.
+#
+class _top_block_waiter(_threading.Thread):
+ def __init__(self, tb):
+ _threading.Thread.__init__(self)
+ self.setDaemon(1)
+ self.tb = tb
+ self.event = _threading.Event()
+ self.start()
+
+ def run(self):
+ top_block_wait_unlocked(self.tb)
+ self.event.set()
+
+ def wait(self):
+ try:
+ while not self.event.isSet():
+ self.event.wait(0.100)
+ except KeyboardInterrupt:
+ self.tb.stop()
+ self.wait()
+
+
#
# This hack forces a 'has-a' relationship to look like an 'is-a' one.
#
self._tb = top_block_swig(name)
def __getattr__(self, name):
+ if not hasattr(self, "_tb"):
+ raise RuntimeError("top_block: invalid state--did you forget to call gr.top_block.__init__ in a derived class?")
return getattr(self._tb, name)
+ def start(self):
+ self._tb.start()
+
+ def stop(self):
+ self._tb.stop()
+
def run(self):
- top_block_run_unlocked(self._tb)
+ self.start()
+ self.wait()
def wait(self):
- top_block_wait_unlocked(self._tb)
+ _top_block_waiter(self._tb).wait()
+
# FIXME: these are duplicated from hier_block2.py; they should really be implemented
# in the original C++ class (gr_hier_block2), then they would all be inherited here