diff --git a/gnuradio-runtime/include/gnuradio/tpb_detail.h b/gnuradio-runtime/include/gnuradio/tpb_detail.h
index 9b7454b..71380b5 100644
--- a/gnuradio-runtime/include/gnuradio/tpb_detail.h
+++ b/gnuradio-runtime/include/gnuradio/tpb_detail.h
@@ -58,6 +58,7 @@ namespace gr {
 
     //! Called by pmt msg posters
     void notify_msg() {
+      gr::thread::scoped_lock guard(mutex);
       input_cond.notify_one();
       output_cond.notify_one();
     }
diff --git a/gnuradio-runtime/lib/basic_block.cc b/gnuradio-runtime/lib/basic_block.cc
index 8060c53..e8aa274 100644
--- a/gnuradio-runtime/lib/basic_block.cc
+++ b/gnuradio-runtime/lib/basic_block.cc
@@ -198,6 +198,7 @@ namespace gr {
     msg_queue_ready[which_port]->notify_one();
 
     // wake up thread if BLKD_IN or BLKD_OUT
+    guard.unlock();
     global_block_registry.notify_blk(alias());
   }
 
diff --git a/gnuradio-runtime/lib/tpb_thread_body.cc b/gnuradio-runtime/lib/tpb_thread_body.cc
index f6f09a9..70a1430 100644
--- a/gnuradio-runtime/lib/tpb_thread_body.cc
+++ b/gnuradio-runtime/lib/tpb_thread_body.cc
@@ -32,6 +32,15 @@
 
 namespace gr {
 
+  static void send_eof_to_all_msg_ports(block_sptr block) {
+      pmt::pmt_t msg_ports_out = block->message_ports_out();
+      if (pmt::length(msg_ports_out) > 0) {
+          for (unsigned i = 0; i < pmt::length(msg_ports_out); ++i) {
+              block->message_port_pub(pmt::vector_ref(msg_ports_out, i), pmt::PMT_EOF);
+          }
+      }
+  }
+
   tpb_thread_body::tpb_thread_body(block_sptr block, int max_noutput_items)
     : d_exec(block, max_noutput_items)
   {
@@ -67,6 +76,11 @@ namespace gr {
         // startup sequence of the threads.
         if(block->has_msg_handler(i.first)) {
           while((msg = block->delete_head_nowait(i.first))) {
+              if (pmt::eq(msg, pmt::PMT_EOF)) { // EOF indicates the flow graph is done
+                  // propagate the EOF to any downstream message receiving blocks
+                  send_eof_to_all_msg_ports(block);
+                  return;
+              }
             block->dispatch_msg(i.first,msg);
           }
         }
@@ -97,14 +111,15 @@ namespace gr {
         break;
 
       case block_executor::DONE:		// Game over.
+      {
+        send_eof_to_all_msg_ports(block);
         d->d_tpb.notify_neighbors(d);
         return;
-
+      }
       case block_executor::BLKD_IN:		// Wait for input.
       {
         gr::thread::scoped_lock guard(d->d_tpb.mutex);
         while(!d->d_tpb.input_changed) {
-
           // wait for input or message
           while(!d->d_tpb.input_changed && block->empty_handled_p())
             d->d_tpb.input_cond.wait(guard);
@@ -113,21 +128,26 @@ namespace gr {
           BOOST_FOREACH(basic_block::msg_queue_map_t::value_type &i, block->msg_queue) {
             if(block->has_msg_handler(i.first)) {
                 while((msg = block->delete_head_nowait(i.first))) {
-                  guard.unlock();			// release lock while processing msg
+                  if (pmt::eq(msg, pmt::PMT_EOF)) { // EOF indicates the flow graph is done
+                    // propagate the EOF to any downstream message receiving blocks
+                    send_eof_to_all_msg_ports(block);
+                    return;
+                  }
+                  guard.unlock();
                   block->dispatch_msg(i.first, msg);
                   guard.lock();
                 }
-            } 
+            }
             else {
                 // leave msg in queue if no handler is defined
                 // start dropping if we have too many
                 if(block->nmsgs(i.first) > max_nmsgs)
                     msg = block->delete_head_nowait(i.first);
-            }
+              }
           }
-	  if (d->done()) {
-	    return;
-	  }
+         if (d->done()) {
+           return;
+         }
         }
       }
       break;
@@ -144,7 +164,12 @@ namespace gr {
           BOOST_FOREACH(basic_block::msg_queue_map_t::value_type &i, block->msg_queue) {
             if(block->has_msg_handler(i.first)) {
                 while((msg = block->delete_head_nowait(i.first))) {
-                  guard.unlock();			// release lock while processing msg
+                  if (pmt::eq(msg, pmt::PMT_EOF)) { // EOF indicates the flow graph is done
+                      // propagate the EOF to any downstream message receiving blocks
+                      send_eof_to_all_msg_ports(block);
+                      return;
+                  }
+                  guard.unlock();
                   block->dispatch_msg(i.first, msg);
                   guard.lock();
                 }
