diff options
Diffstat (limited to 'gnuradio-core/src/lib/runtime')
130 files changed, 15583 insertions, 0 deletions
diff --git a/gnuradio-core/src/lib/runtime/CMakeLists.txt b/gnuradio-core/src/lib/runtime/CMakeLists.txt new file mode 100644 index 000000000..5f2c317c4 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/CMakeLists.txt @@ -0,0 +1,187 @@ +# Copyright 2010-2010 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Control availability of vmcircbuf methods. +# For now, only allows disabling of shm methods, which cause uncatchable +# segmentation faults on Cygwin with gcc 4.x (x <= 5) +# Usage: +# GR_VMCIRCBUF() +# +# Will set TRY_SHM_VMCIRCBUF to 1 by default except on Windows machines. +# Can manually set with -DTRY_SHM_VMCIRCBUF=0|1 +######################################################################## + + if(WIN32) + OPTION(TRY_SHM_VMCIRCBUF "Try SHM VMCIRCBUF" OFF) + else(WIN32) + OPTION(TRY_SHM_VMCIRCBUF "Try SHM VMCIRCBUF" ON) + endif(WIN32) + + message(STATUS "TRY_SHM_VMCIRCBUF set to ${TRY_SHM_VMCIRCBUF}.") + + if(TRY_SHM_VMCIRCBUF) + add_definitions( -DTRY_SHM_VMCIRCBUF ) + endif(TRY_SHM_VMCIRCBUF) + +######################################################################## +# Append gnuradio-core library sources +######################################################################## +list(APPEND gnuradio_core_sources + #${CMAKE_CURRENT_SOURCE_DIR}/gr_basic_block.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_flowgraph.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_flat_flowgraph.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_block.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_block_detail.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_block_executor.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_block_registry.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_hier_block2.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_hier_block2_detail.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_buffer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_dispatcher.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_error_handler.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_io_signature.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_local_sighandler.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_message.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_msg_accepter.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_msg_handler.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_msg_queue.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_pagesize.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_preferences.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_realtime.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_scheduler.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_scheduler_sts.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_scheduler_tpb.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_single_threaded_scheduler.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_sptr_magic.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_sync_block.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_sync_decimator.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_sync_interpolator.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_sys_paths.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_top_block.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_top_block_impl.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_tpb_detail.cc + #${CMAKE_CURRENT_SOURCE_DIR}/gr_tpb_thread_body.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_vmcircbuf.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_vmcircbuf_mmap_shm_open.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_vmcircbuf_mmap_tmpfile.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_vmcircbuf_createfilemapping.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_vmcircbuf_sysv_shm.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gr_select_handler.cc +) + +######################################################################## +# Append gnuradio-core test sources +######################################################################## +list(APPEND test_gnuradio_core_sources + ${CMAKE_CURRENT_SOURCE_DIR}/qa_gr_block.cc + ${CMAKE_CURRENT_SOURCE_DIR}/qa_gr_hier_block2.cc + ${CMAKE_CURRENT_SOURCE_DIR}/qa_gr_hier_block2_derived.cc + ${CMAKE_CURRENT_SOURCE_DIR}/qa_gr_buffer.cc + #${CMAKE_CURRENT_SOURCE_DIR}/qa_gr_flowgraph.cc + ${CMAKE_CURRENT_SOURCE_DIR}/qa_gr_top_block.cc + ${CMAKE_CURRENT_SOURCE_DIR}/qa_gr_io_signature.cc + ${CMAKE_CURRENT_SOURCE_DIR}/qa_gr_vmcircbuf.cc + #${CMAKE_CURRENT_SOURCE_DIR}/qa_block_tags.cc + ${CMAKE_CURRENT_SOURCE_DIR}/qa_runtime.cc + #${CMAKE_CURRENT_SOURCE_DIR}/qa_set_msg_handler.cc +) + +######################################################################## +# Install runtime headers +######################################################################## +install(FILES + ${CMAKE_CURRENT_SOURCE_DIR}/gr_basic_block.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_flowgraph.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_flat_flowgraph.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_block.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_block_detail.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_block_executor.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_block_registry.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_hier_block2.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_hier_block2_detail.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_buffer.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_complex.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_dispatcher.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_error_handler.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_io_signature.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_local_sighandler.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_message.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_msg_accepter.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_msg_handler.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_msg_queue.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_pagesize.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_preferences.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_realtime.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_runtime_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_scheduler.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_scheduler_sts.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_scheduler_tpb.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_select_handler.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_single_threaded_scheduler.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_sptr_magic.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_sync_block.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_sync_decimator.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_sync_interpolator.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_top_block.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_top_block_impl.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_tpb_detail.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_tpb_thread_body.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_timer.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_sys_paths.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_unittests.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_vmcircbuf.h + ${CMAKE_CURRENT_SOURCE_DIR}/gr_tags.h + DESTINATION ${GR_INCLUDE_DIR}/gnuradio + COMPONENT "core_devel" +) + +######################################################################## +# Install swig headers +######################################################################## +if(ENABLE_PYTHON) +install(FILES + ${CMAKE_CURRENT_SOURCE_DIR}/gr_basic_block.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_block.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_block_detail.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_hier_block2.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_buffer.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_dispatcher.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_error_handler.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_io_signature.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_message.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_msg_handler.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_msg_queue.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_realtime.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_single_threaded_scheduler.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_sync_block.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_sync_decimator.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_sync_interpolator.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_tags.i + ${CMAKE_CURRENT_SOURCE_DIR}/gr_top_block.i + ${CMAKE_CURRENT_SOURCE_DIR}/runtime.i + DESTINATION ${GR_INCLUDE_DIR}/gnuradio/swig + COMPONENT "core_swig" +) +endif(ENABLE_PYTHON) diff --git a/gnuradio-core/src/lib/runtime/gr_basic_block.cc b/gnuradio-core/src/lib/runtime/gr_basic_block.cc new file mode 100644 index 000000000..6ff57a1d6 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_basic_block.cc @@ -0,0 +1,227 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_basic_block.h> +#include <gr_block_registry.h> +#include <stdexcept> +#include <sstream> +#include <iostream> + +using namespace pmt; + +static long s_next_id = 0; +static long s_ncurrently_allocated = 0; + +long +gr_basic_block_ncurrently_allocated() +{ + return s_ncurrently_allocated; +} + +gr_basic_block::gr_basic_block(const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature) + : d_name(name), + d_input_signature(input_signature), + d_output_signature(output_signature), + d_unique_id(s_next_id++), + d_symbolic_id(global_block_registry.block_register(this)), + d_symbol_name(global_block_registry.register_symbolic_name(this)), + d_color(WHITE), + message_subscribers(pmt::pmt_make_dict()) +{ + s_ncurrently_allocated++; +} + +gr_basic_block::~gr_basic_block() +{ + s_ncurrently_allocated--; + global_block_registry.block_unregister(this); +} + +gr_basic_block_sptr +gr_basic_block::to_basic_block() +{ + return shared_from_this(); +} + +void +gr_basic_block::set_block_alias(std::string name) +{ + global_block_registry.register_symbolic_name(this, name); +} + +// ** Message passing interface ** + +// - register a new input message port +void +gr_basic_block::message_port_register_in(pmt::pmt_t port_id) +{ + if(!pmt::pmt_is_symbol(port_id)) { + throw std::runtime_error("message_port_register_in: bad port id"); + } + msg_queue[port_id] = msg_queue_t(); + msg_queue_ready[port_id] = boost::shared_ptr<boost::condition_variable>(new boost::condition_variable()); +} + +pmt::pmt_t +gr_basic_block::message_ports_in() +{ + pmt::pmt_t port_names = pmt::pmt_make_vector(msg_queue.size(), pmt::PMT_NIL); + msg_queue_map_itr itr = msg_queue.begin(); + for(size_t i = 0; i < msg_queue.size(); i++) { + pmt::pmt_vector_set(port_names, i, (*itr).first); + itr++; + } + return port_names; +} + +// - register a new output message port +void +gr_basic_block::message_port_register_out(pmt::pmt_t port_id) +{ + if(!pmt::pmt_is_symbol(port_id)) { + throw std::runtime_error("message_port_register_out: bad port id"); + } + if(pmt::pmt_dict_has_key(message_subscribers, port_id)) { + throw std::runtime_error("message_port_register_out: port already in use"); + } + message_subscribers = pmt::pmt_dict_add(message_subscribers, port_id, pmt::PMT_NIL); +} + +pmt::pmt_t +gr_basic_block::message_ports_out() +{ + size_t len = pmt::pmt_length(message_subscribers); + pmt::pmt_t port_names = pmt::pmt_make_vector(len, pmt::PMT_NIL); + pmt::pmt_t keys = pmt::pmt_dict_keys(message_subscribers); + for(size_t i = 0; i < len; i++) { + pmt::pmt_vector_set(port_names, i, pmt::pmt_nth(i, keys)); + } + return port_names; +} + +// - publish a message on a message port +void gr_basic_block::message_port_pub(pmt::pmt_t port_id, pmt::pmt_t msg) +{ + if(!pmt::pmt_dict_has_key(message_subscribers, port_id)) { + throw std::runtime_error("port does not exist"); + } + + pmt::pmt_t currlist = pmt::pmt_dict_ref(message_subscribers, port_id, pmt::PMT_NIL); + // iterate through subscribers on port + while(pmt::pmt_is_pair(currlist)) { + pmt::pmt_t target = pmt::pmt_car(currlist); + + pmt::pmt_t block = pmt::pmt_car(target); + pmt::pmt_t port = pmt::pmt_cdr(target); + + currlist = pmt::pmt_cdr(currlist); + gr_basic_block_sptr blk = global_block_registry.block_lookup(block); + //blk->post(msg); + blk->post(port, msg); + } +} + +// - subscribe to a message port +void +gr_basic_block::message_port_sub(pmt::pmt_t port_id, pmt::pmt_t target){ + if(!pmt::pmt_dict_has_key(message_subscribers, port_id)){ + std::stringstream ss; + ss << "Port does not exist: \"" << pmt::pmt_write_string(port_id) << "\" on block: " << pmt::pmt_write_string(target) << std::endl; + throw std::runtime_error(ss.str()); + } + pmt::pmt_t currlist = pmt::pmt_dict_ref(message_subscribers,port_id,pmt::PMT_NIL); + + // ignore re-adds of the same target + if(!pmt::pmt_list_has(currlist, target)) + message_subscribers = pmt::pmt_dict_add(message_subscribers,port_id,pmt::pmt_list_add(currlist,target)); +} + +void +gr_basic_block::message_port_unsub(pmt::pmt_t port_id, pmt::pmt_t target){ + if(!pmt::pmt_dict_has_key(message_subscribers, port_id)){ + std::stringstream ss; + ss << "Port does not exist: \"" << pmt::pmt_write_string(port_id) << "\" on block: " << pmt::pmt_write_string(target) << std::endl; + throw std::runtime_error(ss.str()); + } + + // ignore unsubs of unknown targets + pmt::pmt_t currlist = pmt::pmt_dict_ref(message_subscribers,port_id,pmt::PMT_NIL); + message_subscribers = pmt::pmt_dict_add(message_subscribers,port_id,pmt::pmt_list_rm(currlist,target)); +} + +void +gr_basic_block::_post(pmt_t which_port, pmt_t msg) +{ + insert_tail(which_port, msg); +} + +void +gr_basic_block::insert_tail(pmt::pmt_t which_port, pmt::pmt_t msg) +{ + gruel::scoped_lock guard(mutex); + + if( (msg_queue.find(which_port) == msg_queue.end()) || (msg_queue_ready.find(which_port) == msg_queue_ready.end())){ + std::cout << "target port = " << pmt::pmt_symbol_to_string(which_port) << std::endl; + throw std::runtime_error("attempted to insert_tail on invalid queue!"); + } + + msg_queue[which_port].push_back(msg); + msg_queue_ready[which_port]->notify_one(); + + // wake up thread if BLKD_IN or BLKD_OUT + global_block_registry.notify_blk(alias()); +} + +pmt_t +gr_basic_block::delete_head_nowait(pmt::pmt_t which_port) +{ + gruel::scoped_lock guard(mutex); + + if (empty_p(which_port)){ + return pmt::pmt_t(); + } + + pmt_t m(msg_queue[which_port].front()); + msg_queue[which_port].pop_front(); + + return m; +} + +pmt_t +gr_basic_block::delete_head_blocking(pmt::pmt_t which_port) +{ + gruel::scoped_lock guard(mutex); + + while (empty_p(which_port)){ + msg_queue_ready[which_port]->wait(guard); + } + + pmt_t m(msg_queue[which_port].front()); + msg_queue[which_port].pop_front(); + return m; +} diff --git a/gnuradio-core/src/lib/runtime/gr_basic_block.h b/gnuradio-core/src/lib/runtime/gr_basic_block.h new file mode 100644 index 000000000..024159c4c --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_basic_block.h @@ -0,0 +1,299 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2008,2009,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_BASIC_BLOCK_H +#define INCLUDED_GR_BASIC_BLOCK_H + +#include <gr_core_api.h> +#include <gr_runtime_types.h> +#include <gr_sptr_magic.h> +#include <boost/enable_shared_from_this.hpp> +#include <boost/function.hpp> +#include <gr_msg_accepter.h> +#include <string> +#include <deque> +#include <map> +#include <gr_io_signature.h> +#include <gruel/thread.h> +#include <boost/foreach.hpp> +#include <boost/thread/condition_variable.hpp> +#include <iostream> + +/*! + * \brief The abstract base class for all signal processing blocks. + * \ingroup internal + * + * Basic blocks are the bare abstraction of an entity that has a name, + * a set of inputs and outputs, and a message queue. These are never instantiated + * directly; rather, this is the abstract parent class of both gr_hier_block, + * which is a recursive container, and gr_block, which implements actual + * signal processing functions. + */ + +class GR_CORE_API gr_basic_block : public gr_msg_accepter, public boost::enable_shared_from_this<gr_basic_block> +{ + typedef boost::function<void(pmt::pmt_t)> msg_handler_t; + + private: + + //msg_handler_t d_msg_handler; + typedef std::map<pmt::pmt_t , msg_handler_t, pmt::pmt_comperator> d_msg_handlers_t; + d_msg_handlers_t d_msg_handlers; + + typedef std::deque<pmt::pmt_t> msg_queue_t; + typedef std::map<pmt::pmt_t, msg_queue_t, pmt::pmt_comperator> msg_queue_map_t; + typedef std::map<pmt::pmt_t, msg_queue_t, pmt::pmt_comperator>::iterator msg_queue_map_itr; + std::map<pmt::pmt_t, boost::shared_ptr<boost::condition_variable>, pmt::pmt_comperator> msg_queue_ready; + + gruel::mutex mutex; //< protects all vars + + protected: + friend class gr_flowgraph; + friend class gr_flat_flowgraph; // TODO: will be redundant + friend class gr_tpb_thread_body; + + enum vcolor { WHITE, GREY, BLACK }; + + std::string d_name; + gr_io_signature_sptr d_input_signature; + gr_io_signature_sptr d_output_signature; + long d_unique_id; + long d_symbolic_id; + std::string d_symbol_name; + std::string d_symbol_alias; + vcolor d_color; + msg_queue_map_t msg_queue; + + gr_basic_block(void){} //allows pure virtual interface sub-classes + + //! Protected constructor prevents instantiation by non-derived classes + gr_basic_block(const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature); + + //! may only be called during constructor + void set_input_signature(gr_io_signature_sptr iosig) { + d_input_signature = iosig; + } + + //! may only be called during constructor + void set_output_signature(gr_io_signature_sptr iosig) { + d_output_signature = iosig; + } + + /*! + * \brief Allow the flowgraph to set for sorting and partitioning + */ + void set_color(vcolor color) { d_color = color; } + vcolor color() const { return d_color; } + + /*! + * \brief Tests if there is a handler attached to port \p which_port + */ + bool has_msg_handler(pmt::pmt_t which_port) { + return (d_msg_handlers.find(which_port) != d_msg_handlers.end()); + } + + /* + * This function is called by the runtime system to dispatch messages. + * + * The thread-safety guarantees mentioned in set_msg_handler are implemented + * by the callers of this method. + */ + virtual void dispatch_msg(pmt::pmt_t which_port, pmt::pmt_t msg) + { + // AA Update this + if(has_msg_handler(which_port)) { // Is there a handler? + d_msg_handlers[which_port](msg); // Yes, invoke it. + } + } + + // Message passing interface + pmt::pmt_t message_subscribers; + + public: + virtual ~gr_basic_block(); + long unique_id() const { return d_unique_id; } + long symbolic_id() const { return d_symbolic_id; } + std::string name() const { return d_name; } + std::string symbol_name() const { return d_symbol_name; } + gr_io_signature_sptr input_signature() const { return d_input_signature; } + gr_io_signature_sptr output_signature() const { return d_output_signature; } + gr_basic_block_sptr to_basic_block(); // Needed for Python type coercion + bool alias_set() { return !d_symbol_alias.empty(); } + std::string alias(){ return alias_set()?d_symbol_alias:symbol_name(); } + pmt::pmt_t alias_pmt(){ return pmt::pmt_intern(alias()); } + void set_block_alias(std::string name); + + // ** Message passing interface ** + void message_port_register_in(pmt::pmt_t port_id); + void message_port_register_out(pmt::pmt_t port_id); + void message_port_pub(pmt::pmt_t port_id, pmt::pmt_t msg); + void message_port_sub(pmt::pmt_t port_id, pmt::pmt_t target); + void message_port_unsub(pmt::pmt_t port_id, pmt::pmt_t target); + + virtual bool message_port_is_hier(pmt::pmt_t port_id) { (void) port_id; std::cout << "is_hier\n"; return false; } + virtual bool message_port_is_hier_in(pmt::pmt_t port_id) { (void) port_id; std::cout << "is_hier_in\n"; return false; } + virtual bool message_port_is_hier_out(pmt::pmt_t port_id) { (void) port_id; std::cout << "is_hier_out\n"; return false; } + + /*! + * \brief Get input message port names. + * + * Returns the available input message ports for a block. The + * return object is a PMT vector that is filled with PMT symbols. + */ + pmt::pmt_t message_ports_in(); + + /*! + * \brief Get output message port names. + * + * Returns the available output message ports for a block. The + * return object is a PMT vector that is filled with PMT symbols. + */ + pmt::pmt_t message_ports_out(); + + /*! + * Accept msg, place in queue, arrange for thread to be awakened if it's not already. + */ + void _post(pmt::pmt_t which_port, pmt::pmt_t msg); + + //! is the queue empty? + //bool empty_p(const pmt::pmt_t &which_port) const { return msg_queue[which_port].empty(); } + bool empty_p(pmt::pmt_t which_port) { + if(msg_queue.find(which_port) == msg_queue.end()) + throw std::runtime_error("port does not exist!"); + return msg_queue[which_port].empty(); + } + bool empty_p() { + bool rv = true; + BOOST_FOREACH(msg_queue_map_t::value_type &i, msg_queue) { + rv &= msg_queue[i.first].empty(); + } + return rv; + } + + //! How many messages in the queue? + size_t nmsgs(pmt::pmt_t which_port) { + if(msg_queue.find(which_port) == msg_queue.end()) + throw std::runtime_error("port does not exist!"); + return msg_queue[which_port].size(); + } + + //| Acquires and release the mutex + void insert_tail( pmt::pmt_t which_port, pmt::pmt_t msg); + /*! + * \returns returns pmt at head of queue or pmt_t() if empty. + */ + pmt::pmt_t delete_head_nowait( pmt::pmt_t which_port); + + /*! + * \returns returns pmt at head of queue or pmt_t() if empty. + */ + pmt::pmt_t delete_head_blocking( pmt::pmt_t which_port); + + msg_queue_t::iterator get_iterator(pmt::pmt_t which_port){ + return msg_queue[which_port].begin(); + } + + void erase_msg(pmt::pmt_t which_port, msg_queue_t::iterator it){ + msg_queue[which_port].erase(it); + } + + virtual bool has_msg_port(pmt::pmt_t which_port){ + if(msg_queue.find(which_port) != msg_queue.end()){ + return true; + } + if(pmt::pmt_dict_has_key(message_subscribers, which_port)){ + return true; + } + return false; + } + + + /*! + * \brief Confirm that ninputs and noutputs is an acceptable combination. + * + * \param ninputs number of input streams connected + * \param noutputs number of output streams connected + * + * \returns true if this is a valid configuration for this block. + * + * This function is called by the runtime system whenever the + * topology changes. Most classes do not need to override this. + * This check is in addition to the constraints specified by the input + * and output gr_io_signatures. + */ + virtual bool check_topology(int ninputs, int noutputs) { (void) ninputs; (void) noutputs; return true; } + + /*! + * \brief Set the callback that is fired when messages are available. + * + * \p msg_handler can be any kind of function pointer or function object + * that has the signature: + * <pre> + * void msg_handler(pmt::pmt msg); + * </pre> + * + * (You may want to use boost::bind to massage your callable into the + * correct form. See gr_nop.{h,cc} for an example that sets up a class + * method as the callback.) + * + * Blocks that desire to handle messages must call this method in their + * constructors to register the handler that will be invoked when messages + * are available. + * + * If the block inherits from gr_block, the runtime system will ensure that + * msg_handler is called in a thread-safe manner, such that work and + * msg_handler will never be called concurrently. This allows msg_handler + * to update state variables without having to worry about thread-safety + * issues with work, general_work or another invocation of msg_handler. + * + * If the block inherits from gr_hier_block2, the runtime system will + * ensure that no reentrant calls are made to msg_handler. + */ + //template <typename T> void set_msg_handler(T msg_handler){ + // d_msg_handler = msg_handler_t(msg_handler); + //} + template <typename T> void set_msg_handler(pmt::pmt_t which_port, T msg_handler){ + if(msg_queue.find(which_port) == msg_queue.end()){ + throw std::runtime_error("attempt to set_msg_handler() on bad input message port!"); } + d_msg_handlers[which_port] = msg_handler_t(msg_handler); + } +}; + +inline bool operator<(gr_basic_block_sptr lhs, gr_basic_block_sptr rhs) +{ + return lhs->unique_id() < rhs->unique_id(); +} + +typedef std::vector<gr_basic_block_sptr> gr_basic_block_vector_t; +typedef std::vector<gr_basic_block_sptr>::iterator gr_basic_block_viter_t; + +GR_CORE_API long gr_basic_block_ncurrently_allocated(); + +inline std::ostream &operator << (std::ostream &os, gr_basic_block_sptr basic_block) +{ + os << basic_block->name() << "(" << basic_block->unique_id() << ")"; + return os; +} + +#endif /* INCLUDED_GR_BASIC_BLOCK_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_basic_block.i b/gnuradio-core/src/lib/runtime/gr_basic_block.i new file mode 100644 index 000000000..91f3d2cfc --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_basic_block.i @@ -0,0 +1,62 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +class gr_basic_block; +typedef boost::shared_ptr<gr_basic_block> gr_basic_block_sptr; +%template(gr_basic_block_sptr) boost::shared_ptr<gr_basic_block>; +%import "pmt_swig.i" +using namespace pmt; + +// support vectors of these... +namespace std { + %template(x_vector_gr_basic_block_sptr) vector<gr_basic_block_sptr>; +}; + +class gr_basic_block +{ +protected: + gr_basic_block(); + +public: + virtual ~gr_basic_block(); + std::string name() const; + std::string symbol_name() const; + gr_io_signature_sptr input_signature() const; + gr_io_signature_sptr output_signature() const; + long unique_id() const; + gr_basic_block_sptr to_basic_block(); + bool check_topology (int ninputs, int noutputs); + std::string alias(); + void set_block_alias(std::string name); + void _post(pmt_t which_port, pmt_t msg); + pmt_t message_ports_in(); + pmt_t message_ports_out(); +}; + +%rename(block_ncurrently_allocated) gr_basic_block_ncurrently_allocated; +long gr_basic_block_ncurrently_allocated(); + +#ifdef SWIGPYTHON +%pythoncode %{ +gr_basic_block_sptr.__repr__ = lambda self: "<gr_basic_block %s (%d)>" % (self.name(), self.unique_id ()) +%} +#endif diff --git a/gnuradio-core/src/lib/runtime/gr_block.cc b/gnuradio-core/src/lib/runtime/gr_block.cc new file mode 100644 index 000000000..443ac8f64 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_block.cc @@ -0,0 +1,459 @@ +/* + * Copyright 2012-2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "pmx_helper.hpp" +#include <gr_block.h> +#include <boost/foreach.hpp> +#include <iostream> +#include <boost/detail/atomic_count.hpp> + +static boost::detail::atomic_count unique_id_pool(0); + +gr_block::gr_block(void) +{ + //NOP +} + +gr_block::gr_block( + const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature +): + gras::Block(name), + _unique_id(++unique_id_pool), + _name(name) +{ + //this initializes private vars, order matters + this->set_fixed_rate(false); + this->set_output_multiple(1); + this->set_history(1); + this->set_relative_rate(1.0); + this->set_decimation(1); + this->set_interpolation(1); + this->set_tag_propagation_policy(TPP_ALL_TO_ALL); + this->set_input_signature(input_signature); + this->set_output_signature(output_signature); +} + +gr_io_signature_sptr gr_block::input_signature(void) const +{ + return _in_sig; +} + +gr_io_signature_sptr gr_block::output_signature(void) const +{ + return _out_sig; +} + +void gr_block::set_input_signature(gr_io_signature_sptr sig) +{ + for (size_t i = 0; i < sig->sizeof_stream_items().size(); i++) + { + this->input_config(i).item_size = sig->sizeof_stream_items().at(i); + } + _in_sig = sig; +} + +void gr_block::set_output_signature(gr_io_signature_sptr sig) +{ + for (size_t i = 0; i < sig->sizeof_stream_items().size(); i++) + { + this->output_config(i).item_size = sig->sizeof_stream_items().at(i); + } + _out_sig = sig; +} + +gr_block::~gr_block(void) +{ + //NOP +} + +void gr_block::notify_active(void) +{ + this->start(); +} + +bool gr_block::start(void) +{ + return true; +} + +void gr_block::notify_inactive(void) +{ + this->stop(); +} + +bool gr_block::stop(void) +{ + return true; +} + +void gr_block::notify_topology(const size_t num_inputs, const size_t num_outputs) +{ + _num_outputs = num_outputs; + _fcast_ninput_items.resize(num_inputs); + _work_ninput_items.resize(num_inputs); + this->check_topology(num_inputs, num_outputs); +} + +bool gr_block::check_topology(int, int) +{ + return true; +} + +void gr_block::work( + const InputItems &input_items, + const OutputItems &output_items +){ + _work_io_ptr_mask = 0; + #define REALLY_BIG size_t(1 << 30) + const size_t num_inputs = input_items.size(); + const size_t num_outputs = output_items.size(); + + //------------------------------------------------------------------ + //-- initialize input buffers before work + //------------------------------------------------------------------ + size_t num_input_items = input_items.min(); + if (_enable_fixed_rate) num_input_items -= _input_history_items; + for (size_t i = 0; i < num_inputs; i++) + { + _work_ninput_items[i] = input_items[i].size(); + _work_io_ptr_mask |= ptrdiff_t(input_items.vec()[i]); + if GRAS_UNLIKELY(_enable_fixed_rate and input_items[i].size() <= _input_history_items) + { + return this->mark_input_fail(i); + } + } + + //------------------------------------------------------------------ + //-- initialize output buffers before work + //------------------------------------------------------------------ + size_t num_output_items = output_items.min(); + num_output_items /= _output_multiple_items; + num_output_items *= _output_multiple_items; + for (size_t i = 0; i < num_outputs; i++) + { + _work_io_ptr_mask |= ptrdiff_t(output_items.vec()[i]); + } + + //------------------------------------------------------------------ + //-- calculate the work_noutput_items given: + //-- min of num_input_items + //-- min of num_output_items + //-- relative rate and output multiple items + //------------------------------------------------------------------ + size_t work_noutput_items = num_output_items; + if (num_inputs and (_enable_fixed_rate or not num_outputs)) + { + size_t calc_output_items = size_t(num_input_items*_relative_rate); + calc_output_items += _output_multiple_items-1; + calc_output_items /= _output_multiple_items; + calc_output_items *= _output_multiple_items; + if (calc_output_items and calc_output_items < work_noutput_items) + work_noutput_items = calc_output_items; + } + + //------------------------------------------------------------------ + //-- forecast + //------------------------------------------------------------------ + if (num_inputs or num_outputs) + { + forecast_again_you_jerk: + _fcast_ninput_items = _work_ninput_items; //init for NOP case + this->forecast(work_noutput_items, _fcast_ninput_items); + for (size_t i = 0; i < input_items.size(); i++) + { + if GRAS_LIKELY(_fcast_ninput_items[i] <= _work_ninput_items[i]) continue; + + //handle the case of forecast failing + if GRAS_UNLIKELY(work_noutput_items <= _output_multiple_items) + { + return this->mark_input_fail(i); + } + + work_noutput_items = work_noutput_items/2; //backoff regime + work_noutput_items += _output_multiple_items-1; + work_noutput_items /= _output_multiple_items; + work_noutput_items *= _output_multiple_items; + goto forecast_again_you_jerk; + } + } + + const int work_ret = this->general_work( + work_noutput_items, + _work_ninput_items, + const_cast<InputItems &>(input_items).vec(), + const_cast<OutputItems &>(output_items).vec() + ); + + if GRAS_LIKELY(work_ret > 0) for (size_t i = 0; i < num_outputs; i++) + { + this->produce(i, work_ret); + } + + if GRAS_UNLIKELY(work_ret == -1) this->mark_done(); +} + +static inline unsigned long long myullround(const double x) +{ + return (unsigned long long)(x + 0.5); +} + +void gr_block::propagate_tags(const size_t which_input, const gras::TagIter &iter) +{ + switch (_tag_prop_policy) + { + case TPP_DONT: break; //well that was ez + case TPP_ALL_TO_ALL: + for (size_t out_i = 0; out_i < _num_outputs; out_i++) + { + BOOST_FOREACH(gras::Tag t, iter) + { + t.offset = myullround(t.offset * _relative_rate); + this->post_output_tag(out_i, t); + } + } + break; + case TPP_ONE_TO_ONE: + if (which_input < _num_outputs) + { + BOOST_FOREACH(gras::Tag t, iter) + { + t.offset = myullround(t.offset * _relative_rate); + this->post_output_tag(which_input, t); + } + } + break; + }; +} + +void gr_block::forecast(int noutput_items, std::vector<int> &ninputs_req) +{ + for (size_t i = 0; i < ninputs_req.size(); i++) + { + ninputs_req[i] = fixed_rate_noutput_to_ninput(noutput_items); + } +} + +int gr_block::general_work( + int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items +){ + throw std::runtime_error("gr_block subclasses must overload general_work!"); +} + +void gr_block::set_alignment(const size_t) +{ + //TODO + //probably dont need this since buffers always start aligned + //and therefore alignment is always re-acheived +} + +size_t gr_block::fixed_rate_noutput_to_ninput(const size_t noutput_items) +{ + return ((decimation()*noutput_items)/interpolation()) + _input_history_items; +} + +void gr_block::set_interpolation(const size_t interp) +{ + _interp = interp; + this->set_relative_rate(1.0*interp); + this->set_output_multiple(interp); +} + +void gr_block::set_decimation(const size_t decim) +{ + _decim = decim; + this->set_relative_rate(1.0/decim); +} + +unsigned gr_block::history(void) const +{ + //implement off-by-one history compat + return _input_history_items+1; +} + +void gr_block::set_history(unsigned history) +{ + //implement off-by-one history compat + if (history == 0) history++; + _input_history_items = history-1; + this->input_config(0).preload_items = _input_history_items; + this->commit_config(); +} + +void gr_block::set_fixed_rate(const bool fixed_rate) +{ + _enable_fixed_rate = fixed_rate; +} + +bool gr_block::fixed_rate(void) const +{ + return _enable_fixed_rate; +} + +void gr_block::_update_input_reserve(void) +{ + /*! + * Set an input reserve for fixed rate blocks. + * + * FIXME: Also do this when output multiple is large, + * This makes gr-trellis pass under conditions where not fixed rate set, + * but the output multiple is so large that default input isnt sufficient. + */ + if (_enable_fixed_rate or _output_multiple_items > 1024) + { + const size_t reserve = size_t(0.5 + _output_multiple_items/_relative_rate); + if (reserve) this->input_config(0).reserve_items = reserve; + } +} + +void gr_block::set_output_multiple(const size_t multiple) +{ + _output_multiple_items = multiple; + this->output_config(0).reserve_items = multiple; + this->_update_input_reserve(); +} + +size_t gr_block::output_multiple(void) const +{ + return _output_multiple_items; +} + +void gr_block::set_relative_rate(double relative_rate) +{ + _relative_rate = relative_rate; + this->_update_input_reserve(); +} + +double gr_block::relative_rate(void) const +{ + return _relative_rate; +} + +int gr_block::max_noutput_items(void) const +{ + return this->output_config(0).maximum_items; +} + +void gr_block::set_max_noutput_items(int max_items) +{ + this->output_config(0).maximum_items = max_items; +} + +void gr_block::unset_max_noutput_items(void) +{ + this->set_max_noutput_items(0); +} + +bool gr_block::is_set_max_noutput_items(void) const +{ + return this->max_noutput_items() != 0; +} + +static gr_tag_t Tag2gr_tag(const gras::Tag &tag) +{ + gr_tag_t t; + t.offset = tag.offset; + const gras::StreamTag &st = tag.object.as<gras::StreamTag>(); + t.key = pmt::pmc_to_pmt(st.key); + t.value = pmt::pmc_to_pmt(st.val); + t.srcid = pmt::pmc_to_pmt(st.src); + return t; +} + +static gras::Tag gr_tag2Tag(const gr_tag_t &tag) +{ + return gras::Tag + ( + tag.offset, + PMC_M(gras::StreamTag( + pmt::pmt_to_pmc(tag.key), + pmt::pmt_to_pmc(tag.value), + pmt::pmt_to_pmc(tag.srcid) + )) + ); +} + +void gr_block::add_item_tag( + const size_t which_output, const gr_tag_t &tag +){ + this->post_output_tag(which_output, gr_tag2Tag(tag)); +} + +void gr_block::add_item_tag( + const size_t which_output, + uint64_t abs_offset, + const pmt::pmt_t &key, + const pmt::pmt_t &value, + const pmt::pmt_t &srcid +){ + gr_tag_t t; + t.offset = abs_offset; + t.key = key; + t.value = value; + t.srcid = srcid; + this->add_item_tag(which_output, t); +} + +void gr_block::get_tags_in_range( + std::vector<gr_tag_t> &tags, + const size_t which_input, + uint64_t abs_start, + uint64_t abs_end, + const pmt::pmt_t &key +){ + tags.clear(); + BOOST_FOREACH(const gras::Tag &tag, this->get_input_tags(which_input)) + { + if (tag.offset >= abs_start and tag.offset <= abs_end) + { + gr_tag_t t = Tag2gr_tag(tag); + if (not key or pmt::pmt_equal(t.key, key)) tags.push_back(t); + } + } +} + +gr_block::tag_propagation_policy_t gr_block::tag_propagation_policy(void) +{ + return _tag_prop_policy; +} + +void gr_block::set_tag_propagation_policy(gr_block::tag_propagation_policy_t p) +{ + _tag_prop_policy = p; +} + +gras::BufferQueueSptr gr_block::input_buffer_allocator(const size_t, const gras::SBufferConfig &config) +{ + if (_input_history_items) + { + return gras::BufferQueue::make_circ(config, 32/*many*/); + } + return gras::BufferQueueSptr(); +} + +gras::BufferQueueSptr gr_block::output_buffer_allocator(const size_t which, const gras::SBufferConfig &config) +{ + return gras::Block::output_buffer_allocator(which, config); +} diff --git a/gnuradio-core/src/lib/runtime/gr_block.h b/gnuradio-core/src/lib/runtime/gr_block.h new file mode 100644 index 000000000..bd085100b --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_block.h @@ -0,0 +1,437 @@ +/* + * Copyright 2012-2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GNURADIO_GR_BLOCK_H +#define INCLUDED_GNURADIO_GR_BLOCK_H + +#include <gr_core_api.h> +#include <gras/block.hpp> +#include <gr_io_signature.h> +#include <gr_types.h> +#include <gr_tags.h> +#include <string> +#include <deque> +#include <map> +#include <boost/foreach.hpp> +#include <gruel/thread.h> +#include <gr_sptr_magic.h> + +struct GR_CORE_API gr_block : gras::Block +{ + + gr_block(void); + + gr_block( + const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature + ); + + long unique_id(void) const{return _unique_id;} + std::string name(void) const{return _name;} + long _unique_id; + std::string _name; + + virtual ~gr_block(void); + + gr_io_signature_sptr input_signature(void) const; + gr_io_signature_sptr output_signature(void) const; + + void set_input_signature(gr_io_signature_sptr sig); + void set_output_signature(gr_io_signature_sptr sig); + + virtual bool check_topology(int ninputs, int noutputs); + + //! Overload me! I am the forecast + virtual void forecast(int, std::vector<int> &); + + //! Return options for the work call + enum + { + WORK_CALLED_PRODUCE = -2, + WORK_DONE = -1 + }; + + /*! + * \brief compute output items from input items + * + * \param noutput_items number of output items to write on each output stream + * \param ninput_items number of input items available on each input stream + * \param input_items vector of pointers to the input items, one entry per input stream + * \param output_items vector of pointers to the output items, one entry per output stream + * + * \returns number of items actually written to each output stream, or -1 on EOF. + * It is OK to return a value less than noutput_items. -1 <= return value <= noutput_items + * + * general_work must call consume or consume_each to indicate how many items + * were consumed on each input stream. + */ + virtual int general_work( + int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items + ); + + virtual bool start(void); + virtual bool stop(void); + + //! Call during work to consume items + void consume_each(const int how_many_items); + + void consume(const size_t i, const int how_many_items); + + void produce(const size_t o, const int how_many_items); + + //! Get absolute count of all items consumed on the given input port + uint64_t nitems_read(const size_t which_input = 0); + + //! Get absolute count of all items produced on the given output port + uint64_t nitems_written(const size_t which_output = 0); + + void add_item_tag( + const size_t which_output, const gr_tag_t &tag + ); + + void add_item_tag( + const size_t which_output, + uint64_t abs_offset, + const pmt::pmt_t &key, + const pmt::pmt_t &value, + const pmt::pmt_t &srcid=pmt::PMT_F + ); + + void get_tags_in_range( + std::vector<gr_tag_t> &tags, + const size_t which_input, + uint64_t abs_start, + uint64_t abs_end, + const pmt::pmt_t &key = pmt::pmt_t() + ); + + void set_alignment(const size_t alignment); + + bool is_unaligned(void); + + size_t fixed_rate_noutput_to_ninput(const size_t noutput_items); + + size_t interpolation(void) const; + + void set_interpolation(const size_t); + + size_t decimation(void) const; + + void set_decimation(const size_t); + + int max_noutput_items(void) const; + + void set_max_noutput_items(int); + + void unset_max_noutput_items(void); + + bool is_set_max_noutput_items(void) const; + + /******************************************************************* + * Deal with input and output port configuration + ******************************************************************/ + + unsigned history(void) const; + + void set_history(unsigned history); + + /*! + * Enable fixed rate logic. + * When enabled, relative rate is assumed to be set, + * and forecast is automatically called. + * Also, consume will be called automatically. + */ + void set_fixed_rate(const bool fixed_rate); + + //! Get the fixed rate setting + bool fixed_rate(void) const; + + /*! + * The relative rate can be thought of as interpolation/decimation. + * In other words, relative rate is the ratio of output items to input items. + */ + void set_relative_rate(const double relative_rate); + + //! Get the relative rate setting + double relative_rate(void) const; + + /*! + * The output multiple setting controls work output buffer sizes. + * Buffers will be number of items modulo rounted to the multiple. + */ + void set_output_multiple(const size_t multiple); + + //! Get the output multiple setting + size_t output_multiple(void) const; + + /******************************************************************* + * Deal with tag handling and tag configuration + ******************************************************************/ + + enum tag_propagation_policy_t + { + TPP_DONT = 0, + TPP_ALL_TO_ALL = 1, + TPP_ONE_TO_ONE = 2 + }; + + tag_propagation_policy_t tag_propagation_policy(void); + + void set_tag_propagation_policy(tag_propagation_policy_t p); + + ///////////// TODO ////////////////////// + void set_max_output_buffer(long){} + void set_max_output_buffer(int, long){} + long max_output_buffer(size_t){return 0;} + void set_min_output_buffer(long){} + void set_min_output_buffer(int, long){} + long min_output_buffer(size_t){return 0;} + + ///////////// ALIAS stuff - is it used? ////////////////////// + std::string d_symbol_alias; + std::string d_symbol_name; + std::string symbol_name() const { return d_symbol_name; } + bool alias_set() { return !d_symbol_alias.empty(); } + std::string alias(){ return alias_set()?d_symbol_alias:symbol_name(); } + pmt::pmt_t alias_pmt(){ return pmt::pmt_intern(alias()); } + void set_block_alias(std::string name){d_symbol_alias = name;} + + ///////////// MSG stuff not implemented ////////////////////// + typedef std::deque<pmt::pmt_t> msg_queue_t; + typedef std::map<pmt::pmt_t, msg_queue_t, pmt::pmt_comperator> msg_queue_map_t; + typedef std::map<pmt::pmt_t, msg_queue_t, pmt::pmt_comperator>::iterator msg_queue_map_itr; + msg_queue_map_t msg_queue; + pmt::pmt_t message_subscribers; + + typedef boost::function<void(pmt::pmt_t)> msg_handler_t; + typedef std::map<pmt::pmt_t , msg_handler_t, pmt::pmt_comperator> d_msg_handlers_t; + d_msg_handlers_t d_msg_handlers; + + template <typename T> void set_msg_handler(pmt::pmt_t which_port, T msg_handler){} + + void message_port_register_in(pmt::pmt_t /*port_id*/){} + void message_port_register_out(pmt::pmt_t /*port_id*/){} + void message_port_pub(pmt::pmt_t /*port_id*/, pmt::pmt_t /*msg*/){} + void message_port_sub(pmt::pmt_t /*port_id*/, pmt::pmt_t /*target*/){} + void message_port_unsub(pmt::pmt_t /*port_id*/, pmt::pmt_t /*target*/){} + + virtual bool message_port_is_hier(pmt::pmt_t port_id) { (void) port_id; /*std::cout << "is_hier\n";*/ return false; } + virtual bool message_port_is_hier_in(pmt::pmt_t port_id) { (void) port_id; /*std::cout << "is_hier_in\n";*/ return false; } + virtual bool message_port_is_hier_out(pmt::pmt_t port_id) { (void) port_id; /*std::cout << "is_hier_out\n";*/ return false; } + + /*! + * \brief Get input message port names. + * + * Returns the available input message ports for a block. The + * return object is a PMT vector that is filled with PMT symbols. + */ + pmt::pmt_t message_ports_in(){return pmt::PMT_NIL;} + + /*! + * \brief Get output message port names. + * + * Returns the available output message ports for a block. The + * return object is a PMT vector that is filled with PMT symbols. + */ + pmt::pmt_t message_ports_out(){return pmt::PMT_NIL;} + + //! is the queue empty? + bool empty_p(pmt::pmt_t which_port) { + if(msg_queue.find(which_port) == msg_queue.end()) + throw std::runtime_error("port does not exist!"); + return msg_queue[which_port].empty(); + } + bool empty_p() { + bool rv = true; + BOOST_FOREACH(msg_queue_map_t::value_type &i, msg_queue){ rv &= msg_queue[i.first].empty(); } + return rv; + } + + //| Acquires and release the mutex + void insert_tail( pmt::pmt_t /*which_port*/, pmt::pmt_t /*msg*/){} + /*! + * \returns returns pmt at head of queue or pmt_t() if empty. + */ + pmt::pmt_t delete_head_nowait( pmt::pmt_t /*which_port*/){return pmt::PMT_NIL;} + + /*! + * \returns returns pmt at head of queue or pmt_t() if empty. + */ + pmt::pmt_t delete_head_blocking( pmt::pmt_t /*which_port*/){return pmt::PMT_NIL;} + + msg_queue_t::iterator get_iterator(pmt::pmt_t which_port){ + return msg_queue[which_port].begin(); + } + + void erase_msg(pmt::pmt_t which_port, msg_queue_t::iterator it){ + msg_queue[which_port].erase(it); + } + + virtual bool has_msg_port(pmt::pmt_t which_port){ + if(msg_queue.find(which_port) != msg_queue.end()){ + return true; + } + if(pmt::pmt_dict_has_key(message_subscribers, which_port)){ + return true; + } + return false; + } + + /*! + * \brief Tests if there is a handler attached to port \p which_port + */ + bool has_msg_handler(pmt::pmt_t which_port) { + return (d_msg_handlers.find(which_port) != d_msg_handlers.end()); + } + + /* + * This function is called by the runtime system to dispatch messages. + * + * The thread-safety guarantees mentioned in set_msg_handler are implemented + * by the callers of this method. + */ + virtual void dispatch_msg(pmt::pmt_t which_port, pmt::pmt_t msg) + { + // AA Update this + if(has_msg_handler(which_port)) { // Is there a handler? + d_msg_handlers[which_port](msg); // Yes, invoke it. + } + } + + /*! Used by block's setters and work functions to make + * setting/resetting of parameters thread-safe. + * + * Used by calling gruel::scoped_lock l(d_setlock); + */ + gruel::mutex d_setlock; + + // ---------------------------------------------------------------------------- + // Functions to handle thread affinity + std::vector<int> d_affinity; // thread affinity proc. mask + + /*! + * \brief Set the thread's affinity to processor core \p n. + * + * \param mask a vector of unsigned ints of the core numbers available to this block. + */ + void set_processor_affinity(const std::vector<int> &mask){d_affinity=mask;} + + /*! + * \brief Remove processor affinity to a specific core. + */ + void unset_processor_affinity(){} + + /*! + * \brief Get the current processor affinity. + */ + std::vector<int> processor_affinity() { return d_affinity; } + + ///////////////// private vars ////////////////////// + + gr_vector_int _work_ninput_items; + gr_vector_int _fcast_ninput_items; + size_t _num_outputs; + ptrdiff_t _work_io_ptr_mask; + size_t _output_multiple_items; + double _relative_rate; + bool _enable_fixed_rate; + size_t _input_history_items; + tag_propagation_policy_t _tag_prop_policy; + size_t _interp, _decim; + gr_io_signature_sptr _in_sig, _out_sig; + + ///////////////// the Block overloads ////////////////////// + + //! implements work -> calls general work + void work(const InputItems &, const OutputItems &); + + //! notifications of new topological commits + void notify_topology(const size_t, const size_t); + + //! start notification + void notify_active(void); + + //! stop notification + void notify_inactive(void); + + //! implements tag_propagation_policy() + virtual void propagate_tags(const size_t, const gras::TagIter &); + + void _update_input_reserve(void); + + gras::BufferQueueSptr input_buffer_allocator(const size_t, const gras::SBufferConfig &); + gras::BufferQueueSptr output_buffer_allocator(const size_t, const gras::SBufferConfig &); + +}; + +typedef boost::shared_ptr<gr_block> gr_block_sptr; + +GRAS_FORCE_INLINE void gr_block::consume_each(const int how_many_items) +{ + if GRAS_UNLIKELY(how_many_items < 0) return; + gras::Block::consume(size_t(how_many_items)); +} + +GRAS_FORCE_INLINE void gr_block::consume(const size_t i, const int how_many_items) +{ + if GRAS_UNLIKELY(how_many_items < 0) return; + gras::Block::consume(i, size_t(how_many_items)); +} + +GRAS_FORCE_INLINE void gr_block::produce(const size_t o, const int how_many_items) +{ + if GRAS_UNLIKELY(how_many_items < 0) return; + gras::Block::produce(o, size_t(how_many_items)); +} + +GRAS_FORCE_INLINE uint64_t gr_block::nitems_read(const size_t which_input) +{ + return Block::get_consumed(which_input); +} + +GRAS_FORCE_INLINE uint64_t gr_block::nitems_written(const size_t which_output) +{ + return Block::get_produced(which_output); +} + +GRAS_FORCE_INLINE size_t gr_block::interpolation(void) const +{ + return _interp; +} + +GRAS_FORCE_INLINE size_t gr_block::decimation(void) const +{ + return _decim; +} + +GRAS_FORCE_INLINE bool gr_block::is_unaligned(void) +{ + //TODO + //probably dont need this since volk dispatcher checks alignment + //32 byte aligned is good enough for you + return (_work_io_ptr_mask & ptrdiff_t(GRAS_MAX_ALIGNMENT-1)) != 0; +} + +#endif /*INCLUDED_GNURADIO_GR_BLOCK_H*/ diff --git a/gnuradio-core/src/lib/runtime/gr_block.i b/gnuradio-core/src/lib/runtime/gr_block.i new file mode 100644 index 000000000..a53489f9a --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_block.i @@ -0,0 +1,93 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +%include <gr_basic_block.i> + +class gr_block; +typedef boost::shared_ptr<gr_block> gr_block_sptr; +%template(gr_block_sptr) boost::shared_ptr<gr_block>; + +// support vectors of these... +namespace std { + %template(x_vector_gr_block_sptr) vector<gr_block_sptr>; +}; + +class gr_block : public gr_basic_block { + protected: + gr_block (const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature); + + public: + + virtual ~gr_block (); + + unsigned history () const; + + int output_multiple () const; + double relative_rate () const; + + bool start(); + bool stop(); + + uint64_t nitems_read(unsigned int which_input); + uint64_t nitems_written(unsigned int which_output); + + // Methods to manage the block's max_noutput_items size. + int max_noutput_items(); + void set_max_noutput_items(int m); + void unset_max_noutput_items(); + bool is_set_max_noutput_items(); + + // Methods to manage block's min/max buffer sizes. + long max_output_buffer(int i); + void set_max_output_buffer(long max_output_buffer); + void set_max_output_buffer(int port, long max_output_buffer); + long min_output_buffer(int i); + void set_min_output_buffer(long min_output_buffer); + void set_min_output_buffer(int port, long min_output_buffer); + + // Methods to access performance counters + float pc_noutput_items(); + float pc_noutput_items_var(); + float pc_nproduced(); + float pc_nproduced_var(); + float pc_input_buffers_full(int which); + float pc_input_buffers_full_var(int which); + std::vector<float> pc_input_buffers_full(); + std::vector<float> pc_input_buffers_full_var(); + float pc_output_buffers_full(int which); + float pc_output_buffers_full_var(int which); + std::vector<float> pc_output_buffers_full(); + std::vector<float> pc_output_buffers_full_var(); + float pc_work_time(); + float pc_work_time_var(); + + // Methods to manage processor affinity. + void set_processor_affinity(const std::vector<int> &mask); + void unset_processor_affinity(); + std::vector<int> processor_affinity(); + + // internal use + gr_block_detail_sptr detail () const { return d_detail; } + void set_detail (gr_block_detail_sptr detail) { d_detail = detail; } +}; diff --git a/gnuradio-core/src/lib/runtime/gr_block_detail.cc b/gnuradio-core/src/lib/runtime/gr_block_detail.cc new file mode 100644 index 000000000..4922ea083 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_block_detail.cc @@ -0,0 +1,414 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2009,2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_block_detail.h> +#include <gr_buffer.h> +#include <iostream> + +using namespace pmt; + +static long s_ncurrently_allocated = 0; + +long +gr_block_detail_ncurrently_allocated () +{ + return s_ncurrently_allocated; +} + +gr_block_detail::gr_block_detail (unsigned int ninputs, unsigned int noutputs) + : d_produce_or(0), + d_ninputs (ninputs), d_noutputs (noutputs), + d_input (ninputs), d_output (noutputs), + d_done (false), + d_avg_noutput_items(0), + d_var_noutput_items(0), + d_avg_nproduced(0), + d_var_nproduced(0), + d_avg_input_buffers_full(ninputs, 0), + d_var_input_buffers_full(ninputs, 0), + d_avg_output_buffers_full(noutputs, 0), + d_var_output_buffers_full(noutputs, 0), + d_avg_work_time(0), + d_var_work_time(0), + d_pc_counter(0) +{ + s_ncurrently_allocated++; +} + +gr_block_detail::~gr_block_detail () +{ + // should take care of itself + s_ncurrently_allocated--; +} + +void +gr_block_detail::set_input (unsigned int which, gr_buffer_reader_sptr reader) +{ + if (which >= d_ninputs) + throw std::invalid_argument ("gr_block_detail::set_input"); + + d_input[which] = reader; +} + +void +gr_block_detail::set_output (unsigned int which, gr_buffer_sptr buffer) +{ + if (which >= d_noutputs) + throw std::invalid_argument ("gr_block_detail::set_output"); + + d_output[which] = buffer; +} + +gr_block_detail_sptr +gr_make_block_detail (unsigned int ninputs, unsigned int noutputs) +{ + return gr_block_detail_sptr (new gr_block_detail (ninputs, noutputs)); +} + +void +gr_block_detail::set_done (bool done) +{ + d_done = done; + for (unsigned int i = 0; i < d_noutputs; i++) + d_output[i]->set_done (done); + + for (unsigned int i = 0; i < d_ninputs; i++) + d_input[i]->set_done (done); +} + +void +gr_block_detail::consume (int which_input, int how_many_items) +{ + if (how_many_items > 0) { + input (which_input)->update_read_pointer (how_many_items); + } +} + + +void +gr_block_detail::consume_each (int how_many_items) +{ + if (how_many_items > 0) { + for (int i = 0; i < ninputs (); i++) { + d_input[i]->update_read_pointer (how_many_items); + } + } +} + +void +gr_block_detail::produce (int which_output, int how_many_items) +{ + if (how_many_items > 0){ + d_output[which_output]->update_write_pointer (how_many_items); + d_produce_or |= how_many_items; + } +} + +void +gr_block_detail::produce_each (int how_many_items) +{ + if (how_many_items > 0) { + for (int i = 0; i < noutputs (); i++) { + d_output[i]->update_write_pointer (how_many_items); + } + d_produce_or |= how_many_items; + } +} + + +uint64_t +gr_block_detail::nitems_read(unsigned int which_input) +{ + if(which_input >= d_ninputs) + throw std::invalid_argument ("gr_block_detail::n_input_items"); + return d_input[which_input]->nitems_read(); +} + +uint64_t +gr_block_detail::nitems_written(unsigned int which_output) +{ + if(which_output >= d_noutputs) + throw std::invalid_argument ("gr_block_detail::n_output_items"); + return d_output[which_output]->nitems_written(); +} + +void +gr_block_detail::add_item_tag(unsigned int which_output, const gr_tag_t &tag) +{ + if(!pmt_is_symbol(tag.key)) { + throw pmt_wrong_type("gr_block_detail::add_item_tag key", tag.key); + } + else { + // Add tag to gr_buffer's deque tags + d_output[which_output]->add_item_tag(tag); + } +} + +void +gr_block_detail::remove_item_tag(unsigned int which_input, const gr_tag_t &tag) +{ + if(!pmt_is_symbol(tag.key)) { + throw pmt_wrong_type("gr_block_detail::add_item_tag key", tag.key); + } + else { + // Add tag to gr_buffer's deque tags + d_input[which_input]->buffer()->remove_item_tag(tag); + } +} + +void +gr_block_detail::get_tags_in_range(std::vector<gr_tag_t> &v, + unsigned int which_input, + uint64_t abs_start, + uint64_t abs_end) +{ + // get from gr_buffer_reader's deque of tags + d_input[which_input]->get_tags_in_range(v, abs_start, abs_end); +} + +void +gr_block_detail::get_tags_in_range(std::vector<gr_tag_t> &v, + unsigned int which_input, + uint64_t abs_start, + uint64_t abs_end, + const pmt_t &key) +{ + std::vector<gr_tag_t> found_items; + + v.resize(0); + + // get from gr_buffer_reader's deque of tags + d_input[which_input]->get_tags_in_range(found_items, abs_start, abs_end); + + // Filter further by key name + pmt_t itemkey; + std::vector<gr_tag_t>::iterator itr; + for(itr = found_items.begin(); itr != found_items.end(); itr++) { + itemkey = (*itr).key; + if(pmt_eqv(key, itemkey)) { + v.push_back(*itr); + } + } +} + +void +gr_block_detail::set_processor_affinity(const std::vector<int> &mask) +{ + if(threaded) { + try { + gruel::thread_bind_to_processor(thread, mask); + } + catch (std::runtime_error e) { + std::cerr << "set_processor_affinity: invalid mask." << std::endl;; + } + } +} + +void +gr_block_detail::unset_processor_affinity() +{ + if(threaded) { + gruel::thread_unbind(thread); + } +} + +void +gr_block_detail::start_perf_counters() +{ + d_start_of_work = gruel::high_res_timer_now(); +} + +void +gr_block_detail::stop_perf_counters(int noutput_items, int nproduced) +{ + d_end_of_work = gruel::high_res_timer_now(); + gruel::high_res_timer_type diff = d_end_of_work - d_start_of_work; + + if(d_pc_counter == 0) { + d_avg_work_time = diff; + d_var_work_time = 0; + d_avg_nproduced = nproduced; + d_var_nproduced = 0; + d_avg_noutput_items = noutput_items; + d_var_noutput_items = 0; + for(size_t i=0; i < d_input.size(); i++) { + float pfull = static_cast<float>(d_input[i]->items_available()) / + static_cast<float>(d_input[i]->max_possible_items_available()); + d_avg_input_buffers_full[i] = pfull; + d_var_input_buffers_full[i] = 0; + } + for(size_t i=0; i < d_output.size(); i++) { + gruel::scoped_lock guard(*d_output[i]->mutex()); + float pfull = 1.0f - static_cast<float>(d_output[i]->space_available()) / + static_cast<float>(d_output[i]->bufsize()); + d_avg_output_buffers_full[i] = pfull; + d_var_output_buffers_full[i] = 0; + } + } + else { + float d = diff - d_avg_work_time; + d_avg_work_time = d_avg_work_time + d/d_pc_counter; + d_var_work_time = d_var_work_time + d*d; + + d = nproduced - d_avg_nproduced; + d_avg_nproduced = d_avg_nproduced + d/d_pc_counter; + d_var_nproduced = d_var_nproduced + d*d; + + d = noutput_items - d_avg_noutput_items; + d_avg_noutput_items = d_avg_noutput_items + d/d_pc_counter; + d_var_noutput_items = d_var_noutput_items + d*d; + + for(size_t i=0; i < d_input.size(); i++) { + float pfull = static_cast<float>(d_input[i]->items_available()) / + static_cast<float>(d_input[i]->max_possible_items_available()); + + d = pfull - d_avg_input_buffers_full[i]; + d_avg_input_buffers_full[i] = d_avg_input_buffers_full[i] + d/d_pc_counter; + d_var_input_buffers_full[i] = d_var_input_buffers_full[i] + d*d; + } + + for(size_t i=0; i < d_output.size(); i++) { + gruel::scoped_lock guard(*d_output[i]->mutex()); + float pfull = 1.0f - static_cast<float>(d_output[i]->space_available()) / + static_cast<float>(d_output[i]->bufsize()); + + d = pfull - d_avg_output_buffers_full[i]; + d_avg_output_buffers_full[i] = d_avg_output_buffers_full[i] + d/d_pc_counter; + d_var_output_buffers_full[i] = d_var_output_buffers_full[i] + d*d; + } + } + + d_pc_counter++; +} + +void +gr_block_detail::reset_perf_counters() +{ + d_pc_counter = 0; +} + +float +gr_block_detail::pc_noutput_items() +{ + return d_avg_noutput_items; +} + +float +gr_block_detail::pc_nproduced() +{ + return d_avg_nproduced; +} + +float +gr_block_detail::pc_input_buffers_full(size_t which) +{ + if(which < d_avg_input_buffers_full.size()) + return d_avg_input_buffers_full[which]; + else + return 0; +} + +std::vector<float> +gr_block_detail::pc_input_buffers_full() +{ + return d_avg_input_buffers_full; +} + +float +gr_block_detail::pc_output_buffers_full(size_t which) +{ + if(which < d_avg_output_buffers_full.size()) + return d_avg_output_buffers_full[which]; + else + return 0; +} + +std::vector<float> +gr_block_detail::pc_output_buffers_full() +{ + return d_avg_output_buffers_full; +} + +float +gr_block_detail::pc_work_time() +{ + return d_avg_work_time; +} + + +float +gr_block_detail::pc_noutput_items_var() +{ + return d_var_noutput_items/(d_pc_counter-1); +} + +float +gr_block_detail::pc_nproduced_var() +{ + return d_var_nproduced/(d_pc_counter-1); +} + +float +gr_block_detail::pc_input_buffers_full_var(size_t which) +{ + if(which < d_avg_input_buffers_full.size()) + return d_var_input_buffers_full[which]/(d_pc_counter-1); + else + return 0; +} + +std::vector<float> +gr_block_detail::pc_input_buffers_full_var() +{ + std::vector<float> var(d_avg_input_buffers_full.size(), 0); + for(size_t i = 0; i < d_avg_input_buffers_full.size(); i++) + var[i] = d_avg_input_buffers_full[i]/(d_pc_counter-1); + return var; +} + +float +gr_block_detail::pc_output_buffers_full_var(size_t which) +{ + if(which < d_avg_output_buffers_full.size()) + return d_var_output_buffers_full[which]/(d_pc_counter-1); + else + return 0; +} + +std::vector<float> +gr_block_detail::pc_output_buffers_full_var() +{ + std::vector<float> var(d_avg_output_buffers_full.size(), 0); + for(size_t i = 0; i < d_avg_output_buffers_full.size(); i++) + var[i] = d_avg_output_buffers_full[i]/(d_pc_counter-1); + return var; +} + +float +gr_block_detail::pc_work_time_var() +{ + return d_var_work_time/(d_pc_counter-1); +} diff --git a/gnuradio-core/src/lib/runtime/gr_block_detail.h b/gnuradio-core/src/lib/runtime/gr_block_detail.h new file mode 100644 index 000000000..15d85135a --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_block_detail.h @@ -0,0 +1,235 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2009,2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more detail. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_BLOCK_DETAIL_H +#define INCLUDED_GR_BLOCK_DETAIL_H + +#include <gr_core_api.h> +#include <gr_runtime_types.h> +#include <gr_tpb_detail.h> +#include <gr_tags.h> +#include <gruel/high_res_timer.h> +#include <stdexcept> + +/*! + * \brief Implementation details to support the signal processing abstraction + * \ingroup internal + * + * This class contains implementation detail that should be "out of sight" + * of almost all users of GNU Radio. This decoupling also means that + * we can make changes to the guts without having to recompile everything. + */ +class GR_CORE_API gr_block_detail { + public: + ~gr_block_detail (); + + int ninputs () const { return d_ninputs; } + int noutputs () const { return d_noutputs; } + bool sink_p () const { return d_noutputs == 0; } + bool source_p () const { return d_ninputs == 0; } + + void set_done (bool done); + bool done () const { return d_done; } + + void set_input (unsigned int which, gr_buffer_reader_sptr reader); + gr_buffer_reader_sptr input (unsigned int which) + { + if (which >= d_ninputs) + throw std::invalid_argument ("gr_block_detail::input"); + return d_input[which]; + } + + void set_output (unsigned int which, gr_buffer_sptr buffer); + gr_buffer_sptr output (unsigned int which) + { + if (which >= d_noutputs) + throw std::invalid_argument ("gr_block_detail::output"); + return d_output[which]; + } + + /*! + * \brief Tell the scheduler \p how_many_items of input stream \p which_input were consumed. + */ + void consume (int which_input, int how_many_items); + + /*! + * \brief Tell the scheduler \p how_many_items were consumed on each input stream. + */ + void consume_each (int how_many_items); + + /*! + * \brief Tell the scheduler \p how_many_items were produced on output stream \p which_output. + */ + void produce (int which_output, int how_many_items); + + /*! + * \brief Tell the scheduler \p how_many_items were produced on each output stream. + */ + void produce_each (int how_many_items); + + // Return the number of items read on input stream which_input + uint64_t nitems_read(unsigned int which_input); + + // Return the number of items written on output stream which_output + uint64_t nitems_written(unsigned int which_output); + + + /*! + * \brief Adds a new tag to the given output stream. + * + * Calls gr_buffer::add_item_tag(), + * which appends the tag onto its deque. + * + * \param which_output an integer of which output stream to attach the tag + * \param tag the tag object to add + */ + void add_item_tag(unsigned int which_output, const gr_tag_t &tag); + + /*! + * \brief Removes a tag from the given input stream. + * + * Calls gr_buffer::remove_item_tag(), which removes the tag from its deque. + * + * \param which_input an integer of which input stream to remove the tag from + * \param tag the tag object to add + */ + void remove_item_tag(unsigned int which_input, const gr_tag_t &tag); + + /*! + * \brief Given a [start,end), returns a vector of all tags in the range. + * + * Pass-through function to gr_buffer_reader to get a vector of tags + * in given range. Range of counts is from start to end-1. + * + * Tags are tuples of: + * (item count, source id, key, value) + * + * \param v a vector reference to return tags into + * \param which_input an integer of which input stream to pull from + * \param abs_start a uint64 count of the start of the range of interest + * \param abs_end a uint64 count of the end of the range of interest + */ + void get_tags_in_range(std::vector<gr_tag_t> &v, + unsigned int which_input, + uint64_t abs_start, + uint64_t abs_end); + + /*! + * \brief Given a [start,end), returns a vector of all tags in the range + * with a given key. + * + * Calls get_tags_in_range(which_input, abs_start, abs_end) to get a vector of + * tags from the buffers. This function then provides a secondary filter to + * the tags to extract only tags with the given 'key'. + * + * Tags are tuples of: + * (item count, source id, key, value) + * + * \param v a vector reference to return tags into + * \param which_input an integer of which input stream to pull from + * \param abs_start a uint64 count of the start of the range of interest + * \param abs_end a uint64 count of the end of the range of interest + * \param key a PMT symbol to select only tags of this key + */ + void get_tags_in_range(std::vector<gr_tag_t> &v, + unsigned int which_input, + uint64_t abs_start, + uint64_t abs_end, + const pmt::pmt_t &key); + + /*! + * \brief Set core affinity of block to the cores in the vector mask. + * + * \param mask a vector of ints of the core numbers available to this block. + */ + void set_processor_affinity(const std::vector<int> &mask); + + /*! + * \brief Unset core affinity. + */ + void unset_processor_affinity(); + + bool threaded; // set if thread is currently running. + gruel::gr_thread_t thread; // portable thread handle + + void start_perf_counters(); + void stop_perf_counters(int noutput_items, int nproduced); + void reset_perf_counters(); + + // Calls to get performance counter items + float pc_noutput_items(); + float pc_nproduced(); + float pc_input_buffers_full(size_t which); + std::vector<float> pc_input_buffers_full(); + float pc_output_buffers_full(size_t which); + std::vector<float> pc_output_buffers_full(); + float pc_work_time(); + + float pc_noutput_items_var(); + float pc_nproduced_var(); + float pc_input_buffers_full_var(size_t which); + std::vector<float> pc_input_buffers_full_var(); + float pc_output_buffers_full_var(size_t which); + std::vector<float> pc_output_buffers_full_var(); + float pc_work_time_var(); + + gr_tpb_detail d_tpb; // used by thread-per-block scheduler + int d_produce_or; + + // ---------------------------------------------------------------------------- + + private: + unsigned int d_ninputs; + unsigned int d_noutputs; + std::vector<gr_buffer_reader_sptr> d_input; + std::vector<gr_buffer_sptr> d_output; + bool d_done; + + // Performance counters + float d_avg_noutput_items; + float d_var_noutput_items; + float d_avg_nproduced; + float d_var_nproduced; + std::vector<float> d_avg_input_buffers_full; + std::vector<float> d_var_input_buffers_full; + std::vector<float> d_avg_output_buffers_full; + std::vector<float> d_var_output_buffers_full; + gruel::high_res_timer_type d_start_of_work, d_end_of_work; + float d_avg_work_time; + float d_var_work_time; + float d_pc_counter; + + gr_block_detail (unsigned int ninputs, unsigned int noutputs); + + friend struct gr_tpb_detail; + + friend GR_CORE_API gr_block_detail_sptr + gr_make_block_detail (unsigned int ninputs, unsigned int noutputs); +}; + +GR_CORE_API gr_block_detail_sptr +gr_make_block_detail (unsigned int ninputs, unsigned int noutputs); + +GR_CORE_API long +gr_block_detail_ncurrently_allocated (); + +#endif /* INCLUDED_GR_BLOCK_DETAIL_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_block_detail.i b/gnuradio-core/src/lib/runtime/gr_block_detail.i new file mode 100644 index 000000000..74ff46360 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_block_detail.i @@ -0,0 +1,66 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +class gr_block_detail; +typedef boost::shared_ptr<gr_block_detail> gr_block_detail_sptr; +%template(gr_block_detail_sptr) boost::shared_ptr<gr_block_detail>; +%rename(block_detail) gr_make_block_detail; +%ignore gr_block_detail; + +gr_block_detail_sptr gr_make_block_detail (unsigned int ninputs, unsigned int noutputs); + +class gr_block_detail { + public: + + ~gr_block_detail (); + + int ninputs () const { return d_ninputs; } + int noutputs () const { return d_noutputs; } + bool sink_p () const { return d_noutputs == 0; } + bool source_p () const { return d_ninputs == 0; } + + void set_input (unsigned int which, gr_buffer_reader_sptr reader); + gr_buffer_reader_sptr input (unsigned int which) + { + if (which >= d_ninputs) + throw std::invalid_argument ("gr_block_detail::input"); + return d_input[which]; + } + + void set_output (unsigned int which, gr_buffer_sptr buffer); + gr_buffer_sptr output (unsigned int which) + { + if (which >= d_noutputs) + throw std::invalid_argument ("gr_block_detail::output"); + return d_output[which]; + } + + // ---------------------------------------------------------------------------- + + private: + gr_block_detail (unsigned int ninputs, unsigned int noutputs); + +}; + + +%rename(block_detail_ncurrently_allocated) gr_block_detail_ncurrently_allocated; +long gr_block_detail_ncurrently_allocated (); diff --git a/gnuradio-core/src/lib/runtime/gr_block_executor.cc b/gnuradio-core/src/lib/runtime/gr_block_executor.cc new file mode 100644 index 000000000..e070f3c50 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_block_executor.cc @@ -0,0 +1,487 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2008,2009,2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_block_executor.h> +#include <gr_block.h> +#include <gr_block_detail.h> +#include <gr_buffer.h> +#include <gr_prefs.h> +#include <boost/thread.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <limits> +#include <assert.h> +#include <stdio.h> + +// must be defined to either 0 or 1 +#define ENABLE_LOGGING 0 + +#if (ENABLE_LOGGING) +#define LOG(x) do { x; } while(0) +#else +#define LOG(x) do {;} while(0) +#endif + +static int which_scheduler = 0; + +inline static unsigned int +round_up (unsigned int n, unsigned int multiple) +{ + return ((n + multiple - 1) / multiple) * multiple; +} + +inline static unsigned int +round_down (unsigned int n, unsigned int multiple) +{ + return (n / multiple) * multiple; +} + +// +// Return minimum available write space in all our downstream buffers +// or -1 if we're output blocked and the output we're blocked +// on is done. +// +static int +min_available_space (gr_block_detail *d, int output_multiple, int min_noutput_items) +{ + int min_space = std::numeric_limits<int>::max(); + if (min_noutput_items == 0) + min_noutput_items = 1; + for (int i = 0; i < d->noutputs (); i++){ + gruel::scoped_lock guard(*d->output(i)->mutex()); + int avail_n = round_down(d->output(i)->space_available(), output_multiple); + int best_n = round_down(d->output(i)->bufsize()/2, output_multiple); + if (best_n < min_noutput_items) + throw std::runtime_error("Buffer too small for min_noutput_items"); + int n = std::min(avail_n, best_n); + if (n < min_noutput_items){ // We're blocked on output. + if (d->output(i)->done()){ // Downstream is done, therefore we're done. + return -1; + } + return 0; + } + min_space = std::min (min_space, n); + } + return min_space; +} + +static bool +propagate_tags(gr_block::tag_propagation_policy_t policy, gr_block_detail *d, + const std::vector<uint64_t> &start_nitems_read, double rrate, + std::vector<gr_tag_t> &rtags) +{ + // Move tags downstream + // if a sink, we don't need to move downstream + if(d->sink_p()) { + return true; + } + + switch(policy) { + case gr_block::TPP_DONT: + return true; + break; + case gr_block::TPP_ALL_TO_ALL: + // every tag on every input propogates to everyone downstream + for(int i = 0; i < d->ninputs(); i++) { + d->get_tags_in_range(rtags, i, start_nitems_read[i], + d->nitems_read(i)); + + std::vector<gr_tag_t>::iterator t; + if(rrate == 1.0) { + for(t = rtags.begin(); t != rtags.end(); t++) { + for(int o = 0; o < d->noutputs(); o++) + d->output(o)->add_item_tag(*t); + } + } + else { + for(t = rtags.begin(); t != rtags.end(); t++) { + gr_tag_t new_tag = *t; + new_tag.offset *= rrate; + for(int o = 0; o < d->noutputs(); o++) + d->output(o)->add_item_tag(new_tag); + } + } + } + break; + case gr_block::TPP_ONE_TO_ONE: + // tags from input i only go to output i + // this requires d->ninputs() == d->noutputs; this is checked when this + // type of tag-propagation system is selected in gr_block_detail + if(d->ninputs() == d->noutputs()) { + for(int i = 0; i < d->ninputs(); i++) { + d->get_tags_in_range(rtags, i, start_nitems_read[i], + d->nitems_read(i)); + + std::vector<gr_tag_t>::iterator t; + for(t = rtags.begin(); t != rtags.end(); t++) { + gr_tag_t new_tag = *t; + new_tag.offset *= rrate; + d->output(i)->add_item_tag(new_tag); + } + } + } + else { + std::cerr << "Error: gr_block_executor: propagation_policy 'ONE-TO-ONE' requires ninputs == noutputs" << std::endl; + return false; + } + + break; + default: + return true; + } + return true; +} + +gr_block_executor::gr_block_executor (gr_block_sptr block, int max_noutput_items) + : d_block(block), d_log(0), d_max_noutput_items(max_noutput_items) +{ + if (ENABLE_LOGGING){ + std::string name = str(boost::format("sst-%03d.log") % which_scheduler++); + d_log = new std::ofstream(name.c_str()); + std::unitbuf(*d_log); // make it unbuffered... + *d_log << "gr_block_executor: " + << d_block << std::endl; + } + +#ifdef GR_PERFORMANCE_COUNTERS + gr_prefs *prefs = gr_prefs::singleton(); + d_use_pc = prefs->get_bool("PerfCounters", "on", false); +#endif /* GR_PERFORMANCE_COUNTERS */ + + d_block->start(); // enable any drivers, etc. +} + +gr_block_executor::~gr_block_executor () +{ + if (ENABLE_LOGGING) + delete d_log; + + d_block->stop(); // stop any drivers, etc. +} + +gr_block_executor::state +gr_block_executor::run_one_iteration() +{ + int noutput_items; + int max_items_avail; + int max_noutput_items = d_max_noutput_items; + int new_alignment=0; + int alignment_state=-1; + + gr_block *m = d_block.get(); + gr_block_detail *d = m->detail().get(); + + LOG(*d_log << std::endl << m); + + if (d->done()){ + assert(0); + return DONE; + } + + if (d->source_p ()){ + d_ninput_items_required.resize (0); + d_ninput_items.resize (0); + d_input_items.resize (0); + d_input_done.resize(0); + d_output_items.resize (d->noutputs ()); + d_start_nitems_read.resize(0); + + // determine the minimum available output space + noutput_items = min_available_space (d, m->output_multiple (), m->min_noutput_items ()); + noutput_items = std::min(noutput_items, max_noutput_items); + LOG(*d_log << " source\n noutput_items = " << noutput_items << std::endl); + if (noutput_items == -1) // we're done + goto were_done; + + if (noutput_items == 0){ // we're output blocked + LOG(*d_log << " BLKD_OUT\n"); + return BLKD_OUT; + } + + goto setup_call_to_work; // jump to common code + } + + else if (d->sink_p ()){ + d_ninput_items_required.resize (d->ninputs ()); + d_ninput_items.resize (d->ninputs ()); + d_input_items.resize (d->ninputs ()); + d_input_done.resize(d->ninputs()); + d_output_items.resize (0); + d_start_nitems_read.resize(d->ninputs()); + LOG(*d_log << " sink\n"); + + max_items_avail = 0; + for (int i = 0; i < d->ninputs (); i++){ + { + /* + * Acquire the mutex and grab local copies of items_available and done. + */ + gruel::scoped_lock guard(*d->input(i)->mutex()); + d_ninput_items[i] = d->input(i)->items_available(); + d_input_done[i] = d->input(i)->done(); + } + + LOG(*d_log << " d_ninput_items[" << i << "] = " << d_ninput_items[i] << std::endl); + LOG(*d_log << " d_input_done[" << i << "] = " << d_input_done[i] << std::endl); + + if (d_ninput_items[i] < m->output_multiple() && d_input_done[i]) + goto were_done; + + max_items_avail = std::max (max_items_avail, d_ninput_items[i]); + } + + // take a swag at how much output we can sink + noutput_items = (int) (max_items_avail * m->relative_rate ()); + noutput_items = round_down (noutput_items, m->output_multiple ()); + noutput_items = std::min(noutput_items, max_noutput_items); + LOG(*d_log << " max_items_avail = " << max_items_avail << std::endl); + LOG(*d_log << " noutput_items = " << noutput_items << std::endl); + + if (noutput_items == 0){ // we're blocked on input + LOG(*d_log << " BLKD_IN\n"); + return BLKD_IN; + } + + goto try_again; // Jump to code shared with regular case. + } + + else { + // do the regular thing + d_ninput_items_required.resize (d->ninputs ()); + d_ninput_items.resize (d->ninputs ()); + d_input_items.resize (d->ninputs ()); + d_input_done.resize(d->ninputs()); + d_output_items.resize (d->noutputs ()); + d_start_nitems_read.resize(d->ninputs()); + + max_items_avail = 0; + for (int i = 0; i < d->ninputs (); i++){ + { + /* + * Acquire the mutex and grab local copies of items_available and done. + */ + gruel::scoped_lock guard(*d->input(i)->mutex()); + d_ninput_items[i] = d->input(i)->items_available (); + d_input_done[i] = d->input(i)->done(); + } + max_items_avail = std::max (max_items_avail, d_ninput_items[i]); + } + + // determine the minimum available output space + noutput_items = min_available_space (d, m->output_multiple (), m->min_noutput_items ()); + if (ENABLE_LOGGING){ + *d_log << " regular "; + if (m->relative_rate() >= 1.0) + *d_log << "1:" << m->relative_rate() << std::endl; + else + *d_log << 1.0/m->relative_rate() << ":1\n"; + *d_log << " max_items_avail = " << max_items_avail << std::endl; + *d_log << " noutput_items = " << noutput_items << std::endl; + } + if (noutput_items == -1) // we're done + goto were_done; + + if (noutput_items == 0){ // we're output blocked + LOG(*d_log << " BLKD_OUT\n"); + return BLKD_OUT; + } + + try_again: + if (m->fixed_rate()){ + // try to work it forward starting with max_items_avail. + // We want to try to consume all the input we've got. + int reqd_noutput_items = m->fixed_rate_ninput_to_noutput(max_items_avail); + + // only test this if we specifically set the output_multiple + if(m->output_multiple_set()) + reqd_noutput_items = round_down(reqd_noutput_items, m->output_multiple()); + + if (reqd_noutput_items > 0 && reqd_noutput_items <= noutput_items) + noutput_items = reqd_noutput_items; + + // if we need this many outputs, overrule the max_noutput_items setting + max_noutput_items = std::max(m->output_multiple(), max_noutput_items); + } + noutput_items = std::min(noutput_items, max_noutput_items); + + // Check if we're still unaligned; use up items until we're + // aligned again. Otherwise, make sure we set the alignment + // requirement. + if(!m->output_multiple_set()) { + if(m->is_unaligned()) { + // When unaligned, don't just set noutput_items to the remaining + // samples to meet alignment; this causes too much overhead in + // requiring a premature call back here. Set the maximum amount + // of samples to handle unalignment and get us back aligned. + if(noutput_items >= m->unaligned()) { + noutput_items = round_up(noutput_items, m->alignment()) \ + - (m->alignment() - m->unaligned()); + new_alignment = 0; + } + else { + new_alignment = m->unaligned() - noutput_items; + } + alignment_state = 0; + } + else if(noutput_items < m->alignment()) { + // if we don't have enough for an aligned call, keep track of + // misalignment, set unaligned flag, and proceed. + new_alignment = m->alignment() - noutput_items; + m->set_unaligned(new_alignment); + m->set_is_unaligned(true); + alignment_state = 1; + } + else { + // enough to round down to the nearest alignment and process. + noutput_items = round_down(noutput_items, m->alignment()); + m->set_is_unaligned(false); + alignment_state = 2; + } + } + + // ask the block how much input they need to produce noutput_items + m->forecast (noutput_items, d_ninput_items_required); + + // See if we've got sufficient input available + + int i; + for (i = 0; i < d->ninputs (); i++) + if (d_ninput_items_required[i] > d_ninput_items[i]) // not enough + break; + + if (i < d->ninputs ()){ // not enough input on input[i] + // if we can, try reducing the size of our output request + if (noutput_items > m->output_multiple ()){ + noutput_items /= 2; + noutput_items = round_up (noutput_items, m->output_multiple ()); + goto try_again; + } + + // We're blocked on input + LOG(*d_log << " BLKD_IN\n"); + if (d_input_done[i]) // If the upstream block is done, we're done + goto were_done; + + // Is it possible to ever fulfill this request? + if (d_ninput_items_required[i] > d->input(i)->max_possible_items_available ()){ + // Nope, never going to happen... + std::cerr << "\nsched: <gr_block " << m->name() + << " (" << m->unique_id() << ")>" + << " is requesting more input data\n" + << " than we can provide.\n" + << " ninput_items_required = " + << d_ninput_items_required[i] << "\n" + << " max_possible_items_available = " + << d->input(i)->max_possible_items_available() << "\n" + << " If this is a filter, consider reducing the number of taps.\n"; + goto were_done; + } + + // If we were made unaligned in this round but return here without + // processing; reset the unalignment claim before next entry. + if(alignment_state == 1) { + m->set_unaligned(0); + m->set_is_unaligned(false); + } + return BLKD_IN; + } + + // We've got enough data on each input to produce noutput_items. + // Finish setting up the call to work. + + for (int i = 0; i < d->ninputs (); i++) + d_input_items[i] = d->input(i)->read_pointer(); + + setup_call_to_work: + + d->d_produce_or = 0; + for (int i = 0; i < d->noutputs (); i++) + d_output_items[i] = d->output(i)->write_pointer(); + + // determine where to start looking for new tags + for (int i = 0; i < d->ninputs(); i++) + d_start_nitems_read[i] = d->nitems_read(i); + +#ifdef GR_PERFORMANCE_COUNTERS + if(d_use_pc) + d->start_perf_counters(); +#endif /* GR_PERFORMANCE_COUNTERS */ + + // Do the actual work of the block + int n = m->general_work (noutput_items, d_ninput_items, + d_input_items, d_output_items); + +#ifdef GR_PERFORMANCE_COUNTERS + if(d_use_pc) + d->stop_perf_counters(noutput_items, n); +#endif /* GR_PERFORMANCE_COUNTERS */ + + LOG(*d_log << " general_work: noutput_items = " << noutput_items + << " result = " << n << std::endl); + + // Adjust number of unaligned items left to process + if(m->is_unaligned()) { + m->set_unaligned(new_alignment); + m->set_is_unaligned(m->unaligned() != 0); + } + + if(!propagate_tags(m->tag_propagation_policy(), d, + d_start_nitems_read, m->relative_rate(), + d_returned_tags)) + goto were_done; + + if (n == gr_block::WORK_DONE) + goto were_done; + + if (n != gr_block::WORK_CALLED_PRODUCE) + d->produce_each (n); // advance write pointers + + if (d->d_produce_or > 0) // block produced something + return READY; + + // We didn't produce any output even though we called general_work. + // We have (most likely) consumed some input. + + /* + // If this is a source, it's broken. + if (d->source_p()){ + std::cerr << "gr_block_executor: source " << m + << " produced no output. We're marking it DONE.\n"; + // FIXME maybe we ought to raise an exception... + goto were_done; + } + */ + + // Have the caller try again... + return READY_NO_OUTPUT; + } + assert (0); + + were_done: + LOG(*d_log << " were_done\n"); + d->set_done (true); + return DONE; +} diff --git a/gnuradio-core/src/lib/runtime/gr_block_executor.h b/gnuradio-core/src/lib/runtime/gr_block_executor.h new file mode 100644 index 000000000..fb7f9c269 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_block_executor.h @@ -0,0 +1,78 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_BLOCK_EXECUTOR_H +#define INCLUDED_GR_BLOCK_EXECUTOR_H + +#include <gr_core_api.h> +#include <gr_runtime_types.h> +#include <fstream> +#include <gr_tags.h> + +//class gr_block_executor; +//typedef boost::shared_ptr<gr_block_executor> gr_block_executor_sptr; + + +/*! + * \brief Manage the execution of a single block. + * \ingroup internal + */ + +class GR_CORE_API gr_block_executor { +protected: + gr_block_sptr d_block; // The block we're trying to run + std::ofstream *d_log; + + // These are allocated here so we don't have to on each iteration + + gr_vector_int d_ninput_items_required; + gr_vector_int d_ninput_items; + gr_vector_const_void_star d_input_items; + std::vector<bool> d_input_done; + gr_vector_void_star d_output_items; + std::vector<uint64_t> d_start_nitems_read; //stores where tag counts are before work + std::vector<gr_tag_t> d_returned_tags; + int d_max_noutput_items; + +#ifdef GR_PERFORMANCE_COUNTERS + bool d_use_pc; +#endif /* GR_PERFORMANCE_COUNTERS */ + + public: + gr_block_executor(gr_block_sptr block, int max_noutput_items=100000); + ~gr_block_executor (); + + enum state { + READY, // We made progress; everything's cool. + READY_NO_OUTPUT, // We consumed some input, but produced no output. + BLKD_IN, // no progress; we're blocked waiting for input data. + BLKD_OUT, // no progress; we're blocked waiting for output buffer space. + DONE, // we're done; don't call me again. + }; + + /* + * \brief Run one iteration. + */ + state run_one_iteration(); +}; + +#endif /* INCLUDED_GR_BLOCK_EXECUTOR_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_block_registry.cc b/gnuradio-core/src/lib/runtime/gr_block_registry.cc new file mode 100644 index 000000000..ff23d97eb --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_block_registry.cc @@ -0,0 +1,76 @@ +#include <gr_basic_block.h> +#include <gr_block_registry.h> +#include <gr_tpb_detail.h> +#include <gr_block_detail.h> +#include <gr_block.h> +#include <stdio.h> + +gr_block_registry global_block_registry; + +gr_block_registry::gr_block_registry(){ + d_ref_map = pmt::pmt_make_dict(); +} + +long gr_block_registry::block_register(gr_basic_block* block){ + if(d_map.find(block->name()) == d_map.end()){ + d_map[block->name()] = blocksubmap_t(); + d_map[block->name()][0] = block; + return 0; + } else { + for(size_t i=0; i<=d_map[block->name()].size(); i++){ + if(d_map[block->name()].find(i) == d_map[block->name()].end()){ + d_map[block->name()][i] = block; + return i; + } + } + } + throw std::runtime_error("should not reach this"); +} + +void gr_block_registry::block_unregister(gr_basic_block* block){ + d_map[block->name()].erase( d_map[block->name()].find(block->symbolic_id())); + d_ref_map = pmt::pmt_dict_delete(d_ref_map, pmt::pmt_intern(block->symbol_name())); + if(block->alias_set()){ + d_ref_map = pmt::pmt_dict_delete(d_ref_map, pmt::pmt_intern(block->alias())); + } +} + +std::string gr_block_registry::register_symbolic_name(gr_basic_block* block){ + std::stringstream ss; + ss << block->name() << block->symbolic_id(); + //std::cout << "register_symbolic_name: " << ss.str() << std::endl; + register_symbolic_name(block, ss.str()); + return ss.str(); +} + +void gr_block_registry::register_symbolic_name(gr_basic_block* block, std::string name){ + if(pmt_dict_has_key(d_ref_map, pmt::pmt_intern(name))){ + throw std::runtime_error("symbol already exists, can not re-use!"); + } + d_ref_map = pmt_dict_add(d_ref_map, pmt::pmt_intern(name), pmt::pmt_make_any(block)); +} + +gr_basic_block_sptr gr_block_registry::block_lookup(pmt::pmt_t symbol){ + pmt::pmt_t ref = pmt_dict_ref(d_ref_map, symbol, pmt::PMT_NIL); + if(pmt::pmt_eq(ref, pmt::PMT_NIL)){ + throw std::runtime_error("block lookup failed! block not found!"); + } + gr_basic_block* blk = boost::any_cast<gr_basic_block*>( pmt::pmt_any_ref(ref) ); + return blk->shared_from_this(); +} + + +void gr_block_registry::register_primitive(std::string blk, gr_block* ref){ + primitive_map[blk] = ref; +} + +void gr_block_registry::unregister_primitive(std::string blk){ + primitive_map.erase(primitive_map.find(blk)); +} + +void gr_block_registry::notify_blk(std::string blk){ + if(primitive_map.find(blk) == primitive_map.end()){ return; } + if(primitive_map[blk]->detail().get()) + primitive_map[blk]->detail()->d_tpb.notify_msg(); +} + diff --git a/gnuradio-core/src/lib/runtime/gr_block_registry.h b/gnuradio-core/src/lib/runtime/gr_block_registry.h new file mode 100644 index 000000000..3a9d9868f --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_block_registry.h @@ -0,0 +1,43 @@ +#ifndef GR_BLOCK_REGISTRY_H +#define GR_BLOCK_REGISTRY_H + +#include <map> +#include <gr_basic_block.h> + +#ifndef GR_BASIC_BLOCK_H +class gr_basic_block; +class gr_block; +#endif + +class gr_block_registry { + public: + gr_block_registry(); + + long block_register(gr_basic_block* block); + void block_unregister(gr_basic_block* block); + + std::string register_symbolic_name(gr_basic_block* block); + void register_symbolic_name(gr_basic_block* block, std::string name); + + gr_basic_block_sptr block_lookup(pmt::pmt_t symbol); + + void register_primitive(std::string blk, gr_block* ref); + void unregister_primitive(std::string blk); + void notify_blk(std::string blk); + + private: + + //typedef std::map< long, gr_basic_block_sptr > blocksubmap_t; + typedef std::map< long, gr_basic_block* > blocksubmap_t; + typedef std::map< std::string, blocksubmap_t > blockmap_t; + + blockmap_t d_map; + pmt::pmt_t d_ref_map; + std::map< std::string, gr_block*> primitive_map; + +}; + +extern gr_block_registry global_block_registry; + +#endif + diff --git a/gnuradio-core/src/lib/runtime/gr_buffer.cc b/gnuradio-core/src/lib/runtime/gr_buffer.cc new file mode 100644 index 000000000..369959d65 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_buffer.cc @@ -0,0 +1,347 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2009,2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_buffer.h> +#include <gr_vmcircbuf.h> +#include <gr_math.h> +#include <stdexcept> +#include <iostream> +#include <assert.h> +#include <algorithm> +#include <boost/math/common_factor_rt.hpp> + +static long s_buffer_count = 0; // counts for debugging storage mgmt +static long s_buffer_reader_count = 0; + +// ---------------------------------------------------------------------------- +// Notes on storage management +// +// Pretty much all the fundamental classes are now using the +// shared_ptr stuff for automatic reference counting. To ensure that +// no mistakes are made, we make the constructors for classes private, +// and then provide a free factory function that returns a smart +// pointer to the desired class. +// +// gr_buffer and gr_buffer_reader are no exceptions. However, they +// both want pointers to each other, and unless we do something, we'll +// never delete any of them because of the circular structure. +// They'll always have a reference count of at least one. We could +// use boost::weak_ptr's from gr_buffer to gr_buffer_reader but that +// introduces it's own problems. (gr_buffer_reader's destructor needs +// to call gr_buffer::drop_reader, but has no easy way to get a +// shared_ptr to itself.) +// +// Instead, we solve this problem by having gr_buffer hold a raw +// pointer to gr_buffer_reader in its d_reader vector. +// gr_buffer_reader's destructor calls gr_buffer::drop_reader, so +// we're never left with an dangling pointer. gr_buffer_reader still +// has a shared_ptr to the buffer ensuring that the buffer doesn't go +// away under it. However, when the reference count of a +// gr_buffer_reader goes to zero, we can successfully reclaim it. +// ---------------------------------------------------------------------------- + + +/* + * Compute the minimum number of buffer items that work (i.e., + * address space wrap-around works). To work is to satisfy this + * contraint for integer buffer_size and k: + * + * type_size * nitems == k * page_size + */ +static long +minimum_buffer_items (long type_size, long page_size) +{ + return page_size / boost::math::gcd (type_size, page_size); +} + + +gr_buffer::gr_buffer (int nitems, size_t sizeof_item, gr_block_sptr link) + : d_base (0), d_bufsize (0), d_vmcircbuf (0), + d_sizeof_item (sizeof_item), d_link(link), + d_write_index (0), d_abs_write_offset(0), d_done (false), + d_last_min_items_read(0) +{ + if (!allocate_buffer (nitems, sizeof_item)) + throw std::bad_alloc (); + + s_buffer_count++; +} + +gr_buffer_sptr +gr_make_buffer (int nitems, size_t sizeof_item, gr_block_sptr link) +{ + return gr_buffer_sptr (new gr_buffer (nitems, sizeof_item, link)); +} + +gr_buffer::~gr_buffer () +{ + delete d_vmcircbuf; + assert (d_readers.size() == 0); + s_buffer_count--; +} + +/*! + * sets d_vmcircbuf, d_base, d_bufsize. + * returns true iff successful. + */ +bool +gr_buffer::allocate_buffer (int nitems, size_t sizeof_item) +{ + int orig_nitems = nitems; + + // Any buffersize we come up with must be a multiple of min_nitems. + + int granularity = gr_vmcircbuf_sysconfig::granularity (); + int min_nitems = minimum_buffer_items (sizeof_item, granularity); + + // Round-up nitems to a multiple of min_nitems. + + if (nitems % min_nitems != 0) + nitems = ((nitems / min_nitems) + 1) * min_nitems; + + // If we rounded-up a whole bunch, give the user a heads up. + // This only happens if sizeof_item is not a power of two. + + if (nitems > 2 * orig_nitems && nitems * (int) sizeof_item > granularity){ + std::cerr << "gr_buffer::allocate_buffer: warning: tried to allocate\n" + << " " << orig_nitems << " items of size " + << sizeof_item << ". Due to alignment requirements\n" + << " " << nitems << " were allocated. If this isn't OK, consider padding\n" + << " your structure to a power-of-two bytes.\n" + << " On this platform, our allocation granularity is " << granularity << " bytes.\n"; + } + + d_bufsize = nitems; + d_vmcircbuf = gr_vmcircbuf_sysconfig::make (d_bufsize * d_sizeof_item); + if (d_vmcircbuf == 0){ + std::cerr << "gr_buffer::allocate_buffer: failed to allocate buffer of size " + << d_bufsize * d_sizeof_item / 1024 << " KB\n"; + return false; + } + + d_base = (char *) d_vmcircbuf->pointer_to_first_copy (); + return true; +} + + +int +gr_buffer::space_available () +{ + if (d_readers.empty ()) + return d_bufsize - 1; // See comment below + + else { + + // Find out the maximum amount of data available to our readers + + int most_data = d_readers[0]->items_available (); + uint64_t min_items_read = d_readers[0]->nitems_read(); + for (size_t i = 1; i < d_readers.size (); i++) { + most_data = std::max (most_data, d_readers[i]->items_available ()); + min_items_read = std::min(min_items_read, d_readers[i]->nitems_read()); + } + + if(min_items_read != d_last_min_items_read) { + prune_tags(d_last_min_items_read); + d_last_min_items_read = min_items_read; + } + + // The -1 ensures that the case d_write_index == d_read_index is + // unambiguous. It indicates that there is no data for the reader + + return d_bufsize - most_data - 1; + } +} + +void * +gr_buffer::write_pointer () +{ + return &d_base[d_write_index * d_sizeof_item]; +} + +void +gr_buffer::update_write_pointer (int nitems) +{ + gruel::scoped_lock guard(*mutex()); + d_write_index = index_add (d_write_index, nitems); + d_abs_write_offset += nitems; +} + +void +gr_buffer::set_done (bool done) +{ + gruel::scoped_lock guard(*mutex()); + d_done = done; +} + +gr_buffer_reader_sptr +gr_buffer_add_reader (gr_buffer_sptr buf, int nzero_preload, gr_block_sptr link) +{ + if (nzero_preload < 0) + throw std::invalid_argument("gr_buffer_add_reader: nzero_preload must be >= 0"); + + gr_buffer_reader_sptr r (new gr_buffer_reader (buf, + buf->index_sub(buf->d_write_index, + nzero_preload), + link)); + buf->d_readers.push_back (r.get ()); + + return r; +} + +void +gr_buffer::drop_reader (gr_buffer_reader *reader) +{ + // isn't C++ beautiful... GAG! + + std::vector<gr_buffer_reader *>::iterator result = + std::find (d_readers.begin (), d_readers.end (), reader); + + if (result == d_readers.end ()) + throw std::invalid_argument ("gr_buffer::drop_reader"); // we didn't find it... + + d_readers.erase (result); +} + +void +gr_buffer::add_item_tag(const gr_tag_t &tag) +{ + gruel::scoped_lock guard(*mutex()); + d_item_tags.push_back(tag); +} + +void +gr_buffer::remove_item_tag(const gr_tag_t &tag) +{ + gruel::scoped_lock guard(*mutex()); + for (std::deque<gr_tag_t>::iterator it = d_item_tags.begin(); it != d_item_tags.end(); ++it) { + if (*it == tag) { + d_item_tags.erase(it); + break; + } + } +} + +void +gr_buffer::prune_tags(uint64_t max_time) +{ + /* NOTE: this function _should_ lock the mutex before editing + d_item_tags. In practice, this function is only called at + runtime by min_available_space in gr_block_executor.cc, + which locks the mutex itself. + + If this function is used elsewhere, remember to lock the + buffer's mutex al la the scoped_lock line below. + */ + //gruel::scoped_lock guard(*mutex()); + std::deque<gr_tag_t>::iterator itr = d_item_tags.begin(); + + uint64_t item_time; + + // Since tags are not guarenteed to be in any particular order, + // we need to erase here instead of pop_front. An erase in the + // middle invalidates all iterators; so this resets the iterator + // to find more. Mostly, we wil be erasing from the front and + // therefore lose little time this way. + while(itr != d_item_tags.end()) { + item_time = (*itr).offset; + if(item_time < max_time) { + d_item_tags.erase(itr); + itr = d_item_tags.begin(); + } + else + itr++; + } +} + +long +gr_buffer_ncurrently_allocated () +{ + return s_buffer_count; +} + +// ---------------------------------------------------------------------------- + +gr_buffer_reader::gr_buffer_reader(gr_buffer_sptr buffer, unsigned int read_index, + gr_block_sptr link) + : d_buffer(buffer), d_read_index(read_index), d_abs_read_offset(0), d_link(link) +{ + s_buffer_reader_count++; +} + +gr_buffer_reader::~gr_buffer_reader () +{ + d_buffer->drop_reader(this); + s_buffer_reader_count--; +} + +int +gr_buffer_reader::items_available () const +{ + return d_buffer->index_sub (d_buffer->d_write_index, d_read_index); +} + +const void * +gr_buffer_reader::read_pointer () +{ + return &d_buffer->d_base[d_read_index * d_buffer->d_sizeof_item]; +} + +void +gr_buffer_reader::update_read_pointer (int nitems) +{ + gruel::scoped_lock guard(*mutex()); + d_read_index = d_buffer->index_add (d_read_index, nitems); + d_abs_read_offset += nitems; +} + +void +gr_buffer_reader::get_tags_in_range(std::vector<gr_tag_t> &v, + uint64_t abs_start, + uint64_t abs_end) +{ + gruel::scoped_lock guard(*mutex()); + + v.resize(0); + std::deque<gr_tag_t>::iterator itr = d_buffer->get_tags_begin(); + + uint64_t item_time; + while(itr != d_buffer->get_tags_end()) { + item_time = (*itr).offset; + + if((item_time >= abs_start) && (item_time < abs_end)) { + v.push_back(*itr); + } + + itr++; + } +} + +long +gr_buffer_reader_ncurrently_allocated () +{ + return s_buffer_reader_count; +} diff --git a/gnuradio-core/src/lib/runtime/gr_buffer.h b/gnuradio-core/src/lib/runtime/gr_buffer.h new file mode 100644 index 000000000..28ea97726 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_buffer.h @@ -0,0 +1,309 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2009,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_BUFFER_H +#define INCLUDED_GR_BUFFER_H + +#include <gr_core_api.h> +#include <gr_runtime_types.h> +#include <boost/weak_ptr.hpp> +#include <gruel/thread.h> +#include <gr_tags.h> +#include <deque> + +class gr_vmcircbuf; + +/*! + * \brief Allocate a buffer that holds at least \p nitems of size \p sizeof_item. + * + * The total size of the buffer will be rounded up to a system + * dependent boundary. This is typically the system page size, but + * under MS windows is 64KB. + * + * \param nitems is the minimum number of items the buffer will hold. + * \param sizeof_item is the size of an item in bytes. + * \param link is the block that writes to this buffer. + */ +GR_CORE_API gr_buffer_sptr gr_make_buffer (int nitems, size_t sizeof_item, gr_block_sptr link=gr_block_sptr()); + + +/*! + * \brief Single writer, multiple reader fifo. + * \ingroup internal + */ +class GR_CORE_API gr_buffer { + public: + + virtual ~gr_buffer (); + + /*! + * \brief return number of items worth of space available for writing + */ + int space_available (); + + /*! + * \brief return size of this buffer in items + */ + int bufsize() const { return d_bufsize; } + + /*! + * \brief return pointer to write buffer. + * + * The return value points at space that can hold at least + * space_available() items. + */ + void *write_pointer (); + + /*! + * \brief tell buffer that we wrote \p nitems into it + */ + void update_write_pointer (int nitems); + + void set_done (bool done); + bool done () const { return d_done; } + + /*! + * \brief Return the block that writes to this buffer. + */ + gr_block_sptr link() { return gr_block_sptr(d_link); } + + size_t nreaders() const { return d_readers.size(); } + gr_buffer_reader* reader(size_t index) { return d_readers[index]; } + + gruel::mutex *mutex() { return &d_mutex; } + + uint64_t nitems_written() { return d_abs_write_offset; } + + size_t get_sizeof_item() { return d_sizeof_item; } + + /*! + * \brief Adds a new tag to the buffer. + * + * \param tag the new tag + */ + void add_item_tag(const gr_tag_t &tag); + + /*! + * \brief Removes an existing tag from the buffer. + * + * If no such tag is found, does nothing. + * + * \param tag the tag that needs to be removed + */ + void remove_item_tag(const gr_tag_t &tag); + + /*! + * \brief Removes all tags before \p max_time from buffer + * + * \param max_time the time (item number) to trim up until. + */ + void prune_tags(uint64_t max_time); + + std::deque<gr_tag_t>::iterator get_tags_begin() { return d_item_tags.begin(); } + std::deque<gr_tag_t>::iterator get_tags_end() { return d_item_tags.end(); } + + // ------------------------------------------------------------------------- + + private: + + friend class gr_buffer_reader; + friend GR_CORE_API gr_buffer_sptr gr_make_buffer (int nitems, size_t sizeof_item, gr_block_sptr link); + friend GR_CORE_API gr_buffer_reader_sptr gr_buffer_add_reader (gr_buffer_sptr buf, int nzero_preload, gr_block_sptr link); + + protected: + char *d_base; // base address of buffer + unsigned int d_bufsize; // in items + private: + gr_vmcircbuf *d_vmcircbuf; + size_t d_sizeof_item; // in bytes + std::vector<gr_buffer_reader *> d_readers; + boost::weak_ptr<gr_block> d_link; // block that writes to this buffer + + // + // The mutex protects d_write_index, d_abs_write_offset, d_done, d_item_tags + // and the d_read_index's and d_abs_read_offset's in the buffer readers. + // + gruel::mutex d_mutex; + unsigned int d_write_index; // in items [0,d_bufsize) + uint64_t d_abs_write_offset; // num items written since the start + bool d_done; + std::deque<gr_tag_t> d_item_tags; + uint64_t d_last_min_items_read; + + unsigned + index_add (unsigned a, unsigned b) + { + unsigned s = a + b; + + if (s >= d_bufsize) + s -= d_bufsize; + + assert (s < d_bufsize); + return s; + } + + unsigned + index_sub (unsigned a, unsigned b) + { + int s = a - b; + + if (s < 0) + s += d_bufsize; + + assert ((unsigned) s < d_bufsize); + return s; + } + + virtual bool allocate_buffer (int nitems, size_t sizeof_item); + + /*! + * \brief constructor is private. Use gr_make_buffer to create instances. + * + * Allocate a buffer that holds at least \p nitems of size \p sizeof_item. + * + * \param nitems is the minimum number of items the buffer will hold. + * \param sizeof_item is the size of an item in bytes. + * \param link is the block that writes to this buffer. + * + * The total size of the buffer will be rounded up to a system + * dependent boundary. This is typically the system page size, but + * under MS windows is 64KB. + */ + gr_buffer (int nitems, size_t sizeof_item, gr_block_sptr link); + + /*! + * \brief disassociate \p reader from this buffer + */ + void drop_reader (gr_buffer_reader *reader); + +}; + +/*! + * \brief Create a new gr_buffer_reader and attach it to buffer \p buf + * \param buf is the buffer the \p gr_buffer_reader reads from. + * \param nzero_preload -- number of zero items to "preload" into buffer. + * \param link is the block that reads from the buffer using this gr_buffer_reader. + */ +GR_CORE_API gr_buffer_reader_sptr +gr_buffer_add_reader (gr_buffer_sptr buf, int nzero_preload, gr_block_sptr link=gr_block_sptr()); + +//! returns # of gr_buffers currently allocated +GR_CORE_API long gr_buffer_ncurrently_allocated (); + + +// --------------------------------------------------------------------------- + +/*! + * \brief How we keep track of the readers of a gr_buffer. + * \ingroup internal + */ + +class GR_CORE_API gr_buffer_reader { + public: + + ~gr_buffer_reader (); + + /*! + * \brief Return number of items available for reading. + */ + int items_available () const; + + /*! + * \brief Return buffer this reader reads from. + */ + gr_buffer_sptr buffer () const { return d_buffer; } + + + /*! + * \brief Return maximum number of items that could ever be available for reading. + * This is used as a sanity check in the scheduler to avoid looping forever. + */ + int max_possible_items_available () const { return d_buffer->d_bufsize - 1; } + + /*! + * \brief return pointer to read buffer. + * + * The return value points to items_available() number of items + */ + const void *read_pointer (); + + /* + * \brief tell buffer we read \p items from it + */ + void update_read_pointer (int nitems); + + void set_done (bool done) { d_buffer->set_done (done); } + bool done () const { return d_buffer->done (); } + + gruel::mutex *mutex() { return d_buffer->mutex(); } + + + uint64_t nitems_read() { return d_abs_read_offset; } + + size_t get_sizeof_item() { return d_buffer->get_sizeof_item(); } + + /*! + * \brief Return the block that reads via this reader. + * + */ + gr_block_sptr link() { return gr_block_sptr(d_link); } + + + /*! + * \brief Given a [start,end), returns a vector all tags in the range. + * + * Get a vector of tags in given range. Range of counts is from start to end-1. + * + * Tags are tuples of: + * (item count, source id, key, value) + * + * \param v a vector reference to return tags into + * \param abs_start a uint64 count of the start of the range of interest + * \param abs_end a uint64 count of the end of the range of interest + */ + void get_tags_in_range(std::vector<gr_tag_t> &v, + uint64_t abs_start, + uint64_t abs_end); + + // ------------------------------------------------------------------------- + + private: + + friend class gr_buffer; + friend GR_CORE_API gr_buffer_reader_sptr + gr_buffer_add_reader (gr_buffer_sptr buf, int nzero_preload, gr_block_sptr link); + + + gr_buffer_sptr d_buffer; + unsigned int d_read_index; // in items [0,d->buffer.d_bufsize) + uint64_t d_abs_read_offset; // num items seen since the start + boost::weak_ptr<gr_block> d_link; // block that reads via this buffer reader + + //! constructor is private. Use gr_buffer::add_reader to create instances + gr_buffer_reader (gr_buffer_sptr buffer, unsigned int read_index, gr_block_sptr link); +}; + +//! returns # of gr_buffer_readers currently allocated +GR_CORE_API long gr_buffer_reader_ncurrently_allocated (); + + +#endif /* INCLUDED_GR_BUFFER_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_buffer.i b/gnuradio-core/src/lib/runtime/gr_buffer.i new file mode 100644 index 000000000..390a94e05 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_buffer.i @@ -0,0 +1,63 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +class gr_buffer; +typedef boost::shared_ptr<gr_buffer> gr_buffer_sptr; +%template(gr_buffer_sptr) boost::shared_ptr<gr_buffer>; +%rename(buffer) gr_make_buffer; +%ignore gr_buffer; + +gr_buffer_sptr gr_make_buffer (int nitems, size_t sizeof_item, gr_block_sptr link); + +class gr_buffer { + public: + ~gr_buffer (); + + private: + gr_buffer (int nitems, size_t sizeof_item, gr_block_sptr link); +}; + + +class gr_buffer_reader; +typedef boost::shared_ptr<gr_buffer_reader> gr_buffer_reader_sptr; +%template(gr_buffer_reader_sptr) boost::shared_ptr<gr_buffer_reader>; +%ignore gr_buffer_reader; + +%rename(buffer_add_reader) gr_buffer_add_reader; +gr_buffer_reader_sptr gr_buffer_add_reader (gr_buffer_sptr buf, int nzero_preload, gr_block_sptr link); + +class gr_buffer_reader { + public: + ~gr_buffer_reader (); + + private: + friend class gr_buffer; + gr_buffer_reader (gr_buffer_sptr buffer, unsigned int read_index, gr_block_sptr link); +}; + + +%rename(buffer_ncurrently_allocated) gr_buffer_ncurrently_allocated; +long gr_buffer_ncurrently_allocated (); + +%rename(buffer_reader_ncurrently_allocated) gr_buffer_reader_ncurrently_allocated; +long gr_buffer_reader_ncurrently_allocated (); + diff --git a/gnuradio-core/src/lib/runtime/gr_complex.h b/gnuradio-core/src/lib/runtime/gr_complex.h new file mode 100644 index 000000000..58d1525b4 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_complex.h @@ -0,0 +1,46 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_GR_COMPLEX_H +#define INCLUDED_GR_COMPLEX_H + +#include <complex> +typedef std::complex<float> gr_complex; +typedef std::complex<double> gr_complexd; + + +inline bool is_complex (gr_complex x) { (void) x; return true;} +inline bool is_complex (gr_complexd x) { (void) x; return true;} +inline bool is_complex (float x) { (void) x; return false;} +inline bool is_complex (double x) { (void) x; return false;} +inline bool is_complex (int x) { (void) x; return false;} +inline bool is_complex (char x) { (void) x; return false;} +inline bool is_complex (short x) { (void) x; return false;} + + +// this doesn't really belong here, but there are worse places for it... + +#define CPPUNIT_ASSERT_COMPLEXES_EQUAL(expected,actual,delta) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL (expected.real(), actual.real(), delta); \ + CPPUNIT_ASSERT_DOUBLES_EQUAL (expected.imag(), actual.imag(), delta); + +#endif /* INCLUDED_GR_COMPLEX_H */ + diff --git a/gnuradio-core/src/lib/runtime/gr_dispatcher.cc b/gnuradio-core/src/lib/runtime/gr_dispatcher.cc new file mode 100644 index 000000000..96ebe9ad8 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_dispatcher.cc @@ -0,0 +1,193 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_dispatcher.h> +#include <math.h> +#include <errno.h> +#include <stdio.h> + +#ifdef HAVE_SELECT +# ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> +# else +# ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +# endif +# ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +# endif +# ifdef HAVE_UNISTD_H +# include <unistd.h> +# endif +# endif +#endif + + +static gr_dispatcher_sptr s_singleton; + +gr_dispatcher_sptr +gr_make_dispatcher() +{ + return gr_dispatcher_sptr(new gr_dispatcher()); +} + +gr_dispatcher_sptr +gr_dispatcher_singleton() +{ + if (s_singleton) + return s_singleton; + + s_singleton = gr_make_dispatcher(); + return s_singleton; +} + +#if !defined(HAVE_SELECT) // Stub it out + +gr_dispatcher::gr_dispatcher() +{ +} + +gr_dispatcher::~gr_dispatcher() +{ +} + +bool +gr_dispatcher::add_handler(gr_select_handler_sptr handler) +{ + return true; +} + +bool +gr_dispatcher::del_handler(gr_select_handler_sptr handler) +{ + return true; +} + +bool +gr_dispatcher::del_handler(gr_select_handler *handler) +{ + return true; +} + +void +gr_dispatcher::loop(double timeout) +{ +} + +#else // defined(HAVE_SELECT) + +gr_dispatcher::gr_dispatcher() + : d_handler(FD_SETSIZE), d_max_index(-1) +{ +} + +gr_dispatcher::~gr_dispatcher() +{ +} + +bool +gr_dispatcher::add_handler(gr_select_handler_sptr handler) +{ + int fd = handler->fd(); + if (fd < 0 || fd >= FD_SETSIZE) + return false; + + d_max_index = std::max(d_max_index, fd); + d_handler[fd] = handler; + return true; +} + +bool +gr_dispatcher::del_handler(gr_select_handler_sptr handler) +{ + return del_handler(handler.get()); +} + +bool +gr_dispatcher::del_handler(gr_select_handler *handler) +{ + int fd = handler->fd(); + if (fd < 0 || fd >= FD_SETSIZE) + return false; + + d_handler[fd].reset(); + + if (fd == d_max_index){ + int i; + for (i = fd - 1; i >= 0 && !d_handler[i]; i--) + ; + d_max_index = i; + } + return true; +} + + +void +gr_dispatcher::loop(double timeout) +{ + struct timeval master; + struct timeval tmp; + fd_set rd_set; + fd_set wr_set; + + double secs = floor (timeout); + master.tv_sec = (long) secs; + master.tv_usec = (long) ((timeout - secs) * 1e6); + + while (d_max_index >= 0){ + FD_ZERO(&rd_set); + FD_ZERO(&wr_set); + + for (int i = 0; i <= d_max_index; i++){ + if (d_handler[i] && d_handler[i]->readable()) + FD_SET(i, &rd_set); + if (d_handler[i] && d_handler[i]->writable()) + FD_SET(i, &wr_set); + } + + tmp = master; + int retval = select(d_max_index+1, &rd_set, &wr_set, 0, &tmp); + if (retval == 0) // timed out with nothing ready + continue; + if (retval < 0){ + if (errno == EINTR) + continue; + perror ("gr_dispatcher/select"); + return; + } + + for (int i = 0; i <= d_max_index; i++){ + if (FD_ISSET(i, &rd_set)) + if (d_handler[i]) + d_handler[i]->handle_read(); + if (FD_ISSET(i, &wr_set)) + if (d_handler[i]) + d_handler[i]->handle_write(); + } + } +} + +#endif diff --git a/gnuradio-core/src/lib/runtime/gr_dispatcher.h b/gnuradio-core/src/lib/runtime/gr_dispatcher.h new file mode 100644 index 000000000..72a0b0176 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_dispatcher.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_DISPATCHER_H +#define INCLUDED_GR_DISPATCHER_H + +#include <gr_core_api.h> +#include <gr_select_handler.h> +#include <vector> + +class gr_dispatcher; +typedef boost::shared_ptr<gr_dispatcher> gr_dispatcher_sptr; + +GR_CORE_API gr_dispatcher_sptr gr_dispatcher_singleton(); +GR_CORE_API gr_dispatcher_sptr gr_make_dispatcher(); + +/*! + * \brief invoke callbacks based on select. + * \ingroup internal + * + * \sa gr_select_handler + */ +class GR_CORE_API gr_dispatcher +{ + gr_dispatcher(); + friend GR_CORE_API gr_dispatcher_sptr gr_make_dispatcher(); + + std::vector<gr_select_handler_sptr> d_handler; + int d_max_index; + +public: + ~gr_dispatcher(); + + bool add_handler(gr_select_handler_sptr handler); + bool del_handler(gr_select_handler_sptr handler); + bool del_handler(gr_select_handler *handler); + + /*! + * \brief Event dispatching loop. + * + * Enter a polling loop that only terminates after all gr_select_handlers + * have been removed. \p timeout sets the timeout parameter to the select() + * call, measured in seconds. + * + * \param timeout maximum number of seconds to block in select. + */ + void loop(double timeout=10); +}; + +#endif /* INCLUDED_GR_DISPATCHER_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_dispatcher.i b/gnuradio-core/src/lib/runtime/gr_dispatcher.i new file mode 100644 index 000000000..28737cd31 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_dispatcher.i @@ -0,0 +1,55 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +class gr_dispatcher; +typedef boost::shared_ptr<gr_dispatcher> gr_dispatcher_sptr; +%template(gr_dispatcher_sptr) boost::shared_ptr<gr_dispatcher>; + +%rename(dispatcher) gr_make_dispatcher; +gr_dispatcher_sptr gr_make_dispatcher(); + +%rename(dispatcher_singleton) gr_dispatcher_singleton; +gr_dispatcher_sptr gr_dispatcher_singleton(); + +/*! + * \brief invoke callbacks based on select. + * + * \sa gr_select_handler + */ +class gr_dispatcher +{ + gr_dispatcher(); + +public: + ~gr_dispatcher(); + + /*! + * \brief Event dispatching loop. + * + * Enter a polling loop that only terminates after all gr_select_handlers + * have been removed. \p timeout sets the timeout parameter to the select() + * call, measured in seconds. + * + * \param timeout maximum number of seconds to block in select. + */ + void loop(double timeout=10); +}; diff --git a/gnuradio-core/src/lib/runtime/gr_error_handler.cc b/gnuradio-core/src/lib/runtime/gr_error_handler.cc new file mode 100644 index 000000000..448682966 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_error_handler.cc @@ -0,0 +1,244 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +/* + * This code is based on error.cc from the "Click Modular Router". + * Original copyright follows: + */ +/* + * error.{cc,hh} -- flexible classes for error reporting + * Eddie Kohler + * + * Copyright (c) 1999-2000 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, subject to the conditions + * listed in the Click LICENSE file. These conditions include: you must + * preserve this copyright notice, and you cannot mention the copyright + * holders in advertising related to the Software without their permission. + * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This + * notice is a summary of the Click LICENSE file; the license in that file is + * legally binding. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_error_handler.h> +#include <assert.h> +#include <stdexcept> +#include <unistd.h> + +#ifdef HAVE_IO_H +#include <io.h> +#endif + +static gr_error_handler *s_default_handler = 0; +static gr_error_handler *s_silent_handler = 0; + +bool +gr_error_handler::has_default_handler() +{ + return s_default_handler != 0; +} + +void +gr_error_handler::set_default_handler(gr_error_handler *errh) +{ + s_default_handler = errh; +} + +gr_error_handler * +gr_error_handler::default_handler() +{ + assert (s_default_handler != 0); + return s_default_handler; +} + +gr_error_handler * +gr_error_handler::silent_handler() +{ + assert (s_silent_handler != 0); + return s_silent_handler; +} + +// ---------------------------------------------------------------- + +gr_error_handler::~gr_error_handler() +{ + // nop +} + +void +gr_error_handler::debug(const char *format, ...) +{ + va_list val; + va_start(val, format); + verror(ERR_DEBUG, format, val); + va_end(val); +} + +void +gr_error_handler::message(const char *format, ...) +{ + va_list val; + va_start(val, format); + verror(ERR_MESSAGE, format, val); + va_end(val); +} + +void +gr_error_handler::warning(const char *format, ...) +{ + va_list val; + va_start(val, format); + verror(ERR_WARNING, format, val); + va_end(val); +} + +void +gr_error_handler::error(const char *format, ...) +{ + va_list val; + va_start(val, format); + verror(ERR_ERROR, format, val); + va_end(val); +} + +void +gr_error_handler::fatal(const char *format, ...) +{ + va_list val; + va_start(val, format); + verror(ERR_FATAL, format, val); + va_end(val); +} + +void +gr_error_handler::verror(seriousness s, const char *format, va_list val) +{ + std::string text = make_text(s, format, val); + handle_text(s, text); + count_error(s); +} + +void +gr_error_handler::verror_text(seriousness s, const std::string &text) +{ + // text is already made + handle_text(s, text); + count_error(s); +} + +std::string +gr_error_handler::make_text(seriousness s, const char *format, va_list val) +{ + char text_buf[4096]; + vsnprintf(text_buf, sizeof(text_buf), format, val); + text_buf[sizeof(text_buf)-1] = 0; + return text_buf; +} + +// ---------------------------------------------------------------- + +void +gr_base_error_handler::count_error(seriousness s) +{ + if (s < ERR_WARNING) + /* do nothing */; + else if (s < ERR_ERROR) + d_nwarnings++; + else + d_nerrors++; +} + +// ---------------------------------------------------------------- + +gr_file_error_handler::gr_file_error_handler(FILE *file) + : d_file(file), d_fd(-1) +{ +} + +gr_file_error_handler::gr_file_error_handler(int file_descriptor) +{ + d_fd = dup(file_descriptor); // so we can fclose it + if (d_fd == -1){ + perror("gr_file_error_handler:dup"); + throw std::invalid_argument("gr_file_error_handler:dup"); + } + d_file = fdopen(d_fd, "w"); + if (d_file == 0){ + perror("gr_file_error_handler:fdopen"); + throw std::invalid_argument("gr_file_error_handler:fdopen"); + } +} + +gr_file_error_handler::~gr_file_error_handler() +{ + if (d_fd != -1){ + fclose(d_file); + } +} + +void +gr_file_error_handler::handle_text(seriousness s, const std::string &text) +{ + if (text.length() <= 0) + return; + + fwrite(text.data(), 1, text.length(), d_file); + if (text[text.length()-1] != '\n') + fwrite("\n", 1, 1, d_file); + + if (d_fd != -1) + fflush(d_file); // keep synced with any other users of fd +} + + +// ---------------------------------------------------------------- +// static error handlers +// + +class gr_silent_error_handler : public gr_base_error_handler +{ +public: + gr_silent_error_handler() {} + void handle_text(seriousness s, const std::string &str); +}; + +void +gr_silent_error_handler::handle_text(seriousness s, const std::string &str) +{ + // nop +} + +class force_init { +public: + force_init() + { + s_default_handler = new gr_file_error_handler(stdout); + s_silent_handler = new gr_silent_error_handler(); + } +}; + +static force_init kludge; diff --git a/gnuradio-core/src/lib/runtime/gr_error_handler.h b/gnuradio-core/src/lib/runtime/gr_error_handler.h new file mode 100644 index 000000000..569c01c45 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_error_handler.h @@ -0,0 +1,117 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +/* + * This code is based on error.hh from the "Click Modular Router". + * Original copyright follows: + */ +/* + * error.{cc,hh} -- flexible classes for error reporting + * Eddie Kohler + * + * Copyright (c) 1999-2000 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, subject to the conditions + * listed in the Click LICENSE file. These conditions include: you must + * preserve this copyright notice, and you cannot mention the copyright + * holders in advertising related to the Software without their permission. + * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This + * notice is a summary of the Click LICENSE file; the license in that file is + * legally binding. + */ + +#ifndef INCLUDED_GR_ERROR_HANDLER_H +#define INCLUDED_GR_ERROR_HANDLER_H + +#include <gr_core_api.h> +#include <stdarg.h> +#include <string> +#include <cstdio> // for FILE + +/*! + * \brief abstract error handler + * \ingroup base + */ +class GR_CORE_API gr_error_handler { +public: + enum seriousness { + ERR_DEBUG = 0x00000000, + ERR_MESSAGE = 0x00010000, + ERR_WARNING = 0x00020000, + ERR_ERROR = 0x00030000, + ERR_FATAL = 0x00040000 + }; + + gr_error_handler() {} + virtual ~gr_error_handler(); + + static gr_error_handler *default_handler(); + static gr_error_handler *silent_handler(); + + static bool has_default_handler(); + static void set_default_handler(gr_error_handler *errh); + + void debug(const char *format, ...); + void message(const char *format, ...); + void warning(const char *format, ...); + void error(const char *format, ...); + void fatal(const char *format, ...); + + virtual int nwarnings() const = 0; + virtual int nerrors() const = 0; + virtual void reset_counts() = 0; + + void verror(seriousness s, const char *format, va_list); + void verror_text(seriousness s, const std::string &text); + +protected: + virtual void count_error(seriousness s) = 0; + virtual void handle_text(seriousness s, const std::string &str) = 0; + std::string make_text(seriousness s, const char *format, va_list); +}; + + +class GR_CORE_API gr_base_error_handler : public gr_error_handler { + int d_nwarnings; + int d_nerrors; + +public: + gr_base_error_handler() : d_nwarnings(0), d_nerrors(0) {} + int nwarnings() const { return d_nwarnings; } + int nerrors() const { return d_nerrors; } + void reset_counts() { d_nwarnings = d_nerrors = 0; } + void count_error(seriousness s); +}; + +class GR_CORE_API gr_file_error_handler : public gr_base_error_handler { + FILE *d_file; + int d_fd; +public: + gr_file_error_handler(FILE *file); + gr_file_error_handler(int file_descriptor); + ~gr_file_error_handler(); + + void handle_text(seriousness s, const std::string &str); +}; + +#endif /* INCLUDED_GR_ERROR_HANDLER_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_error_handler.i b/gnuradio-core/src/lib/runtime/gr_error_handler.i new file mode 100644 index 000000000..072394a72 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_error_handler.i @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +%rename(error_handler) gr_error_handler; +%rename(file_error_handler) gr_file_error_handler; + +class gr_error_handler { +public: + enum seriousness { + ERR_DEBUG = 0x00000000, + ERR_MESSAGE = 0x00010000, + ERR_WARNING = 0x00020000, + ERR_ERROR = 0x00030000, + ERR_FATAL = 0x00040000 + }; + + gr_error_handler() {} + virtual ~gr_error_handler(); + + static gr_error_handler *default_handler(); + static gr_error_handler *silent_handler(); + + static bool has_default_handler(); + static void set_default_handler(gr_error_handler *errh); + + virtual int nwarnings() const = 0; + virtual int nerrors() const = 0; + virtual void reset_counts() = 0; + + void verror_text(seriousness s, const std::string &text); +}; + +%ignore gr_base_error_handler; +class gr_base_error_handler : public gr_error_handler { + int d_nwarnings; + int d_nerrors; + +public: + gr_base_error_handler() : d_nwarnings(0), d_nerrors(0) {} + int nwarnings() const { return d_nwarnings; } + int nerrors() const { return d_nerrors; } + void reset_counts() { d_nwarnings = d_nerrors = 0; } + void count_error(seriousness s); +}; + +class gr_file_error_handler : public gr_base_error_handler { +public: + gr_file_error_handler(int file_descriptor); + ~gr_file_error_handler(); +}; diff --git a/gnuradio-core/src/lib/runtime/gr_flat_flowgraph.cc b/gnuradio-core/src/lib/runtime/gr_flat_flowgraph.cc new file mode 100644 index 000000000..9294a5dca --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_flat_flowgraph.cc @@ -0,0 +1,403 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_flat_flowgraph.h> +#include <gr_block_detail.h> +#include <gr_io_signature.h> +#include <gr_buffer.h> +#include <volk/volk.h> +#include <iostream> +#include <map> +#include <boost/format.hpp> + +#define GR_FLAT_FLOWGRAPH_DEBUG 0 + +// 32Kbyte buffer size between blocks +#define GR_FIXED_BUFFER_SIZE (32*(1L<<10)) + +static const unsigned int s_fixed_buffer_size = GR_FIXED_BUFFER_SIZE; + +gr_flat_flowgraph_sptr +gr_make_flat_flowgraph() +{ + return gr_flat_flowgraph_sptr(new gr_flat_flowgraph()); +} + +gr_flat_flowgraph::gr_flat_flowgraph() +{ +} + +gr_flat_flowgraph::~gr_flat_flowgraph() +{ +} + +void +gr_flat_flowgraph::setup_connections() +{ + gr_basic_block_vector_t blocks = calc_used_blocks(); + + // Assign block details to blocks + for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) + cast_to_block_sptr(*p)->set_detail(allocate_block_detail(*p)); + + // Connect inputs to outputs for each block + for(gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) { + connect_block_inputs(*p); + + gr_block_sptr block = cast_to_block_sptr(*p); + block->set_unaligned(0); + block->set_is_unaligned(false); + } + + // Connect message ports connetions + for(gr_msg_edge_viter_t i = d_msg_edges.begin(); i != d_msg_edges.end(); i++){ + if(GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << boost::format("flat_fg connecting msg primitives: (%s, %s)->(%s, %s)\n") % + i->src().block() % i->src().port() % + i->dst().block() % i->dst().port(); + i->src().block()->message_port_sub( i->src().port(), pmt::pmt_cons(i->dst().block()->alias_pmt(), i->dst().port()) ); + } + +} + +gr_block_detail_sptr +gr_flat_flowgraph::allocate_block_detail(gr_basic_block_sptr block) +{ + int ninputs = calc_used_ports(block, true).size(); + int noutputs = calc_used_ports(block, false).size(); + gr_block_detail_sptr detail = gr_make_block_detail(ninputs, noutputs); + + gr_block_sptr grblock = cast_to_block_sptr(block); + if(!grblock) + throw std::runtime_error("allocate_block_detail found non-gr_block"); + + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "Creating block detail for " << block << std::endl; + + for (int i = 0; i < noutputs; i++) { + grblock->expand_minmax_buffer(i); + + gr_buffer_sptr buffer = allocate_buffer(block, i); + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "Allocated buffer for output " << block << ":" << i << std::endl; + detail->set_output(i, buffer); + + // Update the block's max_output_buffer based on what was actually allocated. + grblock->set_max_output_buffer(i, buffer->bufsize()); + } + + return detail; +} + +gr_buffer_sptr +gr_flat_flowgraph::allocate_buffer(gr_basic_block_sptr block, int port) +{ + gr_block_sptr grblock = cast_to_block_sptr(block); + if (!grblock) + throw std::runtime_error("allocate_buffer found non-gr_block"); + int item_size = block->output_signature()->sizeof_stream_item(port); + + // *2 because we're now only filling them 1/2 way in order to + // increase the available parallelism when using the TPB scheduler. + // (We're double buffering, where we used to single buffer) + int nitems = s_fixed_buffer_size * 2 / item_size; + + // Make sure there are at least twice the output_multiple no. of items + if (nitems < 2*grblock->output_multiple()) // Note: this means output_multiple() + nitems = 2*grblock->output_multiple(); // can't be changed by block dynamically + + // If any downstream blocks are decimators and/or have a large output_multiple, + // ensure we have a buffer at least twice their decimation factor*output_multiple + gr_basic_block_vector_t blocks = calc_downstream_blocks(block, port); + + // limit buffer size if indicated + if(grblock->max_output_buffer(port) > 0) { +// std::cout << "constraining output items to " << block->max_output_buffer(port) << "\n"; + nitems = std::min((long)nitems, (long)grblock->max_output_buffer(port)); + nitems -= nitems%grblock->output_multiple(); + if( nitems < 1 ) + throw std::runtime_error("problems allocating a buffer with the given max output buffer constraint!"); + } + else if(grblock->min_output_buffer(port) > 0) { + nitems = std::max((long)nitems, (long)grblock->min_output_buffer(port)); + nitems -= nitems%grblock->output_multiple(); + if( nitems < 1 ) + throw std::runtime_error("problems allocating a buffer with the given min output buffer constraint!"); + } + + for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) { + gr_block_sptr dgrblock = cast_to_block_sptr(*p); + if (!dgrblock) + throw std::runtime_error("allocate_buffer found non-gr_block"); + + double decimation = (1.0/dgrblock->relative_rate()); + int multiple = dgrblock->output_multiple(); + int history = dgrblock->history(); + nitems = std::max(nitems, static_cast<int>(2*(decimation*multiple+history))); + } + +// std::cout << "gr_make_buffer(" << nitems << ", " << item_size << ", " << grblock << "\n"; + return gr_make_buffer(nitems, item_size, grblock); +} + +void +gr_flat_flowgraph::connect_block_inputs(gr_basic_block_sptr block) +{ + gr_block_sptr grblock = cast_to_block_sptr(block); + if (!grblock) + throw std::runtime_error("connect_block_inputs found non-gr_block"); + + // Get its detail and edges that feed into it + gr_block_detail_sptr detail = grblock->detail(); + gr_edge_vector_t in_edges = calc_upstream_edges(block); + + // For each edge that feeds into it + for (gr_edge_viter_t e = in_edges.begin(); e != in_edges.end(); e++) { + // Set the buffer reader on the destination port to the output + // buffer on the source port + int dst_port = e->dst().port(); + int src_port = e->src().port(); + gr_basic_block_sptr src_block = e->src().block(); + gr_block_sptr src_grblock = cast_to_block_sptr(src_block); + if (!src_grblock) + throw std::runtime_error("connect_block_inputs found non-gr_block"); + gr_buffer_sptr src_buffer = src_grblock->detail()->output(src_port); + + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "Setting input " << dst_port << " from edge " << (*e) << std::endl; + + detail->set_input(dst_port, gr_buffer_add_reader(src_buffer, grblock->history()-1, grblock)); + } +} + +void +gr_flat_flowgraph::merge_connections(gr_flat_flowgraph_sptr old_ffg) +{ + // Allocate block details if needed. Only new blocks that aren't pruned out + // by flattening will need one; existing blocks still in the new flowgraph will + // already have one. + for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) { + gr_block_sptr block = cast_to_block_sptr(*p); + + if (!block->detail()) { + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "merge: allocating new detail for block " << (*p) << std::endl; + block->set_detail(allocate_block_detail(block)); + } + else + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "merge: reusing original detail for block " << (*p) << std::endl; + } + + // Calculate the old edges that will be going away, and clear the buffer readers + // on the RHS. + for (gr_edge_viter_t old_edge = old_ffg->d_edges.begin(); old_edge != old_ffg->d_edges.end(); old_edge++) { + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "merge: testing old edge " << (*old_edge) << "..."; + + gr_edge_viter_t new_edge; + for (new_edge = d_edges.begin(); new_edge != d_edges.end(); new_edge++) + if (new_edge->src() == old_edge->src() && + new_edge->dst() == old_edge->dst()) + break; + + if (new_edge == d_edges.end()) { // not found in new edge list + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "not in new edge list" << std::endl; + // zero the buffer reader on RHS of old edge + gr_block_sptr block(cast_to_block_sptr(old_edge->dst().block())); + int port = old_edge->dst().port(); + block->detail()->set_input(port, gr_buffer_reader_sptr()); + } + else { + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "found in new edge list" << std::endl; + } + } + + // Now connect inputs to outputs, reusing old buffer readers if they exist + for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) { + gr_block_sptr block = cast_to_block_sptr(*p); + + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "merge: merging " << (*p) << "..."; + + if (old_ffg->has_block_p(*p)) { + // Block exists in old flow graph + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "used in old flow graph" << std::endl; + gr_block_detail_sptr detail = block->detail(); + + // Iterate through the inputs and see what needs to be done + int ninputs = calc_used_ports(block, true).size(); // Might be different now + for (int i = 0; i < ninputs; i++) { + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "Checking input " << block << ":" << i << "..."; + gr_edge edge = calc_upstream_edge(*p, i); + + // Fish out old buffer reader and see if it matches correct buffer from edge list + gr_block_sptr src_block = cast_to_block_sptr(edge.src().block()); + gr_block_detail_sptr src_detail = src_block->detail(); + gr_buffer_sptr src_buffer = src_detail->output(edge.src().port()); + gr_buffer_reader_sptr old_reader; + if (i < detail->ninputs()) // Don't exceed what the original detail has + old_reader = detail->input(i); + + // If there's a match, use it + if (old_reader && (src_buffer == old_reader->buffer())) { + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "matched, reusing" << std::endl; + } + else { + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "needs a new reader" << std::endl; + + // Create new buffer reader and assign + detail->set_input(i, gr_buffer_add_reader(src_buffer, block->history()-1, block)); + } + } + } + else { + // Block is new, it just needs buffer readers at this point + if (GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << "new block" << std::endl; + connect_block_inputs(block); + + // Make sure all buffers are aligned + setup_buffer_alignment(block); + } + + // Now deal with the fact that the block details might have changed numbers of + // inputs and outputs vs. in the old flowgraph. + } +} + +void +gr_flat_flowgraph::setup_buffer_alignment(gr_block_sptr block) +{ + const int alignment = volk_get_alignment(); + for(int i = 0; i < block->detail()->ninputs(); i++) { + void *r = (void*)block->detail()->input(i)->read_pointer(); + unsigned long int ri = (unsigned long int)r % alignment; + //std::cerr << "reader: " << r << " alignment: " << ri << std::endl; + if(ri != 0) { + size_t itemsize = block->detail()->input(i)->get_sizeof_item(); + block->detail()->input(i)->update_read_pointer(alignment-ri/itemsize); + } + block->set_unaligned(0); + block->set_is_unaligned(false); + } + + for(int i = 0; i < block->detail()->noutputs(); i++) { + void *w = (void*)block->detail()->output(i)->write_pointer(); + unsigned long int wi = (unsigned long int)w % alignment; + //std::cerr << "writer: " << w << " alignment: " << wi << std::endl; + if(wi != 0) { + size_t itemsize = block->detail()->output(i)->get_sizeof_item(); + block->detail()->output(i)->update_write_pointer(alignment-wi/itemsize); + } + block->set_unaligned(0); + block->set_is_unaligned(false); + } +} + +void gr_flat_flowgraph::dump() +{ + for (gr_edge_viter_t e = d_edges.begin(); e != d_edges.end(); e++) + std::cout << " edge: " << (*e) << std::endl; + + for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) { + std::cout << " block: " << (*p) << std::endl; + gr_block_detail_sptr detail = cast_to_block_sptr(*p)->detail(); + std::cout << " detail @" << detail << ":" << std::endl; + + int ni = detail->ninputs(); + int no = detail->noutputs(); + for (int i = 0; i < no; i++) { + gr_buffer_sptr buffer = detail->output(i); + std::cout << " output " << i << ": " << buffer << std::endl; + } + + for (int i = 0; i < ni; i++) { + gr_buffer_reader_sptr reader = detail->input(i); + std::cout << " reader " << i << ": " << reader + << " reading from buffer=" << reader->buffer() << std::endl; + } + } + +} + +gr_block_vector_t +gr_flat_flowgraph::make_block_vector(gr_basic_block_vector_t &blocks) +{ + gr_block_vector_t result; + for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) { + result.push_back(cast_to_block_sptr(*p)); + } + + return result; +} + + +void gr_flat_flowgraph::clear_endpoint(const gr_msg_endpoint &e, bool is_src){ + for(size_t i=0; i<d_msg_edges.size(); i++){ + if(is_src){ + if(d_msg_edges[i].src() == e){ + d_msg_edges.erase(d_msg_edges.begin() + i); + i--; + } + } else { + if(d_msg_edges[i].dst() == e){ + d_msg_edges.erase(d_msg_edges.begin() + i); + i--; + } + } + } +} + +void gr_flat_flowgraph::replace_endpoint(const gr_msg_endpoint &e, const gr_msg_endpoint &r, bool is_src){ + size_t n_replr(0); + if(GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << boost::format("gr_flat_flowgraph::replace_endpoint( %s, %s, %d )\n") % e.block()% r.block()% is_src; + for(size_t i=0; i<d_msg_edges.size(); i++){ + if(is_src){ + if(d_msg_edges[i].src() == e){ + if(GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << boost::format("gr_flat_flowgraph::replace_endpoint() flattening to ( %s, %s )\n") % r.block()% d_msg_edges[i].dst().block(); + d_msg_edges.push_back( gr_msg_edge(r, d_msg_edges[i].dst() ) ); + n_replr++; + } + } else { + if(d_msg_edges[i].dst() == e){ + if(GR_FLAT_FLOWGRAPH_DEBUG) + std::cout << boost::format("gr_flat_flowgraph::replace_endpoint() flattening to ( %s, %s )\n") % r.block()% d_msg_edges[i].dst().block(); + d_msg_edges.push_back( gr_msg_edge(d_msg_edges[i].src(), r ) ); + n_replr++; + } + } + } +} + diff --git a/gnuradio-core/src/lib/runtime/gr_flat_flowgraph.h b/gnuradio-core/src/lib/runtime/gr_flat_flowgraph.h new file mode 100644 index 000000000..5c8268d7d --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_flat_flowgraph.h @@ -0,0 +1,80 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_FLAT_FLOWGRAPH_H +#define INCLUDED_GR_FLAT_FLOWGRAPH_H + +#include <gr_core_api.h> +#include <gr_flowgraph.h> +#include <gr_block.h> + +// Create a shared pointer to a heap allocated gr_flat_flowgraph +// (types defined in gr_runtime_types.h) +GR_CORE_API gr_flat_flowgraph_sptr gr_make_flat_flowgraph(); + +/*! + *\brief Class specializing gr_flat_flowgraph that has all nodes + * as gr_blocks, with no hierarchy + * \ingroup internal + */ +class GR_CORE_API gr_flat_flowgraph : public gr_flowgraph +{ +public: + friend GR_CORE_API gr_flat_flowgraph_sptr gr_make_flat_flowgraph(); + + // Destruct an arbitrary gr_flat_flowgraph + ~gr_flat_flowgraph(); + + // Wire gr_blocks together in new flat_flowgraph + void setup_connections(); + + // Merge applicable connections from existing flat flowgraph + void merge_connections(gr_flat_flowgraph_sptr sfg); + + void dump(); + + /*! + * Make a vector of gr_block from a vector of gr_basic_block + */ + static gr_block_vector_t make_block_vector(gr_basic_block_vector_t &blocks); + + void replace_endpoint(const gr_msg_endpoint &e, const gr_msg_endpoint &r, bool is_src); + void clear_endpoint(const gr_msg_endpoint &e, bool is_src); + +private: + gr_flat_flowgraph(); + + gr_block_detail_sptr allocate_block_detail(gr_basic_block_sptr block); + gr_buffer_sptr allocate_buffer(gr_basic_block_sptr block, int port); + void connect_block_inputs(gr_basic_block_sptr block); + + /* When reusing a flowgraph's blocks, this call makes sure all of the + * buffer's are aligned at the machine's alignment boundary and tells + * the blocks that they are aligned. + * + * Called from both setup_connections and merge_connections for + * start and restarts. + */ + void setup_buffer_alignment(gr_block_sptr block); +}; + +#endif /* INCLUDED_GR_FLAT_FLOWGRAPH_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_flowgraph.cc b/gnuradio-core/src/lib/runtime/gr_flowgraph.cc new file mode 100644 index 000000000..63a208480 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_flowgraph.cc @@ -0,0 +1,515 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_flowgraph.h> +#include <gr_io_signature.h> +#include <stdexcept> +#include <sstream> +#include <iterator> + +#define GR_FLOWGRAPH_DEBUG 0 + +gr_edge::~gr_edge() +{ +} + +gr_flowgraph_sptr gr_make_flowgraph() +{ + return gr_flowgraph_sptr(new gr_flowgraph()); +} + +gr_flowgraph::gr_flowgraph() +{ +} + +gr_flowgraph::~gr_flowgraph() +{ +} + +// FIXME: move to libgruel as a utility function +template<class T> +static +std::vector<T> +unique_vector(std::vector<T> v) +{ + std::vector<T> result; + std::insert_iterator<std::vector<T> > inserter(result, result.begin()); + + sort(v.begin(), v.end()); + unique_copy(v.begin(), v.end(), inserter); + return result; +} + +void +gr_flowgraph::connect(const gr_endpoint &src, const gr_endpoint &dst) +{ + check_valid_port(src.block()->output_signature(), src.port()); + check_valid_port(dst.block()->input_signature(), dst.port()); + check_dst_not_used(dst); + check_type_match(src, dst); + + // All ist klar, Herr Kommisar + d_edges.push_back(gr_edge(src,dst)); +} + +void +gr_flowgraph::disconnect(const gr_endpoint &src, const gr_endpoint &dst) +{ + for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) { + if (src == p->src() && dst == p->dst()) { + d_edges.erase(p); + return; + } + } + + std::stringstream msg; + msg << "cannot disconnect edge " << gr_edge(src, dst) << ", not found"; + throw std::invalid_argument(msg.str()); +} + +void +gr_flowgraph::validate() +{ + d_blocks = calc_used_blocks(); + + for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) { + std::vector<int> used_ports; + int ninputs, noutputs; + + if (GR_FLOWGRAPH_DEBUG) + std::cout << "Validating block: " << (*p) << std::endl; + + used_ports = calc_used_ports(*p, true); // inputs + ninputs = used_ports.size(); + check_contiguity(*p, used_ports, true); // inputs + + used_ports = calc_used_ports(*p, false); // outputs + noutputs = used_ports.size(); + check_contiguity(*p, used_ports, false); // outputs + + if (!((*p)->check_topology(ninputs, noutputs))) { + std::stringstream msg; + msg << "check topology failed on " << (*p) + << " using ninputs=" << ninputs + << ", noutputs=" << noutputs; + throw std::runtime_error(msg.str()); + } + } +} + +void +gr_flowgraph::clear() +{ + // Boost shared pointers will deallocate as needed + d_blocks.clear(); + d_edges.clear(); +} + +void +gr_flowgraph::check_valid_port(gr_io_signature_sptr sig, int port) +{ + std::stringstream msg; + + if (port < 0) { + msg << "negative port number " << port << " is invalid"; + throw std::invalid_argument(msg.str()); + } + + int max = sig->max_streams(); + if (max != gr_io_signature::IO_INFINITE && port >= max) { + msg << "port number " << port << " exceeds max of "; + if (max == 0) + msg << "(none)"; + else + msg << max-1; + throw std::invalid_argument(msg.str()); + } +} + +void +gr_flowgraph::check_valid_port(const gr_msg_endpoint &e) +{ + if (GR_FLOWGRAPH_DEBUG) + std::cout << "check_valid_port( " << e.block() << ", " << e.port() << ")\n"; + + if(!e.block()->has_msg_port(e.port())) + throw std::invalid_argument("invalid msg port in connect() or disconnect()"); +} + +void +gr_flowgraph::check_dst_not_used(const gr_endpoint &dst) +{ + // A destination is in use if it is already on the edge list + for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) + if (p->dst() == dst) { + std::stringstream msg; + msg << "destination already in use by edge " << (*p); + throw std::invalid_argument(msg.str()); + } +} + +void +gr_flowgraph::check_type_match(const gr_endpoint &src, const gr_endpoint &dst) +{ + int src_size = src.block()->output_signature()->sizeof_stream_item(src.port()); + int dst_size = dst.block()->input_signature()->sizeof_stream_item(dst.port()); + + if (src_size != dst_size) { + std::stringstream msg; + msg << "itemsize mismatch: " << src << " using " << src_size + << ", " << dst << " using " << dst_size; + throw std::invalid_argument(msg.str()); + } +} + +gr_basic_block_vector_t +gr_flowgraph::calc_used_blocks() +{ + gr_basic_block_vector_t tmp; + + // make sure free standing message blocks are included + for (gr_msg_edge_viter_t p = d_msg_edges.begin(); p != d_msg_edges.end(); p++) { +// for now only blocks receiving messages get a thread context - uncomment to allow senders to also obtain one +// tmp.push_back(p->src().block()); + tmp.push_back(p->dst().block()); + } + + // Collect all blocks in the edge list + for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) { + tmp.push_back(p->src().block()); + tmp.push_back(p->dst().block()); + } + + return unique_vector<gr_basic_block_sptr>(tmp); +} + +std::vector<int> +gr_flowgraph::calc_used_ports(gr_basic_block_sptr block, bool check_inputs) +{ + std::vector<int> tmp; + + // Collect all seen ports + gr_edge_vector_t edges = calc_connections(block, check_inputs); + for (gr_edge_viter_t p = edges.begin(); p != edges.end(); p++) { + if (check_inputs == true) + tmp.push_back(p->dst().port()); + else + tmp.push_back(p->src().port()); + } + + return unique_vector<int>(tmp); +} + +gr_edge_vector_t +gr_flowgraph::calc_connections(gr_basic_block_sptr block, bool check_inputs) +{ + gr_edge_vector_t result; + + for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) { + if (check_inputs) { + if (p->dst().block() == block) + result.push_back(*p); + } + else { + if (p->src().block() == block) + result.push_back(*p); + } + } + + return result; // assumes no duplicates +} + +void +gr_flowgraph::check_contiguity(gr_basic_block_sptr block, + const std::vector<int> &used_ports, + bool check_inputs) +{ + std::stringstream msg; + + gr_io_signature_sptr sig = + check_inputs ? block->input_signature() : block->output_signature(); + + int nports = used_ports.size(); + int min_ports = sig->min_streams(); + int max_ports = sig->max_streams(); + + if (nports == 0 && min_ports == 0) + return; + + if (nports < min_ports) { + msg << block << ": insufficient connected " + << (check_inputs ? "input ports " : "output ports ") + << "(" << min_ports << " needed, " << nports << " connected)"; + throw std::runtime_error(msg.str()); + } + + if (nports > max_ports && max_ports != gr_io_signature::IO_INFINITE) { + msg << block << ": too many connected " + << (check_inputs ? "input ports " : "output ports ") + << "(" << max_ports << " allowed, " << nports << " connected)"; + throw std::runtime_error(msg.str()); + } + + if (used_ports[nports-1]+1 != nports) { + for (int i = 0; i < nports; i++) { + if (used_ports[i] != i) { + msg << block << ": missing connection " + << (check_inputs ? "to input port " : "from output port ") + << i; + throw std::runtime_error(msg.str()); + } + } + } +} + +gr_basic_block_vector_t +gr_flowgraph::calc_downstream_blocks(gr_basic_block_sptr block, int port) +{ + gr_basic_block_vector_t tmp; + + for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) + if (p->src() == gr_endpoint(block, port)) + tmp.push_back(p->dst().block()); + + return unique_vector<gr_basic_block_sptr>(tmp); +} + +gr_basic_block_vector_t +gr_flowgraph::calc_downstream_blocks(gr_basic_block_sptr block) +{ + gr_basic_block_vector_t tmp; + + for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) + if (p->src().block() == block) + tmp.push_back(p->dst().block()); + + return unique_vector<gr_basic_block_sptr>(tmp); +} + +gr_edge_vector_t +gr_flowgraph::calc_upstream_edges(gr_basic_block_sptr block) +{ + gr_edge_vector_t result; + + for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) + if (p->dst().block() == block) + result.push_back(*p); + + return result; // Assume no duplicates +} + +bool +gr_flowgraph::has_block_p(gr_basic_block_sptr block) +{ + gr_basic_block_viter_t result; + result = std::find(d_blocks.begin(), d_blocks.end(), block); + return (result != d_blocks.end()); +} + +gr_edge +gr_flowgraph::calc_upstream_edge(gr_basic_block_sptr block, int port) +{ + gr_edge result; + + for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) { + if (p->dst() == gr_endpoint(block, port)) { + result = (*p); + break; + } + } + + return result; +} + +std::vector<gr_basic_block_vector_t> +gr_flowgraph::partition() +{ + std::vector<gr_basic_block_vector_t> result; + gr_basic_block_vector_t blocks = calc_used_blocks(); + gr_basic_block_vector_t graph; + + while (blocks.size() > 0) { + graph = calc_reachable_blocks(blocks[0], blocks); + assert(graph.size()); + result.push_back(topological_sort(graph)); + + for (gr_basic_block_viter_t p = graph.begin(); p != graph.end(); p++) + blocks.erase(find(blocks.begin(), blocks.end(), *p)); + } + + return result; +} + +gr_basic_block_vector_t +gr_flowgraph::calc_reachable_blocks(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks) +{ + gr_basic_block_vector_t result; + + // Mark all blocks as unvisited + for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) + (*p)->set_color(gr_basic_block::WHITE); + + // Recursively mark all reachable blocks + reachable_dfs_visit(block, blocks); + + // Collect all the blocks that have been visited + for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) + if ((*p)->color() == gr_basic_block::BLACK) + result.push_back(*p); + + return result; +} + +// Recursively mark all reachable blocks from given block and block list +void +gr_flowgraph::reachable_dfs_visit(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks) +{ + // Mark the current one as visited + block->set_color(gr_basic_block::BLACK); + + // Recurse into adjacent vertices + gr_basic_block_vector_t adjacent = calc_adjacent_blocks(block, blocks); + + for (gr_basic_block_viter_t p = adjacent.begin(); p != adjacent.end(); p++) + if ((*p)->color() == gr_basic_block::WHITE) + reachable_dfs_visit(*p, blocks); +} + +// Return a list of block adjacent to a given block along any edge +gr_basic_block_vector_t +gr_flowgraph::calc_adjacent_blocks(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks) +{ + gr_basic_block_vector_t tmp; + + // Find any blocks that are inputs or outputs + for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) { + if (p->src().block() == block) + tmp.push_back(p->dst().block()); + if (p->dst().block() == block) + tmp.push_back(p->src().block()); + } + + return unique_vector<gr_basic_block_sptr>(tmp); +} + +gr_basic_block_vector_t +gr_flowgraph::topological_sort(gr_basic_block_vector_t &blocks) +{ + gr_basic_block_vector_t tmp; + gr_basic_block_vector_t result; + tmp = sort_sources_first(blocks); + + // Start 'em all white + for (gr_basic_block_viter_t p = tmp.begin(); p != tmp.end(); p++) + (*p)->set_color(gr_basic_block::WHITE); + + for (gr_basic_block_viter_t p = tmp.begin(); p != tmp.end(); p++) { + if ((*p)->color() == gr_basic_block::WHITE) + topological_dfs_visit(*p, result); + } + + reverse(result.begin(), result.end()); + return result; +} + +gr_basic_block_vector_t +gr_flowgraph::sort_sources_first(gr_basic_block_vector_t &blocks) +{ + gr_basic_block_vector_t sources, nonsources, result; + + for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) { + if (source_p(*p)) + sources.push_back(*p); + else + nonsources.push_back(*p); + } + + for (gr_basic_block_viter_t p = sources.begin(); p != sources.end(); p++) + result.push_back(*p); + + for (gr_basic_block_viter_t p = nonsources.begin(); p != nonsources.end(); p++) + result.push_back(*p); + + return result; +} + +bool +gr_flowgraph::source_p(gr_basic_block_sptr block) +{ + return (calc_upstream_edges(block).size() == 0); +} + +void +gr_flowgraph::topological_dfs_visit(gr_basic_block_sptr block, gr_basic_block_vector_t &output) +{ + block->set_color(gr_basic_block::GREY); + gr_basic_block_vector_t blocks(calc_downstream_blocks(block)); + + for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) { + switch ((*p)->color()) { + case gr_basic_block::WHITE: + topological_dfs_visit(*p, output); + break; + + case gr_basic_block::GREY: + throw std::runtime_error("flow graph has loops!"); + + case gr_basic_block::BLACK: + continue; + + default: + throw std::runtime_error("invalid color on block!"); + } + } + + block->set_color(gr_basic_block::BLACK); + output.push_back(block); +} + +void gr_flowgraph::connect(const gr_msg_endpoint &src, const gr_msg_endpoint &dst){ + check_valid_port(src); + check_valid_port(dst); + for (gr_msg_edge_viter_t p = d_msg_edges.begin(); p != d_msg_edges.end(); p++) { + if(p->src() == src && p->dst() == dst){ + throw std::runtime_error("connect called on already connected edge!"); + } + } + d_msg_edges.push_back(gr_msg_edge(src,dst)); +} + +void gr_flowgraph::disconnect(const gr_msg_endpoint &src, const gr_msg_endpoint &dst){ + check_valid_port(src); + check_valid_port(dst); + for (gr_msg_edge_viter_t p = d_msg_edges.begin(); p != d_msg_edges.end(); p++) { + if(p->src() == src && p->dst() == dst){ + d_msg_edges.erase(p); + return; + } + } + throw std::runtime_error("disconnect called on non-connected edge!"); +} + + diff --git a/gnuradio-core/src/lib/runtime/gr_flowgraph.h b/gnuradio-core/src/lib/runtime/gr_flowgraph.h new file mode 100644 index 000000000..bef70f626 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_flowgraph.h @@ -0,0 +1,251 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_FLOWGRAPH_H +#define INCLUDED_GR_FLOWGRAPH_H + +#include <gr_core_api.h> +#include <gr_basic_block.h> +#include <iostream> + +/*! + * \brief Class representing a specific input or output graph endpoint + * \ingroup internal + */ +class GR_CORE_API gr_endpoint +{ +private: + gr_basic_block_sptr d_basic_block; + int d_port; + +public: + gr_endpoint() : d_basic_block(), d_port(0) { } + gr_endpoint(gr_basic_block_sptr block, int port) { d_basic_block = block; d_port = port; } + gr_basic_block_sptr block() const { return d_basic_block; } + int port() const { return d_port; } + + bool operator==(const gr_endpoint &other) const; +}; + +inline bool gr_endpoint::operator==(const gr_endpoint &other) const +{ + return (d_basic_block == other.d_basic_block && + d_port == other.d_port); +} + +class GR_CORE_API gr_msg_endpoint +{ +private: + gr_basic_block_sptr d_basic_block; + pmt::pmt_t d_port; + bool d_is_hier; +public: + gr_msg_endpoint() : d_basic_block(), d_port(pmt::PMT_NIL) { } + gr_msg_endpoint(gr_basic_block_sptr block, pmt::pmt_t port, bool is_hier=false){ d_basic_block = block; d_port = port; d_is_hier = is_hier;} + gr_basic_block_sptr block() const { return d_basic_block; } + pmt::pmt_t port() const { return d_port; } + bool is_hier() const { return d_is_hier; } + void set_hier(bool h) { d_is_hier = h; } + + bool operator==(const gr_msg_endpoint &other) const; + +}; + +inline bool gr_msg_endpoint::operator==(const gr_msg_endpoint &other) const +{ + return (d_basic_block == other.d_basic_block && + pmt::pmt_equal(d_port, other.d_port)); +} + + +// Hold vectors of gr_endpoint objects +typedef std::vector<gr_endpoint> gr_endpoint_vector_t; +typedef std::vector<gr_endpoint>::iterator gr_endpoint_viter_t; + +/*! + *\brief Class representing a connection between to graph endpoints + * + */ +class GR_CORE_API gr_edge +{ +public: + gr_edge() : d_src(), d_dst() { }; + gr_edge(const gr_endpoint &src, const gr_endpoint &dst) : d_src(src), d_dst(dst) { } + ~gr_edge(); + + const gr_endpoint &src() const { return d_src; } + const gr_endpoint &dst() const { return d_dst; } + +private: + gr_endpoint d_src; + gr_endpoint d_dst; +}; + + +// Hold vectors of gr_edge objects +typedef std::vector<gr_edge> gr_edge_vector_t; +typedef std::vector<gr_edge>::iterator gr_edge_viter_t; + + +/*! + *\brief Class representing a msg connection between to graph msg endpoints + * + */ +class GR_CORE_API gr_msg_edge +{ +public: + gr_msg_edge() : d_src(), d_dst() { }; + gr_msg_edge(const gr_msg_endpoint &src, const gr_msg_endpoint &dst) : d_src(src), d_dst(dst) { } + ~gr_msg_edge() {} + + const gr_msg_endpoint &src() const { return d_src; } + const gr_msg_endpoint &dst() const { return d_dst; } + +private: + gr_msg_endpoint d_src; + gr_msg_endpoint d_dst; +}; + +// Hold vectors of gr_edge objects +typedef std::vector<gr_msg_edge> gr_msg_edge_vector_t; +typedef std::vector<gr_msg_edge>::iterator gr_msg_edge_viter_t; + +// Create a shared pointer to a heap allocated flowgraph +// (types defined in gr_runtime_types.h) +GR_CORE_API gr_flowgraph_sptr gr_make_flowgraph(); + +/*! + * \brief Class representing a directed, acyclic graph of basic blocks + * \ingroup internal + */ +class GR_CORE_API gr_flowgraph +{ +public: + friend GR_CORE_API gr_flowgraph_sptr gr_make_flowgraph(); + + // Destruct an arbitrary flowgraph + ~gr_flowgraph(); + + // Connect two endpoints + void connect(const gr_endpoint &src, const gr_endpoint &dst); + + // Disconnect two endpoints + void disconnect(const gr_endpoint &src, const gr_endpoint &dst); + + // Connect an output port to an input port (convenience) + void connect(gr_basic_block_sptr src_block, int src_port, + gr_basic_block_sptr dst_block, int dst_port); + + // Disconnect an input port from an output port (convenience) + void disconnect(gr_basic_block_sptr src_block, int src_port, + gr_basic_block_sptr dst_block, int dst_port); + + // Connect two msg endpoints + void connect(const gr_msg_endpoint &src, const gr_msg_endpoint &dst); + + // Disconnect two msg endpoints + void disconnect(const gr_msg_endpoint &src, const gr_msg_endpoint &dst); + + // Validate connectivity, raise exception if invalid + void validate(); + + // Clear existing flowgraph + void clear(); + + // Return vector of edges + const gr_edge_vector_t &edges() const { return d_edges; } + + // Return vector of msg edges + const gr_msg_edge_vector_t &msg_edges() const { return d_msg_edges; } + + // Return vector of connected blocks + gr_basic_block_vector_t calc_used_blocks(); + + // Return toplogically sorted vector of blocks. All the sources come first. + gr_basic_block_vector_t topological_sort(gr_basic_block_vector_t &blocks); + + // Return vector of vectors of disjointly connected blocks, topologically + // sorted. + std::vector<gr_basic_block_vector_t> partition(); + +protected: + gr_basic_block_vector_t d_blocks; + gr_edge_vector_t d_edges; + gr_msg_edge_vector_t d_msg_edges; + + gr_flowgraph(); + std::vector<int> calc_used_ports(gr_basic_block_sptr block, bool check_inputs); + gr_basic_block_vector_t calc_downstream_blocks(gr_basic_block_sptr block, int port); + gr_edge_vector_t calc_upstream_edges(gr_basic_block_sptr block); + bool has_block_p(gr_basic_block_sptr block); + gr_edge calc_upstream_edge(gr_basic_block_sptr block, int port); + +private: + + void check_valid_port(gr_io_signature_sptr sig, int port); + void check_valid_port(const gr_msg_endpoint &e); + void check_dst_not_used(const gr_endpoint &dst); + void check_type_match(const gr_endpoint &src, const gr_endpoint &dst); + gr_edge_vector_t calc_connections(gr_basic_block_sptr block, bool check_inputs); // false=use outputs + void check_contiguity(gr_basic_block_sptr block, const std::vector<int> &used_ports, bool check_inputs); + + gr_basic_block_vector_t calc_downstream_blocks(gr_basic_block_sptr block); + gr_basic_block_vector_t calc_reachable_blocks(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks); + void reachable_dfs_visit(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks); + gr_basic_block_vector_t calc_adjacent_blocks(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks); + gr_basic_block_vector_t sort_sources_first(gr_basic_block_vector_t &blocks); + bool source_p(gr_basic_block_sptr block); + void topological_dfs_visit(gr_basic_block_sptr block, gr_basic_block_vector_t &output); +}; + +// Convenience functions +inline +void gr_flowgraph::connect(gr_basic_block_sptr src_block, int src_port, + gr_basic_block_sptr dst_block, int dst_port) +{ + connect(gr_endpoint(src_block, src_port), + gr_endpoint(dst_block, dst_port)); +} + +inline +void gr_flowgraph::disconnect(gr_basic_block_sptr src_block, int src_port, + gr_basic_block_sptr dst_block, int dst_port) +{ + disconnect(gr_endpoint(src_block, src_port), + gr_endpoint(dst_block, dst_port)); +} + +inline std::ostream& +operator <<(std::ostream &os, const gr_endpoint endp) +{ + os << endp.block() << ":" << endp.port(); + return os; +} + +inline std::ostream& +operator <<(std::ostream &os, const gr_edge edge) +{ + os << edge.src() << "->" << edge.dst(); + return os; +} + +#endif /* INCLUDED_GR_FLOWGRAPH_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_hier_block2.cc b/gnuradio-core/src/lib/runtime/gr_hier_block2.cc new file mode 100644 index 000000000..04eb60fc6 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_hier_block2.cc @@ -0,0 +1,77 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2007,2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_hier_block2.h> +#include <boost/detail/atomic_count.hpp> + +static boost::detail::atomic_count unique_id_pool(0); + + +gr_hier_block2::gr_hier_block2(void) +{ + //NOP +} + +gr_hier_block2::gr_hier_block2( + const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature +): + gras::HierBlock(name), + _unique_id(++unique_id_pool), + _name(name) +{ + this->set_input_signature(input_signature); + this->set_output_signature(output_signature); +} + +gr_hier_block2_sptr gr_make_hier_block2( + const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature +){ + return gr_hier_block2_sptr(new gr_hier_block2(name, input_signature, output_signature)); +} + +gr_io_signature_sptr gr_hier_block2::input_signature(void) const +{ + return _in_sig; +} + +gr_io_signature_sptr gr_hier_block2::output_signature(void) const +{ + return _out_sig; +} + +void gr_hier_block2::set_input_signature(gr_io_signature_sptr sig) +{ + _in_sig = sig; +} + +void gr_hier_block2::set_output_signature(gr_io_signature_sptr sig) +{ + _out_sig = sig; +} diff --git a/gnuradio-core/src/lib/runtime/gr_hier_block2.h b/gnuradio-core/src/lib/runtime/gr_hier_block2.h new file mode 100644 index 000000000..d393c42cb --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_hier_block2.h @@ -0,0 +1,71 @@ +/* + * Copyright 2012-2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GNURADIO_GR_HIER_BLOCK2_H +#define INCLUDED_GNURADIO_GR_HIER_BLOCK2_H + +#include <gr_core_api.h> +#include <gras/hier_block.hpp> +#include <gr_io_signature.h> + +struct GR_CORE_API gr_hier_block2 : gras::HierBlock +{ + + gr_hier_block2(void); + + gr_hier_block2( + const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature + ); + + long unique_id(void) const{return _unique_id;} + std::string name(void) const{return _name;} + long _unique_id; + std::string _name; + + const gr_hier_block2 &self(void) const + { + return *this; + } + + gr_io_signature_sptr input_signature(void) const; + gr_io_signature_sptr output_signature(void) const; + + void set_input_signature(gr_io_signature_sptr sig); + void set_output_signature(gr_io_signature_sptr sig); + + inline void lock(void){} + + inline void unlock(void){this->commit();} + + gr_io_signature_sptr _in_sig, _out_sig; +}; + +typedef boost::shared_ptr<gr_hier_block2> gr_hier_block2_sptr; + +GR_CORE_API gr_hier_block2_sptr gr_make_hier_block2( + const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature +); + +#endif /*INCLUDED_GNURADIO_GR_HIER_BLOCK2_H*/ diff --git a/gnuradio-core/src/lib/runtime/gr_hier_block2.i b/gnuradio-core/src/lib/runtime/gr_hier_block2.i new file mode 100644 index 000000000..a857394ca --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_hier_block2.i @@ -0,0 +1,88 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2006,2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +%include <gr_basic_block.i> + +class gr_hier_block2; +typedef boost::shared_ptr<gr_hier_block2> gr_hier_block2_sptr; +%template(gr_hier_block2_sptr) boost::shared_ptr<gr_hier_block2>; + +// Hack to have a Python shim implementation of gr.hier_block2 +// that instantiates one of these and passes through calls +%rename(hier_block2_swig) gr_make_hier_block2; +gr_hier_block2_sptr gr_make_hier_block2(const std::string name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature) + throw (std::runtime_error); + +// Rename connect and disconnect so that we can more easily build a +// better interface in scripting land. +%rename(primitive_connect) gr_hier_block2::connect; +%rename(primitive_disconnect) gr_hier_block2::disconnect; +%rename(primitive_msg_connect) gr_hier_block2::msg_connect; +%rename(primitive_msg_disconnect) gr_hier_block2::msg_disconnect; +%rename(primitive_message_port_register_hier_in) gr_hier_block2::message_port_register_hier_in; +%rename(primitive_message_port_register_hier_out) gr_hier_block2::message_port_register_hier_out; + +class gr_hier_block2 : public gr_basic_block +{ +private: + gr_hier_block2(const std::string name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature); + +public: + ~gr_hier_block2 (); + + void connect(gr_basic_block_sptr block) + throw (std::invalid_argument); + void connect(gr_basic_block_sptr src, int src_port, + gr_basic_block_sptr dst, int dst_port) + throw (std::invalid_argument); + void msg_connect(gr_basic_block_sptr src, pmt::pmt_t srcport, + gr_basic_block_sptr dst, pmt::pmt_t dstport) + throw (std::runtime_error); + void msg_connect(gr_basic_block_sptr src, std::string srcport, + gr_basic_block_sptr dst, std::string dstport) + throw (std::runtime_error); + void msg_disconnect(gr_basic_block_sptr src, pmt::pmt_t srcport, + gr_basic_block_sptr dst, pmt::pmt_t dstport) + throw (std::runtime_error); + void msg_disconnect(gr_basic_block_sptr src, std::string srcport, + gr_basic_block_sptr dst, std::string dstport) + throw (std::runtime_error); + + void disconnect(gr_basic_block_sptr block) + throw (std::invalid_argument); + void disconnect(gr_basic_block_sptr src, int src_port, + gr_basic_block_sptr dst, int dst_port) + throw (std::invalid_argument); + void disconnect_all(); + void lock(); + void unlock(); + + void message_port_register_hier_in(pmt::pmt_t port_id); + void message_port_register_hier_out(pmt::pmt_t port_id); + + + gr_hier_block2_sptr to_hier_block2(); // Needed for Python type coercion +}; diff --git a/gnuradio-core/src/lib/runtime/gr_hier_block2_detail.cc b/gnuradio-core/src/lib/runtime/gr_hier_block2_detail.cc new file mode 100644 index 000000000..add6da024 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_hier_block2_detail.cc @@ -0,0 +1,615 @@ +/* + * Copyright 2006,2007,2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_hier_block2_detail.h> +#include <gr_io_signature.h> +#include <stdexcept> +#include <sstream> +#include <boost/format.hpp> + +#define GR_HIER_BLOCK2_DETAIL_DEBUG 0 + +gr_hier_block2_detail::gr_hier_block2_detail(gr_hier_block2 *owner) : + d_owner(owner), + d_parent_detail(0), + d_fg(gr_make_flowgraph()) +{ + int min_inputs = owner->input_signature()->min_streams(); + int max_inputs = owner->input_signature()->max_streams(); + int min_outputs = owner->output_signature()->min_streams(); + int max_outputs = owner->output_signature()->max_streams(); + + if (max_inputs == gr_io_signature::IO_INFINITE || + max_outputs == gr_io_signature::IO_INFINITE || + (min_inputs != max_inputs) ||(min_outputs != max_outputs) ) { + std::stringstream msg; + msg << "Hierarchical blocks do not yet support arbitrary or" + << " variable numbers of inputs or outputs (" << d_owner->name() << ")"; + throw std::runtime_error(msg.str()); + } + + d_inputs = std::vector<gr_endpoint_vector_t>(max_inputs); + d_outputs = gr_endpoint_vector_t(max_outputs); +} + + +gr_hier_block2_detail::~gr_hier_block2_detail() +{ + d_owner = 0; // Don't use delete, we didn't allocate +} + +void +gr_hier_block2_detail::connect(gr_basic_block_sptr block) +{ + std::stringstream msg; + + // Check if duplicate + if (std::find(d_blocks.begin(), d_blocks.end(), block) != d_blocks.end()) { + msg << "Block " << block << " already connected."; + throw std::invalid_argument(msg.str()); + } + + // Check if has inputs or outputs + if (block->input_signature()->max_streams() != 0 || + block->output_signature()->max_streams() != 0) { + msg << "Block " << block << " must not have any input or output ports"; + throw std::invalid_argument(msg.str()); + } + + gr_hier_block2_sptr hblock(cast_to_hier_block2_sptr(block)); + + if (hblock && hblock.get() != d_owner) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "connect: block is hierarchical, setting parent to " << this << std::endl; + hblock->d_detail->d_parent_detail = this; + } + + d_blocks.push_back(block); +} + +void +gr_hier_block2_detail::connect(gr_basic_block_sptr src, int src_port, + gr_basic_block_sptr dst, int dst_port) +{ + std::stringstream msg; + + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "connecting: " << gr_endpoint(src, src_port) + << " -> " << gr_endpoint(dst, dst_port) << std::endl; + + if (src.get() == dst.get()) + throw std::invalid_argument("connect: src and destination blocks cannot be the same"); + + gr_hier_block2_sptr src_block(cast_to_hier_block2_sptr(src)); + gr_hier_block2_sptr dst_block(cast_to_hier_block2_sptr(dst)); + + if (src_block && src.get() != d_owner) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "connect: src is hierarchical, setting parent to " << this << std::endl; + src_block->d_detail->d_parent_detail = this; + } + + if (dst_block && dst.get() != d_owner) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "connect: dst is hierarchical, setting parent to " << this << std::endl; + dst_block->d_detail->d_parent_detail = this; + } + + // Connections to block inputs or outputs + int max_port; + if (src.get() == d_owner) { + max_port = src->input_signature()->max_streams(); + if ((max_port != -1 && (src_port >= max_port)) || src_port < 0) { + msg << "source port " << src_port << " out of range for " << src; + throw std::invalid_argument(msg.str()); + } + + return connect_input(src_port, dst_port, dst); + } + + if (dst.get() == d_owner) { + max_port = dst->output_signature()->max_streams(); + if ((max_port != -1 && (dst_port >= max_port)) || dst_port < 0) { + msg << "destination port " << dst_port << " out of range for " << dst; + throw std::invalid_argument(msg.str()); + } + + return connect_output(dst_port, src_port, src); + } + + // Internal connections + d_fg->connect(src, src_port, dst, dst_port); + + // TODO: connects to NC +} + +void +gr_hier_block2_detail::msg_connect(gr_basic_block_sptr src, pmt::pmt_t srcport, + gr_basic_block_sptr dst, pmt::pmt_t dstport) +{ + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "connecting message port..." << std::endl; + + // register the subscription +// this is done later... +// src->message_port_sub(srcport, pmt::pmt_cons(dst->alias_pmt(), dstport)); + + // add block uniquely to list to internal blocks + if (std::find(d_blocks.begin(), d_blocks.end(), dst) == d_blocks.end()){ + d_blocks.push_back(src); + d_blocks.push_back(dst); + } + + bool hier_out = (d_owner == src.get()) && src->message_port_is_hier_out(srcport);; + bool hier_in = (d_owner == dst.get()) && dst->message_port_is_hier_in(dstport); + + gr_hier_block2_sptr src_block(cast_to_hier_block2_sptr(src)); + gr_hier_block2_sptr dst_block(cast_to_hier_block2_sptr(dst)); + + if (src_block && src.get() != d_owner) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "connect: src is hierarchical, setting parent to " << this << std::endl; + src_block->d_detail->d_parent_detail = this; + } + + if (dst_block && dst.get() != d_owner) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "connect: dst is hierarchical, setting parent to " << this << std::endl; + dst_block->d_detail->d_parent_detail = this; + } + + // add edge for this message connection + if(GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << boost::format("connect( (%s, %s, %d), (%s, %s, %d) )\n") % + src % srcport % hier_out % + dst % dstport % hier_in; + d_fg->connect( gr_msg_endpoint(src, srcport, hier_out), gr_msg_endpoint(dst, dstport, hier_in)); +} + +void +gr_hier_block2_detail::msg_disconnect(gr_basic_block_sptr src, pmt::pmt_t srcport, + gr_basic_block_sptr dst, pmt::pmt_t dstport) +{ + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "disconnecting message port..." << std::endl; + + // unregister the subscription - if already subscribed + src->message_port_unsub(srcport, pmt::pmt_cons(dst->alias_pmt(), dstport)); + + // remove edge for this message connection + bool hier_out = (d_owner == src.get()) && src->message_port_is_hier_out(srcport);; + bool hier_in = (d_owner == dst.get()) && dst->message_port_is_hier_in(dstport); + d_fg->disconnect( gr_msg_endpoint(src, srcport, hier_out), gr_msg_endpoint(dst, dstport, hier_in)); +} + +void +gr_hier_block2_detail::disconnect(gr_basic_block_sptr block) +{ + // Check on singleton list + for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) { + if (*p == block) { + d_blocks.erase(p); + + gr_hier_block2_sptr hblock(cast_to_hier_block2_sptr(block)); + if (block && block.get() != d_owner) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "disconnect: block is hierarchical, clearing parent" << std::endl; + hblock->d_detail->d_parent_detail = 0; + } + + return; + } + } + + // Otherwise find all edges containing block + gr_edge_vector_t edges, tmp = d_fg->edges(); + gr_edge_vector_t::iterator p; + for (p = tmp.begin(); p != tmp.end(); p++) { + if ((*p).src().block() == block || (*p).dst().block() == block) { + edges.push_back(*p); + + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "disconnect: block found in edge " << (*p) << std::endl; + } + } + + if (edges.size() == 0) { + std::stringstream msg; + msg << "cannot disconnect block " << block << ", not found"; + throw std::invalid_argument(msg.str()); + } + + for (p = edges.begin(); p != edges.end(); p++) { + disconnect((*p).src().block(), (*p).src().port(), + (*p).dst().block(), (*p).dst().port()); + } +} + +void +gr_hier_block2_detail::disconnect(gr_basic_block_sptr src, int src_port, + gr_basic_block_sptr dst, int dst_port) +{ + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "disconnecting: " << gr_endpoint(src, src_port) + << " -> " << gr_endpoint(dst, dst_port) << std::endl; + + if (src.get() == dst.get()) + throw std::invalid_argument("disconnect: source and destination blocks cannot be the same"); + + gr_hier_block2_sptr src_block(cast_to_hier_block2_sptr(src)); + gr_hier_block2_sptr dst_block(cast_to_hier_block2_sptr(dst)); + + if (src_block && src.get() != d_owner) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "disconnect: src is hierarchical, clearing parent" << std::endl; + src_block->d_detail->d_parent_detail = 0; + } + + if (dst_block && dst.get() != d_owner) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "disconnect: dst is hierarchical, clearing parent" << std::endl; + dst_block->d_detail->d_parent_detail = 0; + } + + if (src.get() == d_owner) + return disconnect_input(src_port, dst_port, dst); + + if (dst.get() == d_owner) + return disconnect_output(dst_port, src_port, src); + + // Internal connections + d_fg->disconnect(src, src_port, dst, dst_port); +} + +void +gr_hier_block2_detail::connect_input(int my_port, int port, gr_basic_block_sptr block) +{ + std::stringstream msg; + + if (my_port < 0 || my_port >= (signed)d_inputs.size()) { + msg << "input port " << my_port << " out of range for " << block; + throw std::invalid_argument(msg.str()); + } + + gr_endpoint_vector_t &endps = d_inputs[my_port]; + gr_endpoint endp(block, port); + + gr_endpoint_viter_t p = std::find(endps.begin(), endps.end(), endp); + if (p != endps.end()) { + msg << "external input port " << my_port << " already wired to " << endp; + throw std::invalid_argument(msg.str()); + } + + endps.push_back(endp); +} + +void +gr_hier_block2_detail::connect_output(int my_port, int port, gr_basic_block_sptr block) +{ + std::stringstream msg; + + if (my_port < 0 || my_port >= (signed)d_outputs.size()) { + msg << "output port " << my_port << " out of range for " << block; + throw std::invalid_argument(msg.str()); + } + + if (d_outputs[my_port].block()) { + msg << "external output port " << my_port << " already connected from " + << d_outputs[my_port]; + throw std::invalid_argument(msg.str()); + } + + d_outputs[my_port] = gr_endpoint(block, port); +} + +void +gr_hier_block2_detail::disconnect_input(int my_port, int port, gr_basic_block_sptr block) +{ + std::stringstream msg; + + if (my_port < 0 || my_port >= (signed)d_inputs.size()) { + msg << "input port number " << my_port << " out of range for " << block; + throw std::invalid_argument(msg.str()); + } + + gr_endpoint_vector_t &endps = d_inputs[my_port]; + gr_endpoint endp(block, port); + + gr_endpoint_viter_t p = std::find(endps.begin(), endps.end(), endp); + if (p == endps.end()) { + msg << "external input port " << my_port << " not connected to " << endp; + throw std::invalid_argument(msg.str()); + } + + endps.erase(p); +} + +void +gr_hier_block2_detail::disconnect_output(int my_port, int port, gr_basic_block_sptr block) +{ + std::stringstream msg; + + if (my_port < 0 || my_port >= (signed)d_outputs.size()) { + msg << "output port number " << my_port << " out of range for " << block; + throw std::invalid_argument(msg.str()); + } + + if (d_outputs[my_port].block() != block) { + msg << "block " << block << " not assigned to output " + << my_port << ", can't disconnect"; + throw std::invalid_argument(msg.str()); + } + + d_outputs[my_port] = gr_endpoint(); +} + +gr_endpoint_vector_t +gr_hier_block2_detail::resolve_port(int port, bool is_input) +{ + std::stringstream msg; + + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "Resolving port " << port << " as an " + << (is_input ? "input" : "output") + << " of " << d_owner->name() << std::endl; + + gr_endpoint_vector_t result; + + if (is_input) { + if (port < 0 || port >= (signed)d_inputs.size()) { + msg << "resolve_port: hierarchical block '" << d_owner->name() + << "': input " << port << " is out of range"; + throw std::runtime_error(msg.str()); + } + + if (d_inputs[port].empty()) { + msg << "resolve_port: hierarchical block '" << d_owner->name() + << "': input " << port << " is not connected internally"; + throw std::runtime_error(msg.str()); + } + + gr_endpoint_vector_t &endps = d_inputs[port]; + gr_endpoint_viter_t p; + for (p = endps.begin(); p != endps.end(); p++) { + gr_endpoint_vector_t tmp = resolve_endpoint(*p, true); + std::copy(tmp.begin(), tmp.end(), back_inserter(result)); + } + } + else { + if (port < 0 || port >= (signed)d_outputs.size()) { + msg << "resolve_port: hierarchical block '" << d_owner->name() + << "': output " << port << " is out of range"; + throw std::runtime_error(msg.str()); + } + + if (d_outputs[port] == gr_endpoint()) { + msg << "resolve_port: hierarchical block '" << d_owner->name() + << "': output " << port << " is not connected internally"; + throw std::runtime_error(msg.str()); + } + + result = resolve_endpoint(d_outputs[port], false); + } + + if (result.empty()) { + msg << "resolve_port: hierarchical block '" << d_owner->name() + << "': unable to resolve " + << (is_input ? "input port " : "output port ") + << port; + throw std::runtime_error(msg.str()); + } + + return result; +} + +void +gr_hier_block2_detail::disconnect_all() +{ + d_fg->clear(); + d_blocks.clear(); + d_inputs.clear(); + d_outputs.clear(); +} + +gr_endpoint_vector_t +gr_hier_block2_detail::resolve_endpoint(const gr_endpoint &endp, bool is_input) const +{ + std::stringstream msg; + gr_endpoint_vector_t result; + + // Check if endpoint is a leaf node + if (cast_to_block_sptr(endp.block())) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "Block " << endp.block() << " is a leaf node, returning." << std::endl; + result.push_back(endp); + return result; + } + + // Check if endpoint is a hierarchical block + gr_hier_block2_sptr hier_block2(cast_to_hier_block2_sptr(endp.block())); + if (hier_block2) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "Resolving endpoint " << endp << " as an " + << (is_input ? "input" : "output") + << ", recursing" << std::endl; + return hier_block2->d_detail->resolve_port(endp.port(), is_input); + } + + msg << "unable to resolve" << (is_input ? " input " : " output ") + << "endpoint " << endp; + throw std::runtime_error(msg.str()); +} + +void +gr_hier_block2_detail::flatten_aux(gr_flat_flowgraph_sptr sfg) const +{ + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << " ** Flattening " << d_owner->name() << std::endl; + + // Add my edges to the flow graph, resolving references to actual endpoints + gr_edge_vector_t edges = d_fg->edges(); + gr_msg_edge_vector_t msg_edges = d_fg->msg_edges(); + gr_edge_viter_t p; + gr_msg_edge_viter_t q,u; + + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "Flattening stream connections: " << std::endl; + + for (p = edges.begin(); p != edges.end(); p++) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "Flattening edge " << (*p) << std::endl; + + gr_endpoint_vector_t src_endps = resolve_endpoint(p->src(), false); + gr_endpoint_vector_t dst_endps = resolve_endpoint(p->dst(), true); + + gr_endpoint_viter_t s, d; + for (s = src_endps.begin(); s != src_endps.end(); s++) { + for (d = dst_endps.begin(); d != dst_endps.end(); d++) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << (*s) << "->" << (*d) << std::endl; + sfg->connect(*s, *d); + } + } + } + + // loop through flattening hierarchical connections + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "Flattening msg connections: " << std::endl; + + + std::vector<std::pair<gr_msg_endpoint, bool> > resolved_endpoints; + for(q = msg_edges.begin(); q != msg_edges.end(); q++) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << boost::format(" flattening edge ( %s, %s, %d) -> ( %s, %s, %d)\n") % q->src().block() % q->src().port() % q->src().is_hier() % q->dst().block() % q->dst().port() % q->dst().is_hier(); + + bool normal_connection = true; + + // resolve existing connections to hier ports + if(q->dst().is_hier()){ + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << boost::format(" resolve hier output (%s, %s)") % q->dst().block() % q->dst().port() << std::endl; + sfg->replace_endpoint( q->dst(), q->src(), true ); + resolved_endpoints.push_back(std::pair<gr_msg_endpoint, bool>(q->dst(),true)); + normal_connection = false; + } + + if(q->src().is_hier()){ + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << boost::format(" resolve hier input (%s, %s)") % q->src().block() % q->src().port() << std::endl; + sfg->replace_endpoint( q->src(), q->dst(), false ); + resolved_endpoints.push_back(std::pair<gr_msg_endpoint, bool>(q->src(),false)); + normal_connection = false; + } + + // propogate non hier connections through + if(normal_connection){ + sfg->connect( q->src(), q->dst() ); + } + } + for(std::vector<std::pair<gr_msg_endpoint, bool> >::iterator it = resolved_endpoints.begin(); it != resolved_endpoints.end(); it++){ + sfg->clear_endpoint( (*it).first, (*it).second ); + } + +/* // connect primitive edges in the new fg + for(q = msg_edges.begin(); q != msg_edges.end(); q++) { + if( (!q->src().is_hier()) && (!q->dst().is_hier()) ){ + sfg->connect( q->src(), q->dst() ); + } else { + std::cout << "not connecting hier connection!" << std::endl; + } + }*/ + + // Construct unique list of blocks used either in edges, inputs, + // outputs, or by themselves. I still hate STL. + gr_basic_block_vector_t blocks; // unique list of used blocks + gr_basic_block_vector_t tmp = d_fg->calc_used_blocks(); + + // First add the list of singleton blocks + std::vector<gr_basic_block_sptr>::const_iterator b; // Because flatten_aux is const + for (b = d_blocks.begin(); b != d_blocks.end(); b++) + tmp.push_back(*b); + + // Now add the list of connected input blocks + std::stringstream msg; + for (unsigned int i = 0; i < d_inputs.size(); i++) { + if (d_inputs[i].size() == 0) { + msg << "In hierarchical block " << d_owner->name() << ", input " << i + << " is not connected internally"; + throw std::runtime_error(msg.str()); + } + + for (unsigned int j = 0; j < d_inputs[i].size(); j++) + tmp.push_back(d_inputs[i][j].block()); + } + + for (unsigned int i = 0; i < d_outputs.size(); i++) { + gr_basic_block_sptr blk = d_outputs[i].block(); + if (!blk) { + msg << "In hierarchical block " << d_owner->name() << ", output " << i + << " is not connected internally"; + throw std::runtime_error(msg.str()); + } + tmp.push_back(blk); + } + sort(tmp.begin(), tmp.end()); + + std::insert_iterator<gr_basic_block_vector_t> inserter(blocks, blocks.begin()); + unique_copy(tmp.begin(), tmp.end(), inserter); + + // Recurse hierarchical children + for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) { + gr_hier_block2_sptr hier_block2(cast_to_hier_block2_sptr(*p)); + if (hier_block2 && (hier_block2.get() != d_owner)) { + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "flatten_aux: recursing into hierarchical block " << hier_block2 << std::endl; + hier_block2->d_detail->flatten_aux(sfg); + } + } +} + +void +gr_hier_block2_detail::lock() +{ + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "lock: entered in " << this << std::endl; + + if (d_parent_detail) + d_parent_detail->lock(); + else + d_owner->lock(); +} + +void +gr_hier_block2_detail::unlock() +{ + if (GR_HIER_BLOCK2_DETAIL_DEBUG) + std::cout << "unlock: entered in " << this << std::endl; + + if (d_parent_detail) + d_parent_detail->unlock(); + else + d_owner->unlock(); +} + diff --git a/gnuradio-core/src/lib/runtime/gr_hier_block2_detail.h b/gnuradio-core/src/lib/runtime/gr_hier_block2_detail.h new file mode 100644 index 000000000..b38dae301 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_hier_block2_detail.h @@ -0,0 +1,74 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2007,2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_GR_HIER_BLOCK2_DETAIL_H +#define INCLUDED_GR_HIER_BLOCK2_DETAIL_H + +#include <gr_core_api.h> +#include <gr_hier_block2.h> +#include <gr_flat_flowgraph.h> +#include <boost/utility.hpp> + +/*! + * \ingroup internal + */ +class GR_CORE_API gr_hier_block2_detail : boost::noncopyable +{ +public: + gr_hier_block2_detail(gr_hier_block2 *owner); + ~gr_hier_block2_detail(); + + void connect(gr_basic_block_sptr block); + void connect(gr_basic_block_sptr src, int src_port, + gr_basic_block_sptr dst, int dst_port); + void msg_connect(gr_basic_block_sptr src, pmt::pmt_t srcport, + gr_basic_block_sptr dst, pmt::pmt_t dstport); + void msg_disconnect(gr_basic_block_sptr src, pmt::pmt_t srcport, + gr_basic_block_sptr dst, pmt::pmt_t dstport); + void disconnect(gr_basic_block_sptr block); + void disconnect(gr_basic_block_sptr, int src_port, + gr_basic_block_sptr, int dst_port); + void disconnect_all(); + void lock(); + void unlock(); + void flatten_aux(gr_flat_flowgraph_sptr sfg) const; + +private: + + // Private implementation data + gr_hier_block2 *d_owner; + gr_hier_block2_detail *d_parent_detail; + gr_flowgraph_sptr d_fg; + std::vector<gr_endpoint_vector_t> d_inputs; // Multiple internal endpoints per external input + gr_endpoint_vector_t d_outputs; // Single internal endpoint per external output + gr_basic_block_vector_t d_blocks; + + + void connect_input(int my_port, int port, gr_basic_block_sptr block); + void connect_output(int my_port, int port, gr_basic_block_sptr block); + void disconnect_input(int my_port, int port, gr_basic_block_sptr block); + void disconnect_output(int my_port, int port, gr_basic_block_sptr block); + + gr_endpoint_vector_t resolve_port(int port, bool is_input); + gr_endpoint_vector_t resolve_endpoint(const gr_endpoint &endp, bool is_input) const; +}; + +#endif /* INCLUDED_GR_HIER_BLOCK2_DETAIL_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_io_signature.cc b/gnuradio-core/src/lib/runtime/gr_io_signature.cc new file mode 100644 index 000000000..6ac9acd17 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_io_signature.cc @@ -0,0 +1,112 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <gr_io_signature.h> +#include <stdexcept> +#include <iostream> + +gr_io_signature_sptr +gr_make_io_signaturev(int min_streams, int max_streams, + const std::vector<int> &sizeof_stream_items) +{ + return gr_io_signature_sptr (new gr_io_signature (min_streams, max_streams, + sizeof_stream_items)); +} + +gr_io_signature_sptr +gr_make_io_signature(int min_streams, int max_streams, + int sizeof_stream_item) +{ + std::vector<int> sizeof_items(1); + sizeof_items[0] = sizeof_stream_item; + return gr_make_io_signaturev(min_streams, max_streams, sizeof_items); +} + +gr_io_signature_sptr +gr_make_io_signature2(int min_streams, int max_streams, + int sizeof_stream_item1, + int sizeof_stream_item2) +{ + std::vector<int> sizeof_items(2); + sizeof_items[0] = sizeof_stream_item1; + sizeof_items[1] = sizeof_stream_item2; + return gr_make_io_signaturev(min_streams, max_streams, sizeof_items); +} + +gr_io_signature_sptr +gr_make_io_signature3(int min_streams, int max_streams, + int sizeof_stream_item1, + int sizeof_stream_item2, + int sizeof_stream_item3) +{ + std::vector<int> sizeof_items(3); + sizeof_items[0] = sizeof_stream_item1; + sizeof_items[1] = sizeof_stream_item2; + sizeof_items[2] = sizeof_stream_item3; + return gr_make_io_signaturev(min_streams, max_streams, sizeof_items); +} + +// ------------------------------------------------------------------------ + + +gr_io_signature::gr_io_signature (int min_streams, int max_streams, + const std::vector<int> &sizeof_stream_items) +{ + if (min_streams < 0 + || (max_streams != IO_INFINITE && max_streams < min_streams)) + throw std::invalid_argument ("gr_io_signature(1)"); + + if (sizeof_stream_items.size() < 1) + throw std::invalid_argument("gr_io_signature(2)"); + + for (size_t i = 0; i < sizeof_stream_items.size(); i++){ + if (max_streams != 0 && sizeof_stream_items[i] < 1) + throw std::invalid_argument("gr_io_signature(3)"); + } + + d_min_streams = min_streams; + d_max_streams = max_streams; + d_sizeof_stream_item = sizeof_stream_items; +} + +gr_io_signature::~gr_io_signature () +{ +} + +int +gr_io_signature::sizeof_stream_item (int _index) const +{ + if (_index < 0) + throw std::invalid_argument ("gr_io_signature::sizeof_stream_item"); + + size_t index = _index; + return d_sizeof_stream_item[std::min(index, d_sizeof_stream_item.size() - 1)]; +} + +std::vector<int> +gr_io_signature::sizeof_stream_items() const +{ + return d_sizeof_stream_item; +} diff --git a/gnuradio-core/src/lib/runtime/gr_io_signature.h b/gnuradio-core/src/lib/runtime/gr_io_signature.h new file mode 100644 index 000000000..fd1825797 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_io_signature.h @@ -0,0 +1,117 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_IO_SIGNATURE_H +#define INCLUDED_IO_SIGNATURE_H + +#include <gr_core_api.h> +#include <gr_runtime_types.h> + +/*! + * \brief Create an i/o signature + * + * \ingroup internal + * \param min_streams specify minimum number of streams (>= 0) + * \param max_streams specify maximum number of streams (>= min_streams or -1 -> infinite) + * \param sizeof_stream_item specify the size of the items in each stream + */ +GR_CORE_API gr_io_signature_sptr +gr_make_io_signature(int min_streams, int max_streams, + int sizeof_stream_item); + +/*! + * \brief Create an i/o signature + * + * \param min_streams specify minimum number of streams (>= 0) + * \param max_streams specify maximum number of streams (>= min_streams or -1 -> infinite) + * \param sizeof_stream_item1 specify the size of the items in the first stream + * \param sizeof_stream_item2 specify the size of the items in the second and subsequent streams + */ +GR_CORE_API gr_io_signature_sptr +gr_make_io_signature2(int min_streams, int max_streams, + int sizeof_stream_item1, + int sizeof_stream_item2 + ); + +/*! + * \brief Create an i/o signature + * + * \param min_streams specify minimum number of streams (>= 0) + * \param max_streams specify maximum number of streams (>= min_streams or -1 -> infinite) + * \param sizeof_stream_item1 specify the size of the items in the first stream + * \param sizeof_stream_item2 specify the size of the items in the second stream + * \param sizeof_stream_item3 specify the size of the items in the third and subsequent streams + */ +GR_CORE_API gr_io_signature_sptr +gr_make_io_signature3(int min_streams, int max_streams, + int sizeof_stream_item1, + int sizeof_stream_item2, + int sizeof_stream_item3 + ); + +/*! + * \brief Create an i/o signature + * + * \param min_streams specify minimum number of streams (>= 0) + * \param max_streams specify maximum number of streams (>= min_streams or -1 -> infinite) + * \param sizeof_stream_items specify the size of the items in the streams + * + * If there are more streams than there are entries in sizeof_stream_items, the + * value of the last entry in sizeof_stream_items is used for the missing values. + * sizeof_stream_items must contain at least 1 entry. + */ +GR_CORE_API gr_io_signature_sptr +gr_make_io_signaturev(int min_streams, int max_streams, + const std::vector<int> &sizeof_stream_items); + + +/*! + * \brief i/o signature for input and output ports. + * \brief misc + */ +class GR_CORE_API gr_io_signature { + int d_min_streams; + int d_max_streams; + std::vector<int> d_sizeof_stream_item; + + gr_io_signature(int min_streams, int max_streams, + const std::vector<int> &sizeof_stream_items); + + friend GR_CORE_API gr_io_signature_sptr + gr_make_io_signaturev(int min_streams, + int max_streams, + const std::vector<int> &sizeof_stream_items); + + public: + + static const int IO_INFINITE = -1; + + ~gr_io_signature (); + + int min_streams () const { return d_min_streams; } + int max_streams () const { return d_max_streams; } + int sizeof_stream_item (int index) const; + std::vector<int> sizeof_stream_items() const; +}; + + +#endif /* INCLUDED_IO_SIGNATURE_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_io_signature.i b/gnuradio-core/src/lib/runtime/gr_io_signature.i new file mode 100644 index 000000000..fe1707e41 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_io_signature.i @@ -0,0 +1,73 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2005,2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +class gr_io_signature; +typedef boost::shared_ptr<gr_io_signature> gr_io_signature_sptr; +%template(gr_io_signature_sptr) boost::shared_ptr<gr_io_signature>; + +%rename(io_signature) gr_make_io_signature; +%rename(io_signature2) gr_make_io_signature2; +%rename(io_signature3) gr_make_io_signature3; +%rename(io_signaturev) gr_make_io_signaturev; + + +gr_io_signature_sptr +gr_make_io_signature(int min_streams, int max_streams, + int sizeof_stream_item); + +gr_io_signature_sptr +gr_make_io_signature2(int min_streams, int max_streams, + int sizeof_stream_item1, + int sizeof_stream_item2 + ); +gr_io_signature_sptr +gr_make_io_signature3(int min_streams, int max_streams, + int sizeof_stream_item1, + int sizeof_stream_item2, + int sizeof_stream_item3 + ); +gr_io_signature_sptr +gr_make_io_signaturev(int min_streams, int max_streams, + const std::vector<int> &sizeof_stream_items); + + +class gr_io_signature { + gr_io_signature (int min_streams, int max_streams, int sizeof_stream_item); + + friend gr_io_signature_sptr + gr_make_io_signaturev(int min_streams, + int max_streams, + const std::vector<int> &sizeof_stream_item); + + public: + + // disabled. Suspected bug in SWIG 1.3.24 + // static const int IO_INFINITE = -1; + + ~gr_io_signature (); + + int min_streams () const { return d_min_streams; } + int max_streams () const { return d_max_streams; } + int sizeof_stream_item (int index) const; + std::vector<int> sizeof_stream_items() const; +}; + diff --git a/gnuradio-core/src/lib/runtime/gr_local_sighandler.cc b/gnuradio-core/src/lib/runtime/gr_local_sighandler.cc new file mode 100644 index 000000000..fb31742e1 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_local_sighandler.cc @@ -0,0 +1,187 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_local_sighandler.h> +#include <stdexcept> +#include <stdio.h> +#include <string.h> + + +gr_local_sighandler::gr_local_sighandler (int signum, + void (*new_handler)(int)) + : d_signum (signum) +{ +#ifdef HAVE_SIGACTION + struct sigaction new_action; + memset (&new_action, 0, sizeof (new_action)); + + new_action.sa_handler = new_handler; + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + + if (sigaction (d_signum, &new_action, &d_old_action) < 0){ + perror ("sigaction (install new)"); + throw std::runtime_error ("sigaction"); + } +#endif +} + +gr_local_sighandler::~gr_local_sighandler () +{ +#ifdef HAVE_SIGACTION + if (sigaction (d_signum, &d_old_action, 0) < 0){ + perror ("sigaction (restore old)"); + throw std::runtime_error ("sigaction"); + } +#endif +} + +void +gr_local_sighandler::throw_signal (int signum) +{ + throw gr_signal (signum); +} + +/* + * Semi-hideous way to may a signal number into a signal name + */ + +#define SIGNAME(x) case x: return #x + +std::string +gr_signal::name () const +{ + char tmp[128]; + + switch (signal ()){ +#ifdef SIGHUP + SIGNAME (SIGHUP); +#endif +#ifdef SIGINT + SIGNAME (SIGINT); +#endif +#ifdef SIGQUIT + SIGNAME (SIGQUIT); +#endif +#ifdef SIGILL + SIGNAME (SIGILL); +#endif +#ifdef SIGTRAP + SIGNAME (SIGTRAP); +#endif +#ifdef SIGABRT + SIGNAME (SIGABRT); +#endif +#ifdef SIGBUS + SIGNAME (SIGBUS); +#endif +#ifdef SIGFPE + SIGNAME (SIGFPE); +#endif +#ifdef SIGKILL + SIGNAME (SIGKILL); +#endif +#ifdef SIGUSR1 + SIGNAME (SIGUSR1); +#endif +#ifdef SIGSEGV + SIGNAME (SIGSEGV); +#endif +#ifdef SIGUSR2 + SIGNAME (SIGUSR2); +#endif +#ifdef SIGPIPE + SIGNAME (SIGPIPE); +#endif +#ifdef SIGALRM + SIGNAME (SIGALRM); +#endif +#ifdef SIGTERM + SIGNAME (SIGTERM); +#endif +#ifdef SIGSTKFLT + SIGNAME (SIGSTKFLT); +#endif +#ifdef SIGCHLD + SIGNAME (SIGCHLD); +#endif +#ifdef SIGCONT + SIGNAME (SIGCONT); +#endif +#ifdef SIGSTOP + SIGNAME (SIGSTOP); +#endif +#ifdef SIGTSTP + SIGNAME (SIGTSTP); +#endif +#ifdef SIGTTIN + SIGNAME (SIGTTIN); +#endif +#ifdef SIGTTOU + SIGNAME (SIGTTOU); +#endif +#ifdef SIGURG + SIGNAME (SIGURG); +#endif +#ifdef SIGXCPU + SIGNAME (SIGXCPU); +#endif +#ifdef SIGXFSZ + SIGNAME (SIGXFSZ); +#endif +#ifdef SIGVTALRM + SIGNAME (SIGVTALRM); +#endif +#ifdef SIGPROF + SIGNAME (SIGPROF); +#endif +#ifdef SIGWINCH + SIGNAME (SIGWINCH); +#endif +#ifdef SIGIO + SIGNAME (SIGIO); +#endif +#ifdef SIGPWR + SIGNAME (SIGPWR); +#endif +#ifdef SIGSYS + SIGNAME (SIGSYS); +#endif + default: +#if defined (HAVE_SNPRINTF) +#if defined (SIGRTMIN) && defined (SIGRTMAX) + if (signal () >= SIGRTMIN && signal () <= SIGRTMAX){ + snprintf (tmp, sizeof (tmp), "SIGRTMIN + %d", signal ()); + return tmp; + } +#endif + snprintf (tmp, sizeof (tmp), "SIGNAL %d", signal ()); + return tmp; +#else + return "Unknown signal"; +#endif + } +} diff --git a/gnuradio-core/src/lib/runtime/gr_local_sighandler.h b/gnuradio-core/src/lib/runtime/gr_local_sighandler.h new file mode 100644 index 000000000..a30e2c13a --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_local_sighandler.h @@ -0,0 +1,65 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_LOCAL_SIGHANDLER_H +#define INCLUDED_GR_LOCAL_SIGHANDLER_H + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +#include <gr_core_api.h> +#include <string> + +/*! + * \brief Get and set signal handler. + * + * \ingroup internal + * Constructor installs new handler, destructor reinstalls + * original value. + */ +class GR_CORE_API gr_local_sighandler { + int d_signum; +#ifdef HAVE_SIGACTION + struct sigaction d_old_action; +#endif +public: + gr_local_sighandler (int signum, void (*new_handler)(int)); + ~gr_local_sighandler (); + + /* throw gr_signal (signum) */ + static void throw_signal (int signum); +}; + +/*! + * \brief Representation of signal. + */ +class GR_CORE_API gr_signal +{ + int d_signum; +public: + gr_signal (int signum) : d_signum (signum) {} + int signal () const { return d_signum; } + std::string name () const; +}; + +#endif /* INCLUDED_GR_LOCAL_SIGHANDLER_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_message.cc b/gnuradio-core/src/lib/runtime/gr_message.cc new file mode 100644 index 000000000..a99dcd765 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_message.cc @@ -0,0 +1,78 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <gr_message.h> +#include <assert.h> +#include <string.h> + +static long s_ncurrently_allocated = 0; + +gr_message_sptr +gr_make_message (long type, double arg1, double arg2, size_t length) +{ + return gr_message_sptr (new gr_message (type, arg1, arg2, length)); +} + +gr_message_sptr +gr_make_message_from_string(const std::string s, long type, double arg1, double arg2) +{ + gr_message_sptr m = gr_make_message(type, arg1, arg2, s.size()); + memcpy(m->msg(), s.data(), s.size()); + return m; +} + + +gr_message::gr_message (long type, double arg1, double arg2, size_t length) + : d_type(type), d_arg1(arg1), d_arg2(arg2) +{ + if (length == 0) + d_buf_start = d_msg_start = d_msg_end = d_buf_end = 0; + else { + d_buf_start = new unsigned char [length]; + d_msg_start = d_buf_start; + d_msg_end = d_buf_end = d_buf_start + length; + } + s_ncurrently_allocated++; +} + +gr_message::~gr_message () +{ + assert (d_next == 0); + delete [] d_buf_start; + d_msg_start = d_msg_end = d_buf_end = 0; + s_ncurrently_allocated--; +} + +std::string +gr_message::to_string() const +{ + return std::string((char *)d_msg_start, length()); +} + +long +gr_message_ncurrently_allocated () +{ + return s_ncurrently_allocated; +} diff --git a/gnuradio-core/src/lib/runtime/gr_message.h b/gnuradio-core/src/lib/runtime/gr_message.h new file mode 100644 index 000000000..d386ca009 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_message.h @@ -0,0 +1,91 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_GR_MESSAGE_H +#define INCLUDED_GR_MESSAGE_H + +#include <gr_core_api.h> +#include <gr_types.h> +#include <string> + +class gr_message; +typedef boost::shared_ptr<gr_message> gr_message_sptr; + +/*! + * \brief public constructor for gr_message + */ +GR_CORE_API gr_message_sptr +gr_make_message(long type = 0, double arg1 = 0, double arg2 = 0, size_t length = 0); + +GR_CORE_API gr_message_sptr +gr_make_message_from_string(const std::string s, long type = 0, double arg1 = 0, double arg2 = 0); + +/*! + * \brief Message class. + * + * \ingroup misc + * The ideas and method names for adjustable message length were + * lifted from the click modular router "Packet" class. + */ +class GR_CORE_API gr_message { + gr_message_sptr d_next; // link field for msg queue + long d_type; // type of the message + double d_arg1; // optional arg1 + double d_arg2; // optional arg2 + + unsigned char *d_buf_start; // start of allocated buffer + unsigned char *d_msg_start; // where the msg starts + unsigned char *d_msg_end; // one beyond end of msg + unsigned char *d_buf_end; // one beyond end of allocated buffer + + gr_message (long type, double arg1, double arg2, size_t length); + + friend GR_CORE_API gr_message_sptr + gr_make_message (long type, double arg1, double arg2, size_t length); + + friend GR_CORE_API gr_message_sptr + gr_make_message_from_string (const std::string s, long type, double arg1, double arg2); + + friend class gr_msg_queue; + + unsigned char *buf_data() const { return d_buf_start; } + size_t buf_len() const { return d_buf_end - d_buf_start; } + +public: + ~gr_message (); + + long type() const { return d_type; } + double arg1() const { return d_arg1; } + double arg2() const { return d_arg2; } + + void set_type(long type) { d_type = type; } + void set_arg1(double arg1) { d_arg1 = arg1; } + void set_arg2(double arg2) { d_arg2 = arg2; } + + unsigned char *msg() const { return d_msg_start; } + size_t length() const { return d_msg_end - d_msg_start; } + std::string to_string() const; + +}; + +GR_CORE_API long gr_message_ncurrently_allocated (); + +#endif /* INCLUDED_GR_MESSAGE_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_message.i b/gnuradio-core/src/lib/runtime/gr_message.i new file mode 100644 index 000000000..356bba5b5 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_message.i @@ -0,0 +1,65 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +class gr_message; +typedef boost::shared_ptr<gr_message> gr_message_sptr; +%template(gr_message_sptr) boost::shared_ptr<gr_message>; + +%rename(message_from_string) gr_make_message_from_string; +gr_message_sptr +gr_make_message_from_string(const std::string s, long type = 0, double arg1 = 0, double arg2 = 0); + +%rename(message) gr_make_message; +gr_message_sptr +gr_make_message(long type = 0, double arg1 = 0, double arg2 = 0, size_t length = 0); + +/*! + * \brief Message. + * + * The ideas and method names for adjustable message length were + * lifted from the click modular router "Packet" class. + */ +class gr_message { + gr_message (long type, double arg1, double arg2, size_t length); + + unsigned char *buf_data() const { return d_buf_start; } + size_t buf_len() const { return d_buf_end - d_buf_start; } + +public: + ~gr_message (); + + long type() const { return d_type; } + double arg1() const { return d_arg1; } + double arg2() const { return d_arg2; } + + void set_type(long type) { d_type = type; } + void set_arg1(double arg1) { d_arg1 = arg1; } + void set_arg2(double arg2) { d_arg2 = arg2; } + + size_t length() const; + std::string to_string() const; + +}; + +%rename(message_ncurrently_allocated) gr_message_ncurrently_allocated; +long gr_message_ncurrently_allocated(); + diff --git a/gnuradio-core/src/lib/runtime/gr_msg_accepter.cc b/gnuradio-core/src/lib/runtime/gr_msg_accepter.cc new file mode 100644 index 000000000..93d5fb20e --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_msg_accepter.cc @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gr_msg_accepter.h> +#include <gr_block.h> +#include <gr_block_detail.h> +#include <gr_hier_block2.h> +#include <stdexcept> + +using namespace pmt; + +gr_msg_accepter::gr_msg_accepter() +{ +} + +gr_msg_accepter::~gr_msg_accepter() +{ + // NOP, required as virtual destructor +} + +void +gr_msg_accepter::post(pmt_t which_port, pmt_t msg) +{ + // Notify derived class, handled case by case + gr_block *p = dynamic_cast<gr_block *>(this); + if (p) { + p->_post(which_port,msg); + return; + } + gr_hier_block2 *p2 = dynamic_cast<gr_hier_block2 *>(this); + if (p2){ + // FIXME do the right thing + return; + } + + throw std::runtime_error("unknown derived class"); +} diff --git a/gnuradio-core/src/lib/runtime/gr_msg_accepter.h b/gnuradio-core/src/lib/runtime/gr_msg_accepter.h new file mode 100644 index 000000000..a497ba6e7 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_msg_accepter.h @@ -0,0 +1,43 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_GR_MSG_ACCEPTER_H +#define INCLUDED_GR_MSG_ACCEPTER_H + +#include <gr_core_api.h> +#include <gruel/msg_accepter.h> +#include <gruel/pmt.h> + +/*! + * \brief Accepts messages and inserts them into a message queue, then notifies + * subclass gr_basic_block there is a message pending. + */ +class GR_CORE_API gr_msg_accepter : public gruel::msg_accepter +{ +public: + gr_msg_accepter(); + ~gr_msg_accepter(); + + void post(pmt::pmt_t which_port, pmt::pmt_t msg); + +}; + +#endif /* INCLUDED_GR_MSG_ACCEPTER_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_msg_handler.cc b/gnuradio-core/src/lib/runtime/gr_msg_handler.cc new file mode 100644 index 000000000..0f9349708 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_msg_handler.cc @@ -0,0 +1,30 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <gr_msg_handler.h> + +gr_msg_handler::~gr_msg_handler () +{ +} diff --git a/gnuradio-core/src/lib/runtime/gr_msg_handler.h b/gnuradio-core/src/lib/runtime/gr_msg_handler.h new file mode 100644 index 000000000..57e8a95d6 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_msg_handler.h @@ -0,0 +1,43 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_GR_MSG_HANDLER_H +#define INCLUDED_GR_MSG_HANDLER_H + +#include <gr_core_api.h> +#include <gr_message.h> + +class gr_msg_handler; +typedef boost::shared_ptr<gr_msg_handler> gr_msg_handler_sptr; + +/*! + * \brief abstract class of message handlers + * \ingroup base + */ +class GR_CORE_API gr_msg_handler { +public: + virtual ~gr_msg_handler (); + + //! handle \p msg + virtual void handle (gr_message_sptr msg) = 0; +}; + +#endif /* INCLUDED_GR_MSG_HANDLER_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_msg_handler.i b/gnuradio-core/src/lib/runtime/gr_msg_handler.i new file mode 100644 index 000000000..f493dac1b --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_msg_handler.i @@ -0,0 +1,32 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/*! + * \brief abstract class of message handlers + */ +class gr_msg_handler { +public: + virtual ~gr_msg_handler () = 0; + + //! handle \p msg + virtual void handle (gr_message_sptr msg) = 0; +}; diff --git a/gnuradio-core/src/lib/runtime/gr_msg_queue.cc b/gnuradio-core/src/lib/runtime/gr_msg_queue.cc new file mode 100644 index 000000000..0cf046771 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_msg_queue.cc @@ -0,0 +1,125 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <gr_msg_queue.h> +#include <stdexcept> + +gr_msg_queue_sptr +gr_make_msg_queue(unsigned int limit) +{ + return gr_msg_queue_sptr (new gr_msg_queue(limit)); +} + +gr_msg_queue::gr_msg_queue(unsigned int limit) + : d_not_empty(), d_not_full(), + /*d_head(0), d_tail(0),*/ d_count(0), d_limit(limit) +{ +} + +gr_msg_queue::~gr_msg_queue() +{ + flush (); +} + +void +gr_msg_queue::insert_tail(gr_message_sptr msg) +{ + if (msg->d_next) + throw std::invalid_argument("gr_msg_queue::insert_tail: msg already in queue"); + + gruel::scoped_lock guard(d_mutex); + + while (full_p()) + d_not_full.wait(guard); + + if (d_tail == 0){ + d_tail = d_head = msg; + //msg->d_next = 0; + msg->d_next.reset(); + } + else { + d_tail->d_next = msg; + d_tail = msg; + //msg->d_next = 0; + msg->d_next.reset(); + } + d_count++; + d_not_empty.notify_one(); +} + +gr_message_sptr +gr_msg_queue::delete_head() +{ + gruel::scoped_lock guard(d_mutex); + gr_message_sptr m; + + while ((m = d_head) == 0) + d_not_empty.wait(guard); + + d_head = m->d_next; + if (d_head == 0){ + //d_tail = 0; + d_tail.reset(); + } + + d_count--; + // m->d_next = 0; + m->d_next.reset(); + d_not_full.notify_one(); + return m; +} + +gr_message_sptr +gr_msg_queue::delete_head_nowait() +{ + gruel::scoped_lock guard(d_mutex); + gr_message_sptr m; + + if ((m = d_head) == 0){ + //return 0; + return gr_message_sptr(); + } + + d_head = m->d_next; + if (d_head == 0){ + //d_tail = 0; + d_tail.reset(); + } + + d_count--; + //m->d_next = 0; + m->d_next.reset(); + d_not_full.notify_one(); + return m; +} + +void +gr_msg_queue::flush() +{ + gr_message_sptr m; + + while ((m = delete_head_nowait ()) != 0) + ; +} diff --git a/gnuradio-core/src/lib/runtime/gr_msg_queue.h b/gnuradio-core/src/lib/runtime/gr_msg_queue.h new file mode 100644 index 000000000..86440bbb0 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_msg_queue.h @@ -0,0 +1,92 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_GR_MSG_QUEUE_H +#define INCLUDED_GR_MSG_QUEUE_H + +#include <gr_core_api.h> +#include <gr_msg_handler.h> +#include <gruel/thread.h> + +class gr_msg_queue; +typedef boost::shared_ptr<gr_msg_queue> gr_msg_queue_sptr; + +GR_CORE_API gr_msg_queue_sptr gr_make_msg_queue(unsigned int limit=0); + +/*! + * \brief thread-safe message queue + * \ingroup misc + */ +class GR_CORE_API gr_msg_queue : public gr_msg_handler { + + gruel::mutex d_mutex; + gruel::condition_variable d_not_empty; + gruel::condition_variable d_not_full; + gr_message_sptr d_head; + gr_message_sptr d_tail; + unsigned int d_count; // # of messages in queue. + unsigned int d_limit; // max # of messages in queue. 0 -> unbounded + +public: + gr_msg_queue(unsigned int limit); + ~gr_msg_queue(); + + //! Generic msg_handler method: insert the message. + void handle(gr_message_sptr msg) { insert_tail (msg); } + + /*! + * \brief Insert message at tail of queue. + * \param msg message + * + * Block if queue if full. + */ + void insert_tail(gr_message_sptr msg); + + /*! + * \brief Delete message from head of queue and return it. + * Block if no message is available. + */ + gr_message_sptr delete_head(); + + /*! + * \brief If there's a message in the q, delete it and return it. + * If no message is available, return 0. + */ + gr_message_sptr delete_head_nowait(); + + //! Delete all messages from the queue + void flush(); + + //! is the queue empty? + bool empty_p() const { return d_count == 0; } + + //! is the queue full? + bool full_p() const { return d_limit != 0 && d_count >= d_limit; } + + //! return number of messages in queue + unsigned int count() const { return d_count; } + + //! return limit on number of message in queue. 0 -> unbounded + unsigned int limit() const { return d_limit; } + +}; + +#endif /* INCLUDED_GR_MSG_QUEUE_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_msg_queue.i b/gnuradio-core/src/lib/runtime/gr_msg_queue.i new file mode 100644 index 000000000..65cbe782b --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_msg_queue.i @@ -0,0 +1,107 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2009,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +class gr_msg_queue; +typedef boost::shared_ptr<gr_msg_queue> gr_msg_queue_sptr; +%template(gr_msg_queue_sptr) boost::shared_ptr<gr_msg_queue>; + +%rename(msg_queue) gr_make_msg_queue; +gr_msg_queue_sptr gr_make_msg_queue(unsigned limit=0); + +/*! + * \brief thread-safe message queue + */ +%ignore gr_msg_queue; +class gr_msg_queue : public gr_msg_handler { +public: + gr_msg_queue(unsigned int limit); + ~gr_msg_queue(); + + //! Generic msg_handler method: insert the message. + //void handle(gr_message_sptr msg) { insert_tail (msg); } + + /*! + * \brief Insert message at tail of queue. + * \param msg message + * + * Block if queue if full. + */ + //void insert_tail(gr_message_sptr msg); + + /*! + * \brief Delete message from head of queue and return it. + * Block if no message is available. + */ + //gr_message_sptr delete_head(); + + /*! + * \brief If there's a message in the q, delete it and return it. + * If no message is available, return 0. + */ + gr_message_sptr delete_head_nowait(); + + //! is the queue empty? + bool empty_p() const; + + //! is the queue full? + bool full_p() const; + + //! return number of messages in queue + unsigned int count() const; + + //! Delete all messages from the queue + void flush(); +}; + +/* + * The following kludge-o-rama releases the Python global interpreter + * lock around these potentially blocking calls. We don't want + * libgnuradio-core to be dependent on Python, thus we create these + * functions that serve as replacements for the normal C++ delete_head + * and insert_tail methods. The %pythoncode smashes these new C++ + * functions into the gr.msg_queue wrapper class, so that everything + * appears normal. (An evil laugh is heard in the distance...) + */ +#ifdef SWIGPYTHON +%inline %{ + gr_message_sptr gr_py_msg_queue__delete_head(gr_msg_queue_sptr q) { + gr_message_sptr msg; + GR_PYTHON_BLOCKING_CODE( + msg = q->delete_head(); + ) + return msg; + } + + void gr_py_msg_queue__insert_tail(gr_msg_queue_sptr q, gr_message_sptr msg) { + GR_PYTHON_BLOCKING_CODE( + q->insert_tail(msg); + ) + } +%} + +// smash in new python delete_head and insert_tail methods... +%pythoncode %{ +gr_msg_queue_sptr.delete_head = gr_py_msg_queue__delete_head +gr_msg_queue_sptr.insert_tail = gr_py_msg_queue__insert_tail +gr_msg_queue_sptr.handle = gr_py_msg_queue__insert_tail +%} +#endif // SWIGPYTHON diff --git a/gnuradio-core/src/lib/runtime/gr_pagesize.cc b/gnuradio-core/src/lib/runtime/gr_pagesize.cc new file mode 100644 index 000000000..e31e05ca7 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_pagesize.cc @@ -0,0 +1,56 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_pagesize.h> +#include <unistd.h> +#include <stdio.h> + +#if defined(_WIN32) && defined(HAVE_GETPAGESIZE) +extern "C" size_t getpagesize(void); +#endif + +int +gr_pagesize () +{ + static int s_pagesize = -1; + + if (s_pagesize == -1){ +#if defined(HAVE_GETPAGESIZE) + s_pagesize = getpagesize (); +#elif defined (HAVE_SYSCONF) + s_pagesize = sysconf (_SC_PAGESIZE); + if (s_pagesize == -1){ + perror ("_SC_PAGESIZE"); + s_pagesize = 4096; + } +#else + fprintf (stderr, "gr_pagesize: no info; setting pagesize = 4096\n"); + s_pagesize = 4096; +#endif + } + return s_pagesize; +} + diff --git a/gnuradio-core/src/lib/runtime/gr_pagesize.h b/gnuradio-core/src/lib/runtime/gr_pagesize.h new file mode 100644 index 000000000..3e2daa925 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_pagesize.h @@ -0,0 +1,34 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef _GR_PAGESIZE_H_ +#define _GR_PAGESIZE_H_ + +#include <gr_core_api.h> + +/*! + * \brief return the page size in bytes + */ + +GR_CORE_API int gr_pagesize (); + + +#endif /* _GR_PAGESIZE_H_ */
\ No newline at end of file diff --git a/gnuradio-core/src/lib/runtime/gr_preferences.cc b/gnuradio-core/src/lib/runtime/gr_preferences.cc new file mode 100644 index 000000000..a0f561660 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_preferences.cc @@ -0,0 +1,108 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_preferences.h> +#include <gr_sys_paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> + +#include <boost/filesystem/operations.hpp> +#include <boost/filesystem/path.hpp> +namespace fs = boost::filesystem; + +/* + * The simplest thing that could possibly work: + * the key is the filename; the value is the file contents. + */ + +static const char * +pathname (const char *key) +{ + static fs::path path; + path = fs::path(gr_appdata_path()) / ".gnuradio" / "prefs" / key; + return path.string().c_str(); +} + +static void +ensure_dir_path () +{ + fs::path path = fs::path(gr_appdata_path()) / ".gnuradio"; + if (!fs::is_directory(path)) fs::create_directory(path); + + path = path / "prefs"; + if (!fs::is_directory(path)) fs::create_directory(path); +} + +const char * +gr_preferences::get (const char *key) +{ + static char buf[1024]; + + FILE *fp = fopen (pathname (key), "r"); + if (fp == 0) { + perror (pathname (key)); + return 0; + } + + memset (buf, 0, sizeof (buf)); + size_t ret = fread (buf, 1, sizeof (buf) - 1, fp); + if(ret == 0) { + if(ferror(fp) != 0) { + perror (pathname (key)); + fclose (fp); + return 0; + } + } + fclose (fp); + return buf; +} + +void +gr_preferences::set (const char *key, const char *value) +{ + ensure_dir_path (); + + FILE *fp = fopen (pathname (key), "w"); + if (fp == 0){ + perror (pathname (key)); + return; + } + + size_t ret = fwrite (value, 1, strlen (value), fp); + if(ret == 0) { + if(ferror(fp) != 0) { + perror (pathname (key)); + fclose (fp); + return; + } + } + fclose (fp); +}; diff --git a/gnuradio-core/src/lib/runtime/gr_preferences.h b/gnuradio-core/src/lib/runtime/gr_preferences.h new file mode 100644 index 000000000..bfcc0424b --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_preferences.h @@ -0,0 +1,34 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GR_PREFERENCES_H_ +#define _GR_PREFERENCES_H_ + +#include <gr_core_api.h> + +class GR_CORE_API gr_preferences { + public: + static const char *get (const char *key); + static void set (const char *key, const char *value); +}; + +#endif /* _GR_PREFERENCES_H_ */
\ No newline at end of file diff --git a/gnuradio-core/src/lib/runtime/gr_realtime.cc b/gnuradio-core/src/lib/runtime/gr_realtime.cc new file mode 100644 index 000000000..75b497999 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_realtime.cc @@ -0,0 +1,32 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2007,2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <gr_realtime.h> + +gr_rt_status_t +gr_enable_realtime_scheduling() +{ + return gruel::enable_realtime_scheduling(); +} diff --git a/gnuradio-core/src/lib/runtime/gr_realtime.h b/gnuradio-core/src/lib/runtime/gr_realtime.h new file mode 100644 index 000000000..fe6549039 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_realtime.h @@ -0,0 +1,37 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_REALTIME_H +#define INCLUDED_GR_REALTIME_H + +#include <gr_core_api.h> +#include <gruel/realtime.h> + +typedef gruel::rt_status_t gr_rt_status_t; + +/*! + * \brief If possible, enable high-priority "real time" scheduling. + * \ingroup misc + */ +GR_CORE_API gr_rt_status_t gr_enable_realtime_scheduling(); + +#endif /* INCLUDED_GR_REALTIME_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_realtime.i b/gnuradio-core/src/lib/runtime/gr_realtime.i new file mode 100644 index 000000000..1051d3e2b --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_realtime.i @@ -0,0 +1,44 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +%rename(enable_realtime_scheduling) gr_enable_realtime_scheduling; + +// NOTE: This is duplicated from gruel/src/include/gruel/gr_realtime.h, +// and must be kept in sync with it. This is the least evil workaround +// for allowing 3rd party code builds to work when GNU Radio is +// installed from binary packages into the standard system directories. +// Otherwise, they can't find #include <gruel/gr_realtime.h>, since +// pkg-config strips -I/usr/include from the --cflags path. + +namespace gruel { + + typedef enum { + RT_OK = 0, + RT_NOT_IMPLEMENTED, + RT_NO_PRIVS, + RT_OTHER_ERROR + } rt_status_t; + +} + +typedef gruel::rt_status_t gr_rt_status_t; +gr_rt_status_t gr_enable_realtime_scheduling(); diff --git a/gnuradio-core/src/lib/runtime/gr_runtime_types.h b/gnuradio-core/src/lib/runtime/gr_runtime_types.h new file mode 100644 index 000000000..0d6149288 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_runtime_types.h @@ -0,0 +1,56 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_RUNTIME_TYPES_H +#define INCLUDED_GR_RUNTIME_TYPES_H + +#include <gr_core_api.h> +#include <gr_types.h> + +/* + * typedefs for smart pointers we use throughout the runtime system + */ + +class gr_basic_block; +class gr_block; +class gr_block_detail; +class gr_hier_block2; +class gr_io_signature; +class gr_buffer; +class gr_buffer_reader; +class gr_flowgraph; +class gr_flat_flowgraph; +class gr_top_block; +class gr_top_block_detail; + +typedef boost::shared_ptr<gr_basic_block> gr_basic_block_sptr; +typedef boost::shared_ptr<gr_block> gr_block_sptr; +typedef boost::shared_ptr<gr_block_detail> gr_block_detail_sptr; +typedef boost::shared_ptr<gr_hier_block2> gr_hier_block2_sptr; +typedef boost::shared_ptr<gr_io_signature> gr_io_signature_sptr; +typedef boost::shared_ptr<gr_buffer> gr_buffer_sptr; +typedef boost::shared_ptr<gr_buffer_reader> gr_buffer_reader_sptr; +typedef boost::shared_ptr<gr_flowgraph> gr_flowgraph_sptr; +typedef boost::shared_ptr<gr_flat_flowgraph> gr_flat_flowgraph_sptr; +typedef boost::shared_ptr<gr_top_block> gr_top_block_sptr; + +#endif /* INCLUDED_GR_RUNTIME_TYPES_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_scheduler.cc b/gnuradio-core/src/lib/runtime/gr_scheduler.cc new file mode 100644 index 000000000..c691f5d99 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_scheduler.cc @@ -0,0 +1,33 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <gr_scheduler.h> + +gr_scheduler::gr_scheduler(gr_flat_flowgraph_sptr ffg, int max_noutput_items) +{ +} + +gr_scheduler::~gr_scheduler() +{ +} diff --git a/gnuradio-core/src/lib/runtime/gr_scheduler.h b/gnuradio-core/src/lib/runtime/gr_scheduler.h new file mode 100644 index 000000000..6d130327f --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_scheduler.h @@ -0,0 +1,65 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_GR_SCHEDULER_H +#define INCLUDED_GR_SCHEDULER_H + +#include <gr_core_api.h> +#include <boost/utility.hpp> +#include <gr_block.h> +#include <gr_flat_flowgraph.h> + + +class gr_scheduler; +typedef boost::shared_ptr<gr_scheduler> gr_scheduler_sptr; + + +/*! + * \brief Abstract scheduler that takes a flattened flow graph and runs it. + * + * Preconditions: details, buffers and buffer readers have been assigned. + */ +class GR_CORE_API gr_scheduler : boost::noncopyable +{ + +public: + /*! + * \brief Construct a scheduler and begin evaluating the graph. + * + * The scheduler will continue running until all blocks until they + * report that they are done or the stop method is called. + */ + gr_scheduler(gr_flat_flowgraph_sptr ffg, int max_noutput_items); + + virtual ~gr_scheduler(); + + /*! + * \brief Tell the scheduler to stop executing. + */ + virtual void stop() = 0; + + /*! + * \brief Block until the graph is done. + */ + virtual void wait() = 0; +}; + +#endif /* INCLUDED_GR_SCHEDULER_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_scheduler_sts.cc b/gnuradio-core/src/lib/runtime/gr_scheduler_sts.cc new file mode 100644 index 000000000..2c96def6d --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_scheduler_sts.cc @@ -0,0 +1,87 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <gr_scheduler_sts.h> +#include <gr_single_threaded_scheduler.h> +#include <gruel/thread_body_wrapper.h> + +class sts_container +{ + gr_block_vector_t d_blocks; + +public: + + sts_container(gr_block_vector_t blocks) + : d_blocks(blocks) {} + + void operator()() + { + gr_make_single_threaded_scheduler(d_blocks)->run(); + } +}; + + +gr_scheduler_sptr +gr_scheduler_sts::make(gr_flat_flowgraph_sptr ffg, int max_noutput_items) +{ + return gr_scheduler_sptr(new gr_scheduler_sts(ffg, max_noutput_items)); +} + +gr_scheduler_sts::gr_scheduler_sts(gr_flat_flowgraph_sptr ffg, int max_noutput_items) + : gr_scheduler(ffg, max_noutput_items) +{ + // Split the flattened flow graph into discrete partitions, each + // of which is topologically sorted. + + std::vector<gr_basic_block_vector_t> graphs = ffg->partition(); + + // For each partition, create a thread to evaluate it using + // an instance of the gr_single_threaded_scheduler + + for (std::vector<gr_basic_block_vector_t>::iterator p = graphs.begin(); + p != graphs.end(); p++) { + + gr_block_vector_t blocks = gr_flat_flowgraph::make_block_vector(*p); + d_threads.create_thread( + gruel::thread_body_wrapper<sts_container>(sts_container(blocks), + "single-threaded-scheduler")); + } +} + +gr_scheduler_sts::~gr_scheduler_sts() +{ + stop(); +} + +void +gr_scheduler_sts::stop() +{ + d_threads.interrupt_all(); +} + +void +gr_scheduler_sts::wait() +{ + d_threads.join_all(); +} diff --git a/gnuradio-core/src/lib/runtime/gr_scheduler_sts.h b/gnuradio-core/src/lib/runtime/gr_scheduler_sts.h new file mode 100644 index 000000000..011874ec0 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_scheduler_sts.h @@ -0,0 +1,63 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_GR_SCHEDULER_STS_H +#define INCLUDED_GR_SCHEDULER_STS_H + +#include <gr_core_api.h> +#include <gr_scheduler.h> +#include <gruel/thread_group.h> + +/*! + * \brief Concrete scheduler that uses the single_threaded_scheduler + */ +class GR_CORE_API gr_scheduler_sts : public gr_scheduler +{ + gruel::thread_group d_threads; + +protected: + /*! + * \brief Construct a scheduler and begin evaluating the graph. + * + * The scheduler will continue running until all blocks until they + * report that they are done or the stop method is called. + */ + gr_scheduler_sts(gr_flat_flowgraph_sptr ffg, int max_noutput_items); + +public: + static gr_scheduler_sptr make(gr_flat_flowgraph_sptr ffg, int max_noutput_items); + + ~gr_scheduler_sts(); + + /*! + * \brief Tell the scheduler to stop executing. + */ + void stop(); + + /*! + * \brief Block until the graph is done. + */ + void wait(); +}; + + + + +#endif /* INCLUDED_GR_SCHEDULER_STS_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_scheduler_tpb.cc b/gnuradio-core/src/lib/runtime/gr_scheduler_tpb.cc new file mode 100644 index 000000000..2824eb1b3 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_scheduler_tpb.cc @@ -0,0 +1,103 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <gr_scheduler_tpb.h> +#include <gr_tpb_thread_body.h> +#include <gruel/thread_body_wrapper.h> +#include <sstream> + +/* + * You know, a lambda expression would be sooo much easier... + */ +class tpb_container +{ + gr_block_sptr d_block; + int d_max_noutput_items; + +public: + tpb_container(gr_block_sptr block, int max_noutput_items) + : d_block(block), d_max_noutput_items(max_noutput_items) {} + + void operator()() + { + gr_tpb_thread_body body(d_block, d_max_noutput_items); + } +}; + + +gr_scheduler_sptr +gr_scheduler_tpb::make(gr_flat_flowgraph_sptr ffg, int max_noutput_items) +{ + return gr_scheduler_sptr(new gr_scheduler_tpb(ffg, max_noutput_items)); +} + +gr_scheduler_tpb::gr_scheduler_tpb(gr_flat_flowgraph_sptr ffg, int max_noutput_items) + : gr_scheduler(ffg, max_noutput_items) +{ + // Get a topologically sorted vector of all the blocks in use. + // Being topologically sorted probably isn't going to matter, but + // there's a non-zero chance it might help... + + gr_basic_block_vector_t used_blocks = ffg->calc_used_blocks(); + used_blocks = ffg->topological_sort(used_blocks); + gr_block_vector_t blocks = gr_flat_flowgraph::make_block_vector(used_blocks); + + // Ensure that the done flag is clear on all blocks + + for (size_t i = 0; i < blocks.size(); i++){ + blocks[i]->detail()->set_done(false); + } + + // Fire off a thead for each block + + for (size_t i = 0; i < blocks.size(); i++){ + std::stringstream name; + name << "thread-per-block[" << i << "]: " << blocks[i]; + + // If set, use internal value instead of global value + if(blocks[i]->is_set_max_noutput_items()) + max_noutput_items = blocks[i]->max_noutput_items(); + + d_threads.create_thread( + gruel::thread_body_wrapper<tpb_container>(tpb_container(blocks[i], max_noutput_items), + name.str())); + } +} + +gr_scheduler_tpb::~gr_scheduler_tpb() +{ + stop(); +} + +void +gr_scheduler_tpb::stop() +{ + d_threads.interrupt_all(); +} + +void +gr_scheduler_tpb::wait() +{ + d_threads.join_all(); +} diff --git a/gnuradio-core/src/lib/runtime/gr_scheduler_tpb.h b/gnuradio-core/src/lib/runtime/gr_scheduler_tpb.h new file mode 100644 index 000000000..a9b3abcfa --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_scheduler_tpb.h @@ -0,0 +1,61 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_GR_SCHEDULER_TPB_H +#define INCLUDED_GR_SCHEDULER_TPB_H + +#include <gr_core_api.h> +#include <gr_scheduler.h> +#include <gruel/thread_group.h> + +/*! + * \brief Concrete scheduler that uses a kernel thread-per-block + */ +class GR_CORE_API gr_scheduler_tpb : public gr_scheduler +{ + gruel::thread_group d_threads; + +protected: + /*! + * \brief Construct a scheduler and begin evaluating the graph. + * + * The scheduler will continue running until all blocks until they + * report that they are done or the stop method is called. + */ + gr_scheduler_tpb(gr_flat_flowgraph_sptr ffg, int max_noutput_items); + +public: + static gr_scheduler_sptr make(gr_flat_flowgraph_sptr ffg, int max_noutput_items=100000); + + ~gr_scheduler_tpb(); + + /*! + * \brief Tell the scheduler to stop executing. + */ + void stop(); + + /*! + * \brief Block until the graph is done. + */ + void wait(); +}; + + +#endif /* INCLUDED_GR_SCHEDULER_TPB_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_select_handler.cc b/gnuradio-core/src/lib/runtime/gr_select_handler.cc new file mode 100644 index 000000000..0fc86354a --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_select_handler.cc @@ -0,0 +1,36 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_select_handler.h> + +gr_select_handler::gr_select_handler(int fd) + : d_fd(fd) +{ +} + +gr_select_handler::~gr_select_handler() +{ +} diff --git a/gnuradio-core/src/lib/runtime/gr_select_handler.h b/gnuradio-core/src/lib/runtime/gr_select_handler.h new file mode 100644 index 000000000..c4c359213 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_select_handler.h @@ -0,0 +1,85 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_SELECT_HANDLER_H +#define INCLUDED_GR_SELECT_HANDLER_H + +#include <gr_core_api.h> +#include <boost/shared_ptr.hpp> + +class gr_select_handler; +typedef boost::shared_ptr<gr_select_handler> gr_select_handler_sptr; + + +/*! + * \brief Abstract handler for select based notification. + * \ingroup base + * + * \sa gr_dispatcher + */ +class GR_CORE_API gr_select_handler +{ + int d_fd; + +protected: + gr_select_handler(int file_descriptor); + +public: + virtual ~gr_select_handler(); + + int fd() const { return d_fd; } + int file_descriptor() const { return d_fd; } + + /*! + * \brief Called when file_descriptor is readable. + * + * Called when the dispatcher detects that file_descriptor can + * be read without blocking. + */ + virtual void handle_read() = 0; + + /*! + * \brief Called when file_descriptor is writable. + * + * Called when dispatcher detects that file descriptor can be + * written without blocking. + */ + virtual void handle_write() = 0; + + /*! + * Called each time around the dispatcher loop to determine whether + * this handler's file descriptor should be added to the list on which + * read events can occur. The default method returns true, indicating + * that by default, all handlers are interested in read events. + */ + virtual bool readable() { return true; } + + /*! + * Called each time around the dispatcher loop to determine whether + * this handler's file descriptor should be added to the list on which + * write events can occur. The default method returns true, indicating + * that by default, all handlers are interested in write events. + */ + virtual bool writable() { return true; } +}; + +#endif /* INCLUDED_GR_SELECT_HANDLER_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_single_threaded_scheduler.cc b/gnuradio-core/src/lib/runtime/gr_single_threaded_scheduler.cc new file mode 100644 index 000000000..1bb9e9b0a --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_single_threaded_scheduler.cc @@ -0,0 +1,364 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_single_threaded_scheduler.h> +#include <gr_block.h> +#include <gr_block_detail.h> +#include <gr_buffer.h> +#include <boost/thread.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <limits> +#include <assert.h> +#include <stdio.h> + +// must be defined to either 0 or 1 +#define ENABLE_LOGGING 0 + +#if (ENABLE_LOGGING) +#define LOG(x) do { x; } while(0) +#else +#define LOG(x) do {;} while(0) +#endif + +static int which_scheduler = 0; + +gr_single_threaded_scheduler_sptr +gr_make_single_threaded_scheduler (const std::vector<gr_block_sptr> &blocks) +{ + return + gr_single_threaded_scheduler_sptr (new gr_single_threaded_scheduler (blocks)); +} + +gr_single_threaded_scheduler::gr_single_threaded_scheduler ( + const std::vector<gr_block_sptr> &blocks) + : d_blocks (blocks), d_enabled (true), d_log(0) +{ + if (ENABLE_LOGGING){ + std::string name = str(boost::format("sst-%d.log") % which_scheduler++); + d_log = new std::ofstream(name.c_str()); + *d_log << "gr_single_threaded_scheduler: " + << d_blocks.size () + << " blocks\n"; + } +} + +gr_single_threaded_scheduler::~gr_single_threaded_scheduler () +{ + if (ENABLE_LOGGING) + delete d_log; +} + +void +gr_single_threaded_scheduler::run () +{ + // d_enabled = true; // KLUDGE + main_loop (); +} + +void +gr_single_threaded_scheduler::stop () +{ + if (0) + std::cout << "gr_singled_threaded_scheduler::stop() " + << this << std::endl; + d_enabled = false; +} + +inline static unsigned int +round_up (unsigned int n, unsigned int multiple) +{ + return ((n + multiple - 1) / multiple) * multiple; +} + +inline static unsigned int +round_down (unsigned int n, unsigned int multiple) +{ + return (n / multiple) * multiple; +} + +// +// Return minimum available write space in all our downstream buffers +// or -1 if we're output blocked and the output we're blocked +// on is done. +// +static int +min_available_space (gr_block_detail *d, int output_multiple) +{ + int min_space = std::numeric_limits<int>::max(); + + for (int i = 0; i < d->noutputs (); i++){ + int n = round_down (d->output(i)->space_available (), output_multiple); + if (n == 0){ // We're blocked on output. + if (d->output(i)->done()){ // Downstream is done, therefore we're done. + return -1; + } + return 0; + } + min_space = std::min (min_space, n); + } + return min_space; +} + +void +gr_single_threaded_scheduler::main_loop () +{ + static const int DEFAULT_CAPACITY = 16; + + int noutput_items; + gr_vector_int ninput_items_required (DEFAULT_CAPACITY); + gr_vector_int ninput_items (DEFAULT_CAPACITY); + gr_vector_const_void_star input_items (DEFAULT_CAPACITY); + gr_vector_void_star output_items (DEFAULT_CAPACITY); + unsigned int bi; + unsigned int nalive; + int max_items_avail; + bool made_progress_last_pass; + bool making_progress; + + for (unsigned i = 0; i < d_blocks.size (); i++) + d_blocks[i]->detail()->set_done (false); // reset any done flags + + for (unsigned i = 0; i < d_blocks.size (); i++) // enable any drivers, etc. + d_blocks[i]->start(); + + + bi = 0; + made_progress_last_pass = true; + making_progress = false; + + // Loop while there are still blocks alive + + nalive = d_blocks.size (); + while (d_enabled && nalive > 0){ + + if (boost::this_thread::interruption_requested()) + break; + + gr_block *m = d_blocks[bi].get (); + gr_block_detail *d = m->detail().get (); + + LOG(*d_log << std::endl << m); + + if (d->done ()) + goto next_block; + + if (d->source_p ()){ + // Invoke sources as a last resort. As long as the previous pass + // made progress, don't call a source. + if (made_progress_last_pass){ + LOG(*d_log << " Skipping source\n"); + goto next_block; + } + + ninput_items_required.resize (0); + ninput_items.resize (0); + input_items.resize (0); + output_items.resize (d->noutputs ()); + + // determine the minimum available output space + noutput_items = min_available_space (d, m->output_multiple ()); + LOG(*d_log << " source\n noutput_items = " << noutput_items << std::endl); + if (noutput_items == -1) // we're done + goto were_done; + + if (noutput_items == 0){ // we're output blocked + LOG(*d_log << " BLKD_OUT\n"); + goto next_block; + } + + goto setup_call_to_work; // jump to common code + } + + else if (d->sink_p ()){ + ninput_items_required.resize (d->ninputs ()); + ninput_items.resize (d->ninputs ()); + input_items.resize (d->ninputs ()); + output_items.resize (0); + LOG(*d_log << " sink\n"); + + max_items_avail = 0; + for (int i = 0; i < d->ninputs (); i++){ + ninput_items[i] = d->input(i)->items_available(); + //if (ninput_items[i] == 0 && d->input(i)->done()) + if (ninput_items[i] < m->output_multiple() && d->input(i)->done()) + goto were_done; + + max_items_avail = std::max (max_items_avail, ninput_items[i]); + } + + // take a swag at how much output we can sink + noutput_items = (int) (max_items_avail * m->relative_rate ()); + noutput_items = round_down (noutput_items, m->output_multiple ()); + LOG(*d_log << " max_items_avail = " << max_items_avail << std::endl); + LOG(*d_log << " noutput_items = " << noutput_items << std::endl); + + if (noutput_items == 0){ // we're blocked on input + LOG(*d_log << " BLKD_IN\n"); + goto next_block; + } + + goto try_again; // Jump to code shared with regular case. + } + + else { + // do the regular thing + ninput_items_required.resize (d->ninputs ()); + ninput_items.resize (d->ninputs ()); + input_items.resize (d->ninputs ()); + output_items.resize (d->noutputs ()); + + max_items_avail = 0; + for (int i = 0; i < d->ninputs (); i++){ + ninput_items[i] = d->input(i)->items_available (); + max_items_avail = std::max (max_items_avail, ninput_items[i]); + } + + // determine the minimum available output space + noutput_items = min_available_space (d, m->output_multiple ()); + if (ENABLE_LOGGING){ + *d_log << " regular "; + if (m->relative_rate() >= 1.0) + *d_log << "1:" << m->relative_rate() << std::endl; + else + *d_log << 1.0/m->relative_rate() << ":1\n"; + *d_log << " max_items_avail = " << max_items_avail << std::endl; + *d_log << " noutput_items = " << noutput_items << std::endl; + } + if (noutput_items == -1) // we're done + goto were_done; + + if (noutput_items == 0){ // we're output blocked + LOG(*d_log << " BLKD_OUT\n"); + goto next_block; + } + +#if 0 + // Compute best estimate of noutput_items that we can really use. + noutput_items = + std::min ((unsigned) noutput_items, + std::max ((unsigned) m->output_multiple(), + round_up ((unsigned) (max_items_avail * m->relative_rate()), + m->output_multiple ()))); + + LOG(*d_log << " revised noutput_items = " << noutput_items << std::endl); +#endif + + try_again: + if (m->fixed_rate()){ + // try to work it forward starting with max_items_avail. + // We want to try to consume all the input we've got. + int reqd_noutput_items = m->fixed_rate_ninput_to_noutput(max_items_avail); + reqd_noutput_items = round_up(reqd_noutput_items, m->output_multiple()); + if (reqd_noutput_items > 0 && reqd_noutput_items <= noutput_items) + noutput_items = reqd_noutput_items; + } + + // ask the block how much input they need to produce noutput_items + m->forecast (noutput_items, ninput_items_required); + + // See if we've got sufficient input available + + int i; + for (i = 0; i < d->ninputs (); i++) + if (ninput_items_required[i] > ninput_items[i]) // not enough + break; + + if (i < d->ninputs ()){ // not enough input on input[i] + // if we can, try reducing the size of our output request + if (noutput_items > m->output_multiple ()){ + noutput_items /= 2; + noutput_items = round_up (noutput_items, m->output_multiple ()); + goto try_again; + } + + // We're blocked on input + LOG(*d_log << " BLKD_IN\n"); + if (d->input(i)->done()) // If the upstream block is done, we're done + goto were_done; + + // Is it possible to ever fulfill this request? + if (ninput_items_required[i] > d->input(i)->max_possible_items_available ()){ + // Nope, never going to happen... + std::cerr << "\nsched: <gr_block " << m->name() + << " (" << m->unique_id() << ")>" + << " is requesting more input data\n" + << " than we can provide.\n" + << " ninput_items_required = " + << ninput_items_required[i] << "\n" + << " max_possible_items_available = " + << d->input(i)->max_possible_items_available() << "\n" + << " If this is a filter, consider reducing the number of taps.\n"; + goto were_done; + } + + goto next_block; + } + + // We've got enough data on each input to produce noutput_items. + // Finish setting up the call to work. + + for (int i = 0; i < d->ninputs (); i++) + input_items[i] = d->input(i)->read_pointer(); + + setup_call_to_work: + + for (int i = 0; i < d->noutputs (); i++) + output_items[i] = d->output(i)->write_pointer(); + + // Do the actual work of the block + int n = m->general_work (noutput_items, ninput_items, + input_items, output_items); + LOG(*d_log << " general_work: noutput_items = " << noutput_items + << " result = " << n << std::endl); + + if (n == -1) // block is done + goto were_done; + + d->produce_each (n); // advance write pointers + if (n > 0) + making_progress = true; + + goto next_block; + } + assert (0); + + were_done: + LOG(*d_log << " were_done\n"); + d->set_done (true); + nalive--; + + next_block: + if (++bi >= d_blocks.size ()){ + bi = 0; + made_progress_last_pass = making_progress; + making_progress = false; + } + } + + for (unsigned i = 0; i < d_blocks.size (); i++) // disable any drivers, etc. + d_blocks[i]->stop(); +} diff --git a/gnuradio-core/src/lib/runtime/gr_single_threaded_scheduler.h b/gnuradio-core/src/lib/runtime/gr_single_threaded_scheduler.h new file mode 100644 index 000000000..3a95c7194 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_single_threaded_scheduler.h @@ -0,0 +1,62 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_SINGLE_THREADED_SCHEDULER_H +#define INCLUDED_GR_SINGLE_THREADED_SCHEDULER_H + +#include <gr_core_api.h> +#include <gr_runtime_types.h> +#include <fstream> + +class gr_single_threaded_scheduler; +typedef boost::shared_ptr<gr_single_threaded_scheduler> gr_single_threaded_scheduler_sptr; + + +/*! + * \brief Simple scheduler for stream computations. + * \ingroup internal + */ + +class GR_CORE_API gr_single_threaded_scheduler { + public: + ~gr_single_threaded_scheduler (); + + void run (); + void stop (); + + private: + const std::vector<gr_block_sptr> d_blocks; + volatile bool d_enabled; + std::ofstream *d_log; + + gr_single_threaded_scheduler (const std::vector<gr_block_sptr> &blocks); + + void main_loop (); + + friend GR_CORE_API gr_single_threaded_scheduler_sptr + gr_make_single_threaded_scheduler (const std::vector<gr_block_sptr> &blocks); +}; + +GR_CORE_API gr_single_threaded_scheduler_sptr +gr_make_single_threaded_scheduler (const std::vector<gr_block_sptr> &blocks); + +#endif /* INCLUDED_GR_SINGLE_THREADED_SCHEDULER_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_single_threaded_scheduler.i b/gnuradio-core/src/lib/runtime/gr_single_threaded_scheduler.i new file mode 100644 index 000000000..7305cc9ad --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_single_threaded_scheduler.i @@ -0,0 +1,54 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <gr_runtime.h> + +class gr_single_threaded_scheduler; +typedef boost::shared_ptr<gr_single_threaded_scheduler> gr_single_threaded_scheduler_sptr; +%template(gr_single_threaded_scheduler_sptr) boost::shared_ptr<gr_single_threaded_scheduler>; +%rename(single_threaded_scheduler) gr_make_single_threaded_scheduler; +%ignore gr_single_threaded_scheduler; + +gr_single_threaded_scheduler_sptr +gr_make_single_threaded_scheduler (const std::vector<gr_block_sptr> &modules); + +class gr_single_threaded_scheduler { + public: + ~gr_single_threaded_scheduler (); + + // void run (); + void stop (); + + private: + gr_single_threaded_scheduler (const std::vector<gr_block_sptr> &modules); +}; + +#ifdef SWIGPYTHON +%inline %{ + void sts_pyrun (gr_single_threaded_scheduler_sptr s) { + Py_BEGIN_ALLOW_THREADS; // release global interpreter lock + s->run (); + Py_END_ALLOW_THREADS; // acquire global interpreter lock + } +%} +#endif + diff --git a/gnuradio-core/src/lib/runtime/gr_sptr_magic.cc b/gnuradio-core/src/lib/runtime/gr_sptr_magic.cc new file mode 100644 index 000000000..7fdadf24a --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_sptr_magic.cc @@ -0,0 +1,72 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <gr_sptr_magic.h> +#include <gr_hier_block2.h> +#include <map> +#include <stdexcept> + + +#include <gruel/thread.h> + +namespace gnuradio { + + static gruel::mutex s_mutex; + typedef std::map<gr_basic_block*, gr_basic_block_sptr> sptr_map; + static sptr_map s_map; + + void + detail::sptr_magic::create_and_stash_initial_sptr(gr_hier_block2 *p) + { + gr_basic_block_sptr sptr(p); + gruel::scoped_lock guard(s_mutex); + s_map.insert(sptr_map::value_type(static_cast<gr_basic_block *>(p), sptr)); + } + + + gr_basic_block_sptr + detail::sptr_magic::fetch_initial_sptr(gr_basic_block *p) + { + /* + * If p isn't a subclass of gr_hier_block2, just create the + * shared ptr and return it. + */ + gr_hier_block2 *hb2 = dynamic_cast<gr_hier_block2 *>(p); + if (!hb2){ + return gr_basic_block_sptr(p); + } + + /* + * p is a subclass of gr_hier_block2, thus we've already created the shared pointer + * and stashed it away. Fish it out and return it. + */ + gruel::scoped_lock guard(s_mutex); + sptr_map::iterator pos = s_map.find(static_cast<gr_basic_block *>(p)); + if (pos == s_map.end()) + throw std::invalid_argument("gr_sptr_magic: invalid pointer!"); + + gr_basic_block_sptr sptr = pos->second; + s_map.erase(pos); + return sptr; + } +}; diff --git a/gnuradio-core/src/lib/runtime/gr_sptr_magic.h b/gnuradio-core/src/lib/runtime/gr_sptr_magic.h new file mode 100644 index 000000000..d9c7f26c4 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_sptr_magic.h @@ -0,0 +1,52 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_GR_SPTR_MAGIC_H +#define INCLUDED_GR_SPTR_MAGIC_H + +#include <gr_core_api.h> +#include <boost/shared_ptr.hpp> + +class gr_basic_block; +class gr_hier_block2; + +namespace gnuradio { + + namespace detail { + + class GR_CORE_API sptr_magic { + public: + static boost::shared_ptr<gr_basic_block> fetch_initial_sptr(gr_basic_block *p); + static void create_and_stash_initial_sptr(gr_hier_block2 *p); + }; + }; + + /* + * \brief New! Improved! Standard method to get/create the boost::shared_ptr for a block. + */ + template<class T> + boost::shared_ptr<T> + get_initial_sptr(T *p) + { + return boost::shared_ptr<T>(p);//return boost::dynamic_pointer_cast<T, gr_basic_block>(detail::sptr_magic::fetch_initial_sptr(p)); + } +}; + +#endif /* INCLUDED_GR_SPTR_MAGIC_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_sync_block.cc b/gnuradio-core/src/lib/runtime/gr_sync_block.cc new file mode 100644 index 000000000..3c79e630d --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_sync_block.cc @@ -0,0 +1,55 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_sync_block.h> + +gr_sync_block::gr_sync_block(void) +{ + //NOP +} + +gr_sync_block::gr_sync_block( + const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature +): + gr_block(name, input_signature, output_signature) +{ + this->set_fixed_rate(true); +} + +gr_sync_block::~gr_sync_block(void) +{ + //NOP +} + +int gr_sync_block::work( + int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items +){ + throw std::runtime_error("gr_block subclasses must overload general_work!"); +} diff --git a/gnuradio-core/src/lib/runtime/gr_sync_block.h b/gnuradio-core/src/lib/runtime/gr_sync_block.h new file mode 100644 index 000000000..0f3daccf2 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_sync_block.h @@ -0,0 +1,74 @@ +/* + * Copyright 2012-2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GNURADIO_GR_SYNC_BLOCK_H +#define INCLUDED_GNURADIO_GR_SYNC_BLOCK_H + +#include <gr_block.h> + +struct GR_CORE_API gr_sync_block : public gr_block +{ + gr_sync_block(void); + + gr_sync_block( + const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature + ); + + virtual ~gr_sync_block(void); + + //! implements work -> calls work + int general_work( + int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items + ); + + /*! + * \brief just like gr_block::general_work, only this arranges to call consume_each for you + * + * The user must override work to define the signal processing code + */ + virtual int work( + int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items + ); + +}; + +GRAS_FORCE_INLINE int gr_sync_block::general_work( + int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items +){ + const int work_ret = this->work(noutput_items, input_items, output_items); + if (work_ret > 0) + { + this->consume_each((decimation()*size_t(work_ret))/interpolation()); + } + return work_ret; +} + +#endif /*INCLUDED_GNURADIO_GR_SYNC_BLOCK_H*/ diff --git a/gnuradio-core/src/lib/runtime/gr_sync_block.i b/gnuradio-core/src/lib/runtime/gr_sync_block.i new file mode 100644 index 000000000..d3e1bb957 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_sync_block.i @@ -0,0 +1,29 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +class gr_sync_block : public gr_block +{ + protected: + + gr_sync_block (const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature); +}; diff --git a/gnuradio-core/src/lib/runtime/gr_sync_decimator.cc b/gnuradio-core/src/lib/runtime/gr_sync_decimator.cc new file mode 100644 index 000000000..a39c53b09 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_sync_decimator.cc @@ -0,0 +1,48 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_sync_decimator.h> + +gr_sync_decimator::gr_sync_decimator(void) +{ + //NOP +} + +gr_sync_decimator::gr_sync_decimator( + const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature, + const size_t decim_rate +): + gr_sync_block(name, input_signature, output_signature) +{ + this->set_decimation(decim_rate); +} + +gr_sync_decimator::~gr_sync_decimator(void) +{ + //NOP +} diff --git a/gnuradio-core/src/lib/runtime/gr_sync_decimator.h b/gnuradio-core/src/lib/runtime/gr_sync_decimator.h new file mode 100644 index 000000000..32edfd78e --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_sync_decimator.h @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GNURADIO_GR_SYNC_DECIMATOR_H +#define INCLUDED_GNURADIO_GR_SYNC_DECIMATOR_H + +#include <gr_sync_block.h> + +struct GR_CORE_API gr_sync_decimator : gr_sync_block +{ + + gr_sync_decimator(void); + + gr_sync_decimator( + const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature, + const size_t decim_rate + ); + + virtual ~gr_sync_decimator(void); + +}; + +#endif /*INCLUDED_GNURADIO_GR_SYNC_DECIMATOR_H*/ diff --git a/gnuradio-core/src/lib/runtime/gr_sync_decimator.i b/gnuradio-core/src/lib/runtime/gr_sync_decimator.i new file mode 100644 index 000000000..af4574b19 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_sync_decimator.i @@ -0,0 +1,31 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +class gr_sync_decimator : public gr_sync_block +{ + protected: + + gr_sync_decimator (const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature, + unsigned decimation); +}; diff --git a/gnuradio-core/src/lib/runtime/gr_sync_interpolator.cc b/gnuradio-core/src/lib/runtime/gr_sync_interpolator.cc new file mode 100644 index 000000000..17f60e613 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_sync_interpolator.cc @@ -0,0 +1,48 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_sync_interpolator.h> + +gr_sync_interpolator::gr_sync_interpolator(void) +{ + //NOP +} + +gr_sync_interpolator::gr_sync_interpolator( + const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature, + const size_t interp_rate +): + gr_sync_block(name, input_signature, output_signature) +{ + this->set_interpolation(interp_rate); +} + +gr_sync_interpolator::~gr_sync_interpolator(void) +{ + //NOP +} diff --git a/gnuradio-core/src/lib/runtime/gr_sync_interpolator.h b/gnuradio-core/src/lib/runtime/gr_sync_interpolator.h new file mode 100644 index 000000000..81c17c18e --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_sync_interpolator.h @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GNURADIO_GR_SYNC_INTERPOLATOR_H +#define INCLUDED_GNURADIO_GR_SYNC_INTERPOLATOR_H + +#include <gr_sync_block.h> + +struct GR_CORE_API gr_sync_interpolator : gr_sync_block +{ + + gr_sync_interpolator(void); + + gr_sync_interpolator( + const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature, + const size_t interp_rate + ); + + virtual ~gr_sync_interpolator(void); + +}; + +#endif /*INCLUDED_GNURADIO_GR_SYNC_INTERPOLATOR_H*/ diff --git a/gnuradio-core/src/lib/runtime/gr_sync_interpolator.i b/gnuradio-core/src/lib/runtime/gr_sync_interpolator.i new file mode 100644 index 000000000..6f8b08252 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_sync_interpolator.i @@ -0,0 +1,31 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +class gr_sync_interpolator : public gr_sync_block +{ + protected: + + gr_sync_interpolator (const std::string &name, + gr_io_signature_sptr input_signature, + gr_io_signature_sptr output_signature, + unsigned interpolation); +}; diff --git a/gnuradio-core/src/lib/runtime/gr_sys_paths.cc b/gnuradio-core/src/lib/runtime/gr_sys_paths.cc new file mode 100644 index 000000000..b4918af33 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_sys_paths.cc @@ -0,0 +1,55 @@ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <gr_sys_paths.h> +#include <cstdlib> //getenv +#include <cstdio> //P_tmpdir (maybe) + +const char *gr_tmp_path(){ + const char *path; + + //first case, try TMP environment variable + path = getenv("TMP"); + if (path) return path; + + //second case, try P_tmpdir when its defined + #ifdef P_tmpdir + if (P_tmpdir) return P_tmpdir; + #endif /*P_tmpdir*/ + + //fall-through case, nothing worked + return "/tmp"; +} + +const char *gr_appdata_path(){ + const char *path; + + //first case, try HOME environment variable (unix) + path = getenv("HOME"); + if (path) return path; + + //second case, try APPDATA environment variable (windows) + path = getenv("APPDATA"); + if (path) return path; + + //fall-through case, nothing worked + return gr_tmp_path(); +} diff --git a/gnuradio-core/src/lib/runtime/gr_sys_paths.h b/gnuradio-core/src/lib/runtime/gr_sys_paths.h new file mode 100644 index 000000000..bd51ebdf9 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_sys_paths.h @@ -0,0 +1,33 @@ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GR_SYS_PATHS_H_ +#define _GR_SYS_PATHS_H_ + +#include <gr_core_api.h> + +//! directory to create temporary files +GR_CORE_API const char *gr_tmp_path(); + +//! directory to store application data +GR_CORE_API const char *gr_appdata_path(); + +#endif /* _GR_SYS_PATHS_H_ */ diff --git a/gnuradio-core/src/lib/runtime/gr_tags.h b/gnuradio-core/src/lib/runtime/gr_tags.h new file mode 100644 index 000000000..a9ca90235 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_tags.h @@ -0,0 +1,55 @@ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_TAGS_H +#define INCLUDED_GR_TAGS_H + +#include <gr_core_api.h> +#include <gruel/pmt.h> + +struct GR_CORE_API gr_tag_t{ + + //! the item \p tag occurred at (as a uint64_t) + uint64_t offset; + + //! the key of \p tag (as a PMT symbol) + pmt::pmt_t key; + + //! the value of \p tag (as a PMT) + pmt::pmt_t value; + + //! the source ID of \p tag (as a PMT) + pmt::pmt_t srcid; + + //! Comparison function to test which tag, \p x or \p y, came first in time + static inline bool offset_compare( + const gr_tag_t &x, const gr_tag_t &y + ){ + return x.offset < y.offset; + } + + inline bool operator == (const gr_tag_t &t) const + { + return (t.key == key) && (t.value == value) && (t.srcid == srcid) && (t.offset == offset); + } +}; + +#endif /*INCLUDED_GR_TAGS_H*/ diff --git a/gnuradio-core/src/lib/runtime/gr_tags.i b/gnuradio-core/src/lib/runtime/gr_tags.i new file mode 100644 index 000000000..f2ee69ce8 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_tags.i @@ -0,0 +1,32 @@ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +%{ +#include <gr_tags.h> +%} + +%import <pmt_swig.i> //for pmt support + +%include <gr_tags.h> + +//gives support for a vector of tags (get tags in range) +%include "std_vector.i" +%template(tags_vector_t) std::vector<gr_tag_t>; diff --git a/gnuradio-core/src/lib/runtime/gr_timer.h b/gnuradio-core/src/lib/runtime/gr_timer.h new file mode 100644 index 000000000..e0b4cc437 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_timer.h @@ -0,0 +1,84 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_GR_TIMER_H +#define INCLUDED_GR_TIMER_H + +#include <gr_core_api.h> +#include <gr_types.h> + +class gr_timer; + +typedef boost::shared_ptr<gr_timer> gr_timer_sptr; + +GR_CORE_API typedef void (*gr_timer_hook)(gr_timer *, void *); + +/*! + * \brief create a timeout. + * + * \ingroup misc + * gr_timer_hook is called when timer fires. + */ +GR_CORE_API gr_timer_sptr gr_make_timer (gr_timer_hook, void *); + +/*! + * \brief implement timeouts + */ +class GR_CORE_API gr_timer { + double d_expiry; + double d_period; + gr_timer_hook d_hook; + void *d_hook_arg; + + friend GR_CORE_API gr_timer_sptr gr_make_timer (gr_timer_hook, void *); + + gr_timer (...); + +public: + ~gr_timer (); + + //! return absolute current time (seconds since the epoc). + static double now (); + + /*! + * \brief schedule timer to fire at abs_when + * \param abs_when absolute time in seconds since the epoc. + */ + void schedule_at (double abs_when); + + /*! + * \brief schedule timer to fire rel_when seconds from now. + * \param rel_when relative time in seconds from now. + */ + void schedule_after (double rel_when); // relative time in seconds + + /*! + * \brief schedule a periodic timeout. + * \param abs_when absolute time to fire first time + * \param period time between firings + */ + void schedule_periodic (double abs_when, double period); + + //! cancel timer + void unschedule (); +}; + +#endif /* INCLUDED_GR_TIMER_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_top_block.cc b/gnuradio-core/src/lib/runtime/gr_top_block.cc new file mode 100644 index 000000000..fa090b4ef --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_top_block.cc @@ -0,0 +1,95 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unistd.h> +#include <gr_top_block.h> +#include <boost/detail/atomic_count.hpp> + +static boost::detail::atomic_count unique_id_pool(0); + +gr_top_block::gr_top_block(void): + //cannot make a null top block, use name constructor + gras::TopBlock("top"), + _unique_id(++unique_id_pool), + _name("top") +{ + //NOP +} + +gr_top_block::gr_top_block(const std::string &name): + gras::TopBlock(name), + _unique_id(++unique_id_pool), + _name(name) +{ + //NOP +} + +gr_top_block_sptr gr_make_top_block(const std::string &name) +{ + return gr_top_block_sptr(new gr_top_block(name)); +} + +void gr_top_block::start(const size_t max_items) +{ + this->set_max_noutput_items(max_items); + this->start(); +} + +void gr_top_block::run(const size_t max_items) +{ + this->set_max_noutput_items(max_items); + this->run(); +} + +int gr_top_block::max_noutput_items(void) const +{ + return this->global_config().maximum_output_items; +} + +void gr_top_block::set_max_noutput_items(int max_items) +{ + this->global_config().maximum_output_items = max_items; +} + +void gr_top_block::run(void) +{ + gras::TopBlock::run(); +} + +void gr_top_block::start(void) +{ + gras::TopBlock::start(); +} + +void gr_top_block::stop(void) +{ + gras::TopBlock::stop(); +} + +void gr_top_block::wait(void) +{ + gras::TopBlock::wait(); +} diff --git a/gnuradio-core/src/lib/runtime/gr_top_block.h b/gnuradio-core/src/lib/runtime/gr_top_block.h new file mode 100644 index 000000000..4e4becd1f --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_top_block.h @@ -0,0 +1,67 @@ +/* + * Copyright 2012-2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GNURADIO_GR_TOP_BLOCK_H +#define INCLUDED_GNURADIO_GR_TOP_BLOCK_H + +#include <gr_core_api.h> +#include <gras/top_block.hpp> +#include <gr_hier_block2.h> + +struct GR_CORE_API gr_top_block : gras::TopBlock +{ + + gr_top_block(void); + + gr_top_block(const std::string &name); + + long unique_id(void) const{return _unique_id;} + std::string name(void) const{return _name;} + long _unique_id; + std::string _name; + + void start(const size_t max_items); + + void run(const size_t max_items); + + int max_noutput_items(void) const; + + void set_max_noutput_items(int max_items); + + void run(void); + + virtual void start(void); + + virtual void stop(void); + + virtual void wait(void); + + inline void lock(void){} + + inline void unlock(void){this->commit();} + +}; + +typedef boost::shared_ptr<gr_top_block> gr_top_block_sptr; + +GR_CORE_API gr_top_block_sptr gr_make_top_block(const std::string &name); + +#endif /*INCLUDED_GNURADIO_GR_TOP_BLOCK_H*/ diff --git a/gnuradio-core/src/lib/runtime/gr_top_block.i b/gnuradio-core/src/lib/runtime/gr_top_block.i new file mode 100644 index 000000000..6ae4c65a9 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_top_block.i @@ -0,0 +1,73 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008,2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +class gr_top_block; +typedef boost::shared_ptr<gr_top_block> gr_top_block_sptr; +%template(gr_top_block_sptr) boost::shared_ptr<gr_top_block>; + +// Hack to have a Python shim implementation of gr.top_block +// that instantiates one of these and passes through calls +%rename(top_block_swig) gr_make_top_block; +gr_top_block_sptr gr_make_top_block(const std::string name) + throw (std::logic_error); + +class gr_top_block : public gr_hier_block2 +{ +private: + gr_top_block(const std::string &name); + +public: + ~gr_top_block(); + + void start(int max_noutput_items=100000) throw (std::runtime_error); + void stop(); + //void wait(); + //void run() throw (std::runtime_error); + void lock(); + void unlock() throw (std::runtime_error); + void dump(); + + int max_noutput_items(); + void set_max_noutput_items(int nmax); + + gr_top_block_sptr to_top_block(); // Needed for Python type coercion +}; + +#ifdef SWIGPYTHON + +%inline %{ +void top_block_run_unlocked(gr_top_block_sptr r) throw (std::runtime_error) +{ + Py_BEGIN_ALLOW_THREADS; // release global interpreter lock + r->run(); + Py_END_ALLOW_THREADS; // acquire global interpreter lock +} + +void top_block_wait_unlocked(gr_top_block_sptr r) throw (std::runtime_error) +{ + Py_BEGIN_ALLOW_THREADS; // release global interpreter lock + r->wait(); + Py_END_ALLOW_THREADS; // acquire global interpreter lock +} +%} + +#endif diff --git a/gnuradio-core/src/lib/runtime/gr_top_block_impl.cc b/gnuradio-core/src/lib/runtime/gr_top_block_impl.cc new file mode 100644 index 000000000..4a3694163 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_top_block_impl.cc @@ -0,0 +1,195 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_top_block.h> +#include <gr_top_block_impl.h> +#include <gr_flat_flowgraph.h> +#include <gr_scheduler_sts.h> +#include <gr_scheduler_tpb.h> + +#include <stdexcept> +#include <iostream> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +#define GR_TOP_BLOCK_IMPL_DEBUG 0 + + +typedef gr_scheduler_sptr (*scheduler_maker)(gr_flat_flowgraph_sptr ffg, + int max_noutput_items); + +static struct scheduler_table { + const char *name; + scheduler_maker f; +} scheduler_table[] = { + { "TPB", gr_scheduler_tpb::make }, // first entry is default + { "STS", gr_scheduler_sts::make } +}; + +static gr_scheduler_sptr +make_scheduler(gr_flat_flowgraph_sptr ffg, int max_noutput_items) +{ + static scheduler_maker factory = 0; + + if (factory == 0){ + char *v = getenv("GR_SCHEDULER"); + if (!v) + factory = scheduler_table[0].f; // use default + else { + for (size_t i = 0; i < sizeof(scheduler_table)/sizeof(scheduler_table[0]); i++){ + if (strcmp(v, scheduler_table[i].name) == 0){ + factory = scheduler_table[i].f; + break; + } + } + if (factory == 0){ + std::cerr << "warning: Invalid GR_SCHEDULER environment variable value \"" + << v << "\". Using \"" << scheduler_table[0].name << "\"\n"; + factory = scheduler_table[0].f; + } + } + } + return factory(ffg, max_noutput_items); +} + + +gr_top_block_impl::gr_top_block_impl(gr_top_block *owner) + : d_owner(owner), d_ffg(), + d_state(IDLE), d_lock_count(0) +{ +} + +gr_top_block_impl::~gr_top_block_impl() +{ + d_owner = 0; +} + +void +gr_top_block_impl::start(int max_noutput_items) +{ + gruel::scoped_lock l(d_mutex); + + d_max_noutput_items = max_noutput_items; + + if (d_state != IDLE) + throw std::runtime_error("top_block::start: top block already running or wait() not called after previous stop()"); + + if (d_lock_count > 0) + throw std::runtime_error("top_block::start: can't start with flow graph locked"); + + // Create new flat flow graph by flattening hierarchy + d_ffg = d_owner->flatten(); + + // Validate new simple flow graph and wire it up + d_ffg->validate(); + d_ffg->setup_connections(); + + d_scheduler = make_scheduler(d_ffg, d_max_noutput_items); + d_state = RUNNING; +} + +void +gr_top_block_impl::stop() +{ + if (d_scheduler) + d_scheduler->stop(); +} + + +void +gr_top_block_impl::wait() +{ + if (d_scheduler) + d_scheduler->wait(); + + d_state = IDLE; +} + +// N.B. lock() and unlock() cannot be called from a flow graph thread or +// deadlock will occur when reconfiguration happens +void +gr_top_block_impl::lock() +{ + gruel::scoped_lock lock(d_mutex); + d_lock_count++; +} + +void +gr_top_block_impl::unlock() +{ + gruel::scoped_lock lock(d_mutex); + + if (d_lock_count <= 0){ + d_lock_count = 0; // fix it, then complain + throw std::runtime_error("unpaired unlock() call"); + } + + d_lock_count--; + if (d_lock_count > 0 || d_state == IDLE) // nothing to do + return; + + restart(); +} + +/* + * restart is called with d_mutex held + */ +void +gr_top_block_impl::restart() +{ + stop(); // Stop scheduler and wait for completion + wait(); + + // Create new simple flow graph + gr_flat_flowgraph_sptr new_ffg = d_owner->flatten(); + new_ffg->validate(); // check consistency, sanity, etc + new_ffg->merge_connections(d_ffg); // reuse buffers, etc + d_ffg = new_ffg; + + // Create a new scheduler to execute it + d_scheduler = make_scheduler(d_ffg, d_max_noutput_items); + d_state = RUNNING; +} + +void +gr_top_block_impl::dump() +{ + if (d_ffg) + d_ffg->dump(); +} + +int +gr_top_block_impl::max_noutput_items() +{ + return d_max_noutput_items; +} + +void +gr_top_block_impl::set_max_noutput_items(int nmax) +{ + d_max_noutput_items = nmax; +} diff --git a/gnuradio-core/src/lib/runtime/gr_top_block_impl.h b/gnuradio-core/src/lib/runtime/gr_top_block_impl.h new file mode 100644 index 000000000..f55c3f021 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_top_block_impl.h @@ -0,0 +1,85 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_TOP_BLOCK_IMPL_H +#define INCLUDED_GR_TOP_BLOCK_IMPL_H + +#include <gr_core_api.h> +#include <gr_scheduler.h> +#include <gruel/thread.h> + +/*! + *\brief Abstract implementation details of gr_top_block + * \ingroup internal + * + * The actual implementation of gr_top_block. Separate class allows + * decoupling of changes from dependent classes. + * + */ +class GR_CORE_API gr_top_block_impl +{ +public: + gr_top_block_impl(gr_top_block *owner); + ~gr_top_block_impl(); + + // Create and start scheduler threads + void start(int max_noutput_items=100000); + + // Signal scheduler threads to stop + void stop(); + + // Wait for scheduler threads to exit + void wait(); + + // Lock the top block to allow reconfiguration + void lock(); + + // Unlock the top block at end of reconfiguration + void unlock(); + + // Dump the flowgraph to stdout + void dump(); + + // Get the number of max noutput_items in the flowgraph + int max_noutput_items(); + + // Set the maximum number of noutput_items in the flowgraph + void set_max_noutput_items(int nmax); + +protected: + + enum tb_state { IDLE, RUNNING }; + + gr_top_block *d_owner; + gr_flat_flowgraph_sptr d_ffg; + gr_scheduler_sptr d_scheduler; + + gruel::mutex d_mutex; // protects d_state and d_lock_count + tb_state d_state; + int d_lock_count; + int d_max_noutput_items; + +private: + void restart(); +}; + +#endif /* INCLUDED_GR_TOP_BLOCK_IMPL_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_tpb_detail.cc b/gnuradio-core/src/lib/runtime/gr_tpb_detail.cc new file mode 100644 index 000000000..46eb6bbe0 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_tpb_detail.cc @@ -0,0 +1,70 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <gr_tpb_detail.h> +#include <gr_block.h> +#include <gr_block_detail.h> +#include <gr_buffer.h> + +using namespace pmt; + +/* + * We assume that no worker threads are ever running when the + * graph structure is being manipulated, thus it's safe for us to poke + * around in our neighbors w/o holding any locks. + */ + +void +gr_tpb_detail::notify_upstream(gr_block_detail *d) +{ + // For each of our inputs, tell the guy upstream that we've consumed + // some input, and that he most likely has more output buffer space + // available. + + for (size_t i = 0; i < d->d_input.size(); i++){ + // Can you say, "pointer chasing?" + d->d_input[i]->buffer()->link()->detail()->d_tpb.set_output_changed(); + } +} + +void +gr_tpb_detail::notify_downstream(gr_block_detail *d) +{ + // For each of our outputs, tell the guys downstream that they have + // new input available. + + for (size_t i = 0; i < d->d_output.size(); i++){ + gr_buffer_sptr buf = d->d_output[i]; + for (size_t j = 0, k = buf->nreaders(); j < k; j++) + buf->reader(j)->link()->detail()->d_tpb.set_input_changed(); + } +} + +void +gr_tpb_detail::notify_neighbors(gr_block_detail *d) +{ + notify_downstream(d); + notify_upstream(d); +} + diff --git a/gnuradio-core/src/lib/runtime/gr_tpb_detail.h b/gnuradio-core/src/lib/runtime/gr_tpb_detail.h new file mode 100644 index 000000000..69feb6007 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_tpb_detail.h @@ -0,0 +1,89 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_GR_TPB_DETAIL_H +#define INCLUDED_GR_TPB_DETAIL_H + +#include <gr_core_api.h> +#include <gruel/thread.h> +#include <deque> +#include <gruel/pmt.h> + +class gr_block_detail; + +/*! + * \brief used by thread-per-block scheduler + */ +struct GR_CORE_API gr_tpb_detail { + + gruel::mutex mutex; //< protects all vars + bool input_changed; + gruel::condition_variable input_cond; + bool output_changed; + gruel::condition_variable output_cond; + +public: + gr_tpb_detail() + : input_changed(false), output_changed(false) { } + + //! Called by us to tell all our upstream blocks that their output may have changed. + void notify_upstream(gr_block_detail *d); + + //! Called by us to tell all our downstream blocks that their input may have changed. + void notify_downstream(gr_block_detail *d); + + //! Called by us to notify both upstream and downstream + void notify_neighbors(gr_block_detail *d); + + //! Called by pmt msg posters + void notify_msg(){ + input_cond.notify_one(); + output_cond.notify_one(); + } + + //! Called by us + void clear_changed() + { + gruel::scoped_lock guard(mutex); + input_changed = false; + output_changed = false; + } + +private: + + //! Used by notify_downstream + void set_input_changed() + { + gruel::scoped_lock guard(mutex); + input_changed = true; + input_cond.notify_one(); + } + + //! Used by notify_upstream + void set_output_changed() + { + gruel::scoped_lock guard(mutex); + output_changed = true; + output_cond.notify_one(); + } + +}; + +#endif /* INCLUDED_GR_TPB_DETAIL_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_tpb_thread_body.cc b/gnuradio-core/src/lib/runtime/gr_tpb_thread_body.cc new file mode 100644 index 000000000..679fd1512 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_tpb_thread_body.cc @@ -0,0 +1,151 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <gr_tpb_thread_body.h> +#include <gr_prefs.h> +#include <iostream> +#include <boost/thread.hpp> +#include <gruel/pmt.h> +#include <boost/foreach.hpp> + +using namespace pmt; + +gr_tpb_thread_body::gr_tpb_thread_body(gr_block_sptr block, int max_noutput_items) + : d_exec(block, max_noutput_items) +{ + //std::cerr << "gr_tpb_thread_body: " << block << std::endl; + + gr_block_detail *d = block->detail().get(); + gr_block_executor::state s; + pmt_t msg; + + d->threaded = true; + d->thread = gruel::get_current_thread_id(); + + gr_prefs *p = gr_prefs::singleton(); + size_t max_nmsgs = static_cast<size_t>(p->get_long("DEFAULT", "max_messages", 100)); + + // Set thread affinity if it was set before fg was started. + if(block->processor_affinity().size() > 0) { + gruel::thread_bind_to_processor(d->thread, block->processor_affinity()); + } + + while (1){ + boost::this_thread::interruption_point(); + + // handle any queued up messages + //BOOST_FOREACH( pmt::pmt_t port, block->msg_queue.keys() ) + + BOOST_FOREACH( gr_basic_block::msg_queue_map_t::value_type &i, block->msg_queue ) + { + // Check if we have a message handler attached before getting + // any messages. This is mostly a protection for the unknown + // startup sequence of the threads. + if(block->has_msg_handler(i.first)) { + while ((msg = block->delete_head_nowait(i.first))){ + block->dispatch_msg(i.first,msg); + } + } + else { + // If we don't have a handler but are building up messages, + // prune the queue from the front to keep memory in check. + if(block->nmsgs(i.first) > max_nmsgs) + msg = block->delete_head_nowait(i.first); + } + } + + d->d_tpb.clear_changed(); + // run one iteration if we are a connected stream block + if(d->noutputs() >0 || d->ninputs()>0){ + s = d_exec.run_one_iteration(); + } else { + s = gr_block_executor::BLKD_IN; + } + + switch(s){ + case gr_block_executor::READY: // Tell neighbors we made progress. + d->d_tpb.notify_neighbors(d); + break; + + case gr_block_executor::READY_NO_OUTPUT: // Notify upstream only + d->d_tpb.notify_upstream(d); + break; + + case gr_block_executor::DONE: // Game over. + d->d_tpb.notify_neighbors(d); + return; + + case gr_block_executor::BLKD_IN: // Wait for input. + { + gruel::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_p()) + d->d_tpb.input_cond.wait(guard); + + // handle all pending messages + BOOST_FOREACH( gr_basic_block::msg_queue_map_t::value_type &i, block->msg_queue ) + { + while ((msg = block->delete_head_nowait(i.first))){ + guard.unlock(); // release lock while processing msg + block->dispatch_msg(i.first, msg); + guard.lock(); + } + } + } + } + break; + + + case gr_block_executor::BLKD_OUT: // Wait for output buffer space. + { + gruel::scoped_lock guard(d->d_tpb.mutex); + while (!d->d_tpb.output_changed){ + + // wait for output room or message + while(!d->d_tpb.output_changed && block->empty_p()) + d->d_tpb.output_cond.wait(guard); + + // handle all pending messages + BOOST_FOREACH( gr_basic_block::msg_queue_map_t::value_type &i, block->msg_queue ) + { + while ((msg = block->delete_head_nowait(i.first))){ + guard.unlock(); // release lock while processing msg + block->dispatch_msg(i.first,msg); + guard.lock(); + } + } + } + } + break; + + default: + assert(0); + } + } +} + +gr_tpb_thread_body::~gr_tpb_thread_body() +{ +} diff --git a/gnuradio-core/src/lib/runtime/gr_tpb_thread_body.h b/gnuradio-core/src/lib/runtime/gr_tpb_thread_body.h new file mode 100644 index 000000000..f920663a2 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_tpb_thread_body.h @@ -0,0 +1,46 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_GR_TPB_THREAD_BODY_H +#define INCLUDED_GR_TPB_THREAD_BODY_H + +#include <gr_core_api.h> +#include <gr_block_executor.h> +#include <gr_block.h> +#include <gr_block_detail.h> + +/*! + * \brief The body of each thread-per-block thread. + * + * One of these is instantiated in its own thread for each block. The + * constructor turns into the main loop which returns when the block is + * done or is interrupted. + */ + +class GR_CORE_API gr_tpb_thread_body { + gr_block_executor d_exec; + +public: + gr_tpb_thread_body(gr_block_sptr block, int max_noutput_items=100000); + ~gr_tpb_thread_body(); +}; + + +#endif /* INCLUDED_GR_TPB_THREAD_BODY_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_types.h b/gnuradio-core/src/lib/runtime/gr_types.h new file mode 100644 index 000000000..b7c297d61 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_types.h @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GRNURADIO_TYPES_H +#define INCLUDED_GRNURADIO_TYPES_H + +// this section is to satisfy swig includes for gras.i +// since gras.i includes gr_types.h, we only have to edit this file +#include <gras/element.hpp> +#include <gras/block.hpp> +#include <gras/top_block.hpp> +#include <gras/hier_block.hpp> + +// and gnuradio apparently needs its own typedefs for stdint... +#ifdef __cplusplus + +#include <boost/cstdint.hpp> +typedef boost::int16_t gr_int16; +typedef boost::int32_t gr_int32; +typedef boost::int64_t gr_int64; +typedef boost::uint16_t gr_uint16; +typedef boost::uint32_t gr_uint32; +typedef boost::uint64_t gr_uint64; + +typedef std::vector<int> gr_vector_int; +typedef std::vector<unsigned int> gr_vector_uint; +typedef std::vector<float> gr_vector_float; +typedef std::vector<double> gr_vector_double; +typedef std::vector<void *> gr_vector_void_star; +typedef std::vector<const void *> gr_vector_const_void_star; + +#include <complex> +typedef std::complex<float> gr_complex; +typedef std::complex<double> gr_complexd; + +#endif + +#endif /* INCLUDED_GRNURADIO_TYPES_H */ diff --git a/gnuradio-core/src/lib/runtime/gr_unittests.h b/gnuradio-core/src/lib/runtime/gr_unittests.h new file mode 100644 index 000000000..9fbf228cd --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_unittests.h @@ -0,0 +1,43 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_core_api.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string> + +#include <boost/filesystem/operations.hpp> +#include <boost/filesystem/path.hpp> + +static std::string get_unittest_path(const std::string &filename){ + boost::filesystem::path path = boost::filesystem::current_path() / ".unittests"; + if (!boost::filesystem::is_directory(path)) boost::filesystem::create_directory(path); + return (path / filename).string(); +} diff --git a/gnuradio-core/src/lib/runtime/gr_vmcircbuf.cc b/gnuradio-core/src/lib/runtime/gr_vmcircbuf.cc new file mode 100644 index 000000000..522d9515d --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_vmcircbuf.cc @@ -0,0 +1,295 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <gr_vmcircbuf.h> +#include <assert.h> +#include <stdexcept> +#include <gr_preferences.h> +#include <stdio.h> +#include <string.h> +#include <gr_local_sighandler.h> +#include <vector> +#include <boost/format.hpp> + +// all the factories we know about +#include <gr_vmcircbuf_createfilemapping.h> +#include <gr_vmcircbuf_sysv_shm.h> +#include <gr_vmcircbuf_mmap_shm_open.h> +#include <gr_vmcircbuf_mmap_tmpfile.h> + +static const char *FACTORY_PREF_KEY = "gr_vmcircbuf_default_factory"; + +gr_vmcircbuf::~gr_vmcircbuf () +{ +} + +gr_vmcircbuf_factory::~gr_vmcircbuf_factory () +{ +} + +// ---------------------------------------------------------------- + +static gr_vmcircbuf_factory *s_default_factory = 0; + +gr_vmcircbuf_factory * +gr_vmcircbuf_sysconfig::get_default_factory () +{ + if (s_default_factory) + return s_default_factory; + + bool verbose = false; + + std::vector<gr_vmcircbuf_factory *> all = all_factories (); + + const char *name = gr_preferences::get (FACTORY_PREF_KEY); + + if (name){ + for (unsigned int i = 0; i < all.size (); i++){ + if (strcmp (name, all[i]->name ()) == 0){ + s_default_factory = all[i]; + if (verbose) + fprintf (stderr, "gr_vmcircbuf_sysconfig: using %s\n", + s_default_factory->name ()); + return s_default_factory; + } + } + } + + // either we don't have a default, or the default named is not in our + // list of factories. Find the first factory that works. + + if (verbose) + fprintf (stderr, "gr_vmcircbuf_sysconfig: finding a working factory...\n"); + + for (unsigned int i = 0; i < all.size (); i++){ + if (test_factory (all[i], verbose)){ + set_default_factory (all[i]); + return s_default_factory; + } + } + + // We're screwed! + + fprintf (stderr, "gr_vmcircbuf_sysconfig: unable to find a working factory!\n"); + throw std::runtime_error ("gr_vmcircbuf_sysconfig"); +} + +std::vector<gr_vmcircbuf_factory *> +gr_vmcircbuf_sysconfig::all_factories () +{ + std::vector<gr_vmcircbuf_factory *> result; + + result.push_back (gr_vmcircbuf_createfilemapping_factory::singleton ()); +#ifdef TRY_SHM_VMCIRCBUF + result.push_back (gr_vmcircbuf_sysv_shm_factory::singleton ()); + result.push_back (gr_vmcircbuf_mmap_shm_open_factory::singleton ()); +#endif + result.push_back (gr_vmcircbuf_mmap_tmpfile_factory::singleton ()); + + return result; +} + +void +gr_vmcircbuf_sysconfig::set_default_factory (gr_vmcircbuf_factory *f) +{ + gr_preferences::set (FACTORY_PREF_KEY, f->name ()); + s_default_factory = f; +} + + +// ------------------------------------------------------------------------ +// test code for vmcircbuf factories +// ------------------------------------------------------------------------ + +static void +init_buffer (gr_vmcircbuf *c, int counter, int size) +{ + unsigned int *p = (unsigned int *) c->pointer_to_first_copy (); + for (unsigned int i = 0; i < size / sizeof (int); i++) + p[i] = counter + i; +} + +static bool +check_mapping (gr_vmcircbuf *c, int counter, int size, const char *msg, bool verbose) +{ + bool ok = true; + + if (verbose) + fprintf (stderr, "... %s", msg); + + unsigned int *p1 = (unsigned int *) c->pointer_to_first_copy (); + unsigned int *p2 = (unsigned int *) c->pointer_to_second_copy (); + + // fprintf (stderr, "p1 = %p, p2 = %p\n", p1, p2); + + for (unsigned int i = 0; i < size / sizeof (int); i++){ + if (p1[i] != counter + i){ + ok = false; + if (verbose) + fprintf (stderr, " p1[%d] == %u, expected %u\n", i, p1[i], counter + i); + break; + } + if (p2[i] != counter + i){ + if (verbose) + fprintf (stderr, " p2[%d] == %u, expected %u\n", i, p2[i], counter + i); + ok = false; + break; + } + } + + if (ok && verbose){ + fprintf (stderr, " OK\n"); + } + return ok; +} + +static const char * +memsize (int size) +{ + static std::string buf; + if (size >= (1 << 20)){ + buf = str(boost::format("%dMB") % (size / (1 << 20))); + } + else if (size >= (1 << 10)){ + buf = str(boost::format("%dKB") % (size / (1 << 10))); + } + else { + buf = str(boost::format("%d") % size); + } + return buf.c_str(); +} + +static bool +test_a_bunch (gr_vmcircbuf_factory *factory, int n, int size, int *start_ptr, bool verbose) +{ + bool ok = true; + std::vector<int> counter(n); + std::vector<gr_vmcircbuf *> c(n); + int cum_size = 0; + + for (int i = 0; i < n; i++){ + counter[i] = *start_ptr; + *start_ptr += size; + if ((c[i] = factory->make (size)) == 0){ + if (verbose) + fprintf (stderr, + "Failed to allocate gr_vmcircbuf number %d of size %d (cum = %s)\n", + i + 1, size, memsize (cum_size)); + return false; + } + init_buffer (c[i], counter[i], size); + cum_size += size; + } + + for (int i = 0; i < n; i++){ + std::string msg = str(boost::format("test_a_bunch_%dx%s[%d]") % n % memsize (size) % i); + ok &= check_mapping (c[i], counter[i], size, msg.c_str(), verbose); + } + + for (int i = 0; i < n; i++){ + delete c[i]; + c[i] = 0; + } + + return ok; +} + +static bool +standard_tests (gr_vmcircbuf_factory *f, int verbose) +{ + if (verbose >= 1) + fprintf (stderr, "Testing %s...\n", f->name ()); + + bool v = verbose >= 2; + int granularity = f->granularity (); + int start = 0; + bool ok = true; + + ok &= test_a_bunch (f, 1, 1 * granularity, &start, v); // 1 x 4KB = 4KB + + if (ok){ + ok &= test_a_bunch (f, 64, 4 * granularity, &start, v); // 64 x 16KB = 1MB + ok &= test_a_bunch (f, 4, 4 * (1L << 20), &start, v); // 4 x 4MB = 16MB +// ok &= test_a_bunch (f, 256, 256 * (1L << 10), &start, v); // 256 x 256KB = 64MB + } + + if (verbose >= 1) + fprintf (stderr, "....... %s: %s", f->name (), ok ? "OK\n" : "Doesn't work\n"); + + return ok; +} + +bool +gr_vmcircbuf_sysconfig::test_factory (gr_vmcircbuf_factory *f, int verbose) +{ + // Install local signal handlers for SIGSEGV and SIGBUS. + // If something goes wrong, these signals may be invoked. + +#ifdef SIGSEGV + gr_local_sighandler sigsegv (SIGSEGV, gr_local_sighandler::throw_signal); +#endif +#ifdef SIGBUS + gr_local_sighandler sigbus (SIGBUS, gr_local_sighandler::throw_signal); +#endif +#ifdef SIGSYS + gr_local_sighandler sigsys (SIGSYS, gr_local_sighandler::throw_signal); +#endif + + try { + return standard_tests (f, verbose); + } + catch (gr_signal &sig){ + if (verbose){ + fprintf (stderr, "....... %s: %s", f->name (), "Doesn't work\n"); + fprintf (stderr, + "gr_vmcircbuf_factory::test_factory (%s): caught %s\n", + f->name (), sig.name().c_str()); + return false; + } + } + catch (...){ + if (verbose){ + fprintf (stderr, "....... %s: %s", f->name (), "Doesn't work\n"); + fprintf (stderr, + "gr_vmcircbuf_factory::test_factory (%s): some kind of uncaught exception\n", + f->name ()); + } + return false; + } + return false; // never gets here. shut compiler up. +} + +bool +gr_vmcircbuf_sysconfig::test_all_factories (int verbose) +{ + bool ok = false; + + std::vector<gr_vmcircbuf_factory *> all = all_factories (); + + for (unsigned int i = 0; i < all.size (); i++) + ok |= test_factory (all[i], verbose); + + return ok; +} diff --git a/gnuradio-core/src/lib/runtime/gr_vmcircbuf.h b/gnuradio-core/src/lib/runtime/gr_vmcircbuf.h new file mode 100644 index 000000000..45c6f969c --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_vmcircbuf.h @@ -0,0 +1,122 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GR_VMCIRCBUF_H_ +#define _GR_VMCIRCBUF_H_ + +#include <gr_core_api.h> +#include <vector> + +/*! + * \brief abstract class to implement doubly mapped virtual memory circular buffers + * \ingroup internal + */ +class GR_CORE_API gr_vmcircbuf { + protected: + int d_size; + char *d_base; + + // CREATORS + gr_vmcircbuf (int size) : d_size (size), d_base (0) {}; + + public: + virtual ~gr_vmcircbuf (); + + // ACCESSORS + void *pointer_to_first_copy () const { return d_base; } + void *pointer_to_second_copy () const { return d_base + d_size; } +}; + +/*! + * \brief abstract factory for creating circular buffers + */ +class GR_CORE_API gr_vmcircbuf_factory { + protected: + gr_vmcircbuf_factory () {}; + virtual ~gr_vmcircbuf_factory (); + + public: + + /*! + * \brief return name of this factory + */ + virtual const char *name () const = 0; + + /*! + * \brief return granularity of mapping, typically equal to page size + */ + virtual int granularity () = 0; + + /*! + * \brief return a gr_vmcircbuf, or 0 if unable. + * + * Call this to create a doubly mapped circular buffer. + */ + virtual gr_vmcircbuf *make (int size) = 0; +}; + +/* + * \brief pulls together all implementations of gr_vmcircbuf + */ +class GR_CORE_API gr_vmcircbuf_sysconfig { + public: + + /* + * \brief return the single instance of the default factory. + * + * returns the default factory to use if it's already defined, + * else find the first working factory and use it. + */ + static gr_vmcircbuf_factory *get_default_factory (); + + + static int granularity () { return get_default_factory()->granularity(); } + static gr_vmcircbuf *make (int size) { return get_default_factory()->make(size); } + + + // N.B. not all factories are guaranteed to work. + // It's too hard to check everything at config time, so we check at runtime + static std::vector<gr_vmcircbuf_factory *> all_factories (); + + // make this factory the default + static void set_default_factory (gr_vmcircbuf_factory *f); + + /*! + * \brief Does this factory really work? + * + * verbose = 0: silent + * verbose = 1: names of factories tested and results + * verbose = 2: all intermediate results + */ + static bool test_factory (gr_vmcircbuf_factory *f, int verbose); + + /*! + * \brief Test all factories, return true if at least one of them works + * verbose = 0: silent + * verbose = 1: names of factories tested and results + * verbose = 2: all intermediate results + */ + static bool test_all_factories (int verbose); +}; + + +#endif /* _GR_VMCIRCBUF_H_ */ diff --git a/gnuradio-core/src/lib/runtime/gr_vmcircbuf_createfilemapping.cc b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_createfilemapping.cc new file mode 100644 index 000000000..1b4d9700a --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_createfilemapping.cc @@ -0,0 +1,204 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2005,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <stdexcept> +#include <assert.h> +#include <unistd.h> +#include <fcntl.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif +#include <errno.h> +#include <stdio.h> +#include <gr_pagesize.h> +#include <gr_vmcircbuf_createfilemapping.h> +#include <boost/format.hpp> + +#ifdef HAVE_CREATEFILEMAPPING +// Print Windows error (could/should be global?) +static void +werror( char *where, DWORD last_error ) +{ + char buf[1024]; + + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + last_error, + 0, // default language + buf, + sizeof(buf)/sizeof(TCHAR), // buffer size + NULL ); + fprintf( stderr, "%s: Error %d: %s", where, last_error, buf ); + return; +} +#endif + + +gr_vmcircbuf_createfilemapping::gr_vmcircbuf_createfilemapping (int size) + : gr_vmcircbuf (size) +{ +#if !defined(HAVE_CREATEFILEMAPPING) + fprintf (stderr, "%s: createfilemapping is not available\n",__FUNCTION__); + throw std::runtime_error ("gr_vmcircbuf_createfilemapping"); +#else + static int s_seg_counter = 0; + + if (size <= 0 || (size % gr_pagesize ()) != 0){ + fprintf (stderr, "gr_vmcircbuf_createfilemapping: invalid size = %d\n", size); + throw std::runtime_error ("gr_vmcircbuf_createfilemapping"); + } + + std::string seg_name = str(boost::format("/gnuradio-%d-%d") % getpid () % s_seg_counter); + + d_handle = CreateFileMapping(INVALID_HANDLE_VALUE, // use paging file + NULL, // default security + PAGE_READWRITE, // read/write access + 0, // max. object size + size, // buffer size + seg_name.c_str()); // name of mapping object + + s_seg_counter++; + if (d_handle == NULL || d_handle == INVALID_HANDLE_VALUE){ + std::string msg = str(boost::format( + "gr_vmcircbuf_mmap_createfilemapping: CreateFileMapping [%s]") % + seg_name ); + werror((char *) msg.c_str(), GetLastError() ); + throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); + } + + // Allocate virtual memory of the needed size, then free it so we can use it + LPVOID first_tmp; + first_tmp = VirtualAlloc( NULL, 2*size, MEM_RESERVE, PAGE_NOACCESS ); + if (first_tmp == NULL){ + werror( "gr_vmcircbuf_mmap_createfilemapping: VirtualAlloc", GetLastError()); + CloseHandle(d_handle); // cleanup + throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); + } + + if (VirtualFree(first_tmp, 0, MEM_RELEASE) == 0){ + werror( "gr_vmcircbuf_mmap_createfilemapping: VirtualFree", GetLastError()); + CloseHandle(d_handle); // cleanup + throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); + } + + d_first_copy = MapViewOfFileEx((HANDLE)d_handle, // handle to map object + FILE_MAP_WRITE, // read/write permission + 0, + 0, + size, + first_tmp); + if (d_first_copy != first_tmp){ + werror( "gr_vmcircbuf_mmap_createfilemapping: MapViewOfFileEx(1)", GetLastError()); + CloseHandle(d_handle); // cleanup + throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); + } + + d_second_copy = MapViewOfFileEx((HANDLE)d_handle, // handle to map object + FILE_MAP_WRITE, // read/write permission + 0, + 0, + size, + (char *)first_tmp + size);//(LPVOID) ((char *)d_first_copy + size)); + + if (d_second_copy != (char *)first_tmp + size){ + werror( "gr_vmcircbuf_mmap_createfilemapping: MapViewOfFileEx(2)", GetLastError()); + UnmapViewOfFile(d_first_copy); + CloseHandle(d_handle); // cleanup + throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); + } + +#ifdef DEBUG + fprintf (stderr,"gr_vmcircbuf_mmap_createfilemapping: contiguous? mmap %p %p %p %p\n", + (char *)d_first_copy, (char *)d_second_copy, size, (char *)d_first_copy + size); +#endif + + // Now remember the important stuff + d_base = (char *) d_first_copy; + d_size = size; +#endif /*HAVE_CREATEFILEMAPPING*/ +} + +gr_vmcircbuf_createfilemapping::~gr_vmcircbuf_createfilemapping () +{ +#ifdef HAVE_CREATEFILEMAPPING + if (UnmapViewOfFile(d_first_copy) == 0) + { + werror("gr_vmcircbuf_createfilemapping: UnmapViewOfFile(d_first_copy)", GetLastError()); + } + d_base=NULL; + if (UnmapViewOfFile(d_second_copy) == 0) + { + werror("gr_vmcircbuf_createfilemapping: UnmapViewOfFile(d_second_copy)", GetLastError()); + } + //d_second=NULL; + CloseHandle(d_handle); +#endif +} + +// ---------------------------------------------------------------- +// The factory interface +// ---------------------------------------------------------------- + + +gr_vmcircbuf_factory *gr_vmcircbuf_createfilemapping_factory::s_the_factory = 0; + +gr_vmcircbuf_factory * +gr_vmcircbuf_createfilemapping_factory::singleton () +{ + if (s_the_factory) + return s_the_factory; + s_the_factory = new gr_vmcircbuf_createfilemapping_factory (); + return s_the_factory; +} + +int +gr_vmcircbuf_createfilemapping_factory::granularity () +{ +#ifdef HAVE_CREATEFILEMAPPING + // return 65536;//TODO, check, is this needed or can we just use gr_pagesize() + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + //fprintf(stderr,"win32 AllocationGranularity %p\n",(int)system_info.dwAllocationGranularity); + return (int)system_info.dwAllocationGranularity; +#else + return gr_pagesize (); +#endif +} + +gr_vmcircbuf * +gr_vmcircbuf_createfilemapping_factory::make (int size) +{ + try + { + return new gr_vmcircbuf_createfilemapping (size); + } + catch (...) + { + return 0; + } +} diff --git a/gnuradio-core/src/lib/runtime/gr_vmcircbuf_createfilemapping.h b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_createfilemapping.h new file mode 100644 index 000000000..5ef31f5c6 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_createfilemapping.h @@ -0,0 +1,76 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GR_VMCIRCBUF_CREATEFILEMAPPING_H_ +#define _GR_VMCIRCBUF_CREATEFILEMAPPING_H_ + +#include <gr_core_api.h> +#include <gr_vmcircbuf.h> + +#ifdef HAVE_CREATEFILEMAPPING +#include <windows.h> +#endif +/*! + * \brief concrete class to implement circular buffers with mmap and shm_open + * \ingroup internal + */ +class GR_CORE_API gr_vmcircbuf_createfilemapping : public gr_vmcircbuf +{ + public: + // CREATORS + gr_vmcircbuf_createfilemapping (int size); + virtual ~gr_vmcircbuf_createfilemapping (); +#ifdef HAVE_CREATEFILEMAPPING + private: + HANDLE d_handle; + LPVOID d_first_copy; + LPVOID d_second_copy; +#endif +}; + +/*! + * \brief concrete factory for circular buffers built using mmap and shm_open + */ +class GR_CORE_API gr_vmcircbuf_createfilemapping_factory : public gr_vmcircbuf_factory +{ + private: + static gr_vmcircbuf_factory *s_the_factory; + + public: + static gr_vmcircbuf_factory *singleton (); + + virtual const char *name () const { return "gr_vmcircbuf_createfilemapping_factory"; } + + /*! + * \brief return granularity of mapping, typically equal to page size + */ + virtual int granularity (); + + /*! + * \brief return a gr_vmcircbuf, or 0 if unable. + * + * Call this to create a doubly mapped circular buffer. + */ + virtual gr_vmcircbuf *make (int size); +}; + +#endif /* _GR_VMCIRCBUF_CREATEFILEMAPPING_H_ */ diff --git a/gnuradio-core/src/lib/runtime/gr_vmcircbuf_mmap_shm_open.cc b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_mmap_shm_open.cc new file mode 100644 index 000000000..3d170081d --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_mmap_shm_open.cc @@ -0,0 +1,205 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <gr_vmcircbuf_mmap_shm_open.h> +#include <stdexcept> +#include <assert.h> +#include <unistd.h> +#include <fcntl.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif +#include <errno.h> +#include <stdio.h> +#include <gr_pagesize.h> +#include <gr_sys_paths.h> + + +gr_vmcircbuf_mmap_shm_open::gr_vmcircbuf_mmap_shm_open (int size) + : gr_vmcircbuf (size) +{ +#if !defined(HAVE_MMAP) || !defined(HAVE_SHM_OPEN) + fprintf (stderr, "gr_vmcircbuf_mmap_shm_open: mmap or shm_open is not available\n"); + throw std::runtime_error ("gr_vmcircbuf_mmap_shm_open"); +#else + static int s_seg_counter = 0; + + if (size <= 0 || (size % gr_pagesize ()) != 0){ + fprintf (stderr, "gr_vmcircbuf_mmap_shm_open: invalid size = %d\n", size); + throw std::runtime_error ("gr_vmcircbuf_mmap_shm_open"); + } + + int shm_fd = -1; + char seg_name[1024]; + static bool portable_format = true; + + // open a new named shared memory segment + + while (1){ + if (portable_format){ + + // This is the POSIX recommended "portable format". + // Of course the "portable format" doesn't work on some systems... + + snprintf (seg_name, sizeof (seg_name), + "/gnuradio-%d-%d", getpid (), s_seg_counter); + } + else { + + // Where the "portable format" doesn't work, we try building + // a full filesystem pathname pointing into a suitable temporary directory. + + snprintf (seg_name, sizeof (seg_name), + "%s/gnuradio-%d-%d", gr_tmp_path (), getpid (), s_seg_counter); + } + + shm_fd = shm_open (seg_name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (shm_fd == -1 && errno == EACCES && portable_format){ + portable_format = false; + continue; // try again using "non-portable format" + } + + s_seg_counter++; + + if (shm_fd == -1){ + if (errno == EEXIST) // Named segment already exists (shouldn't happen). Try again + continue; + + char msg[1024]; + snprintf (msg, sizeof (msg), "gr_vmcircbuf_mmap_shm_open: shm_open [%s]", seg_name); + perror (msg); + throw std::runtime_error ("gr_vmcircbuf_mmap_shm_open"); + } + break; + } + + // We've got a new shared memory segment fd open. + // Now set it's length to 2x what we really want and mmap it in. + + if (ftruncate (shm_fd, (off_t) 2 * size) == -1){ + close (shm_fd); // cleanup + perror ("gr_vmcircbuf_mmap_shm_open: ftruncate (1)"); + throw std::runtime_error ("gr_vmcircbuf_mmap_shm_open"); + } + + void *first_copy = mmap (0, 2 * size, + PROT_READ | PROT_WRITE, MAP_SHARED, + shm_fd, (off_t) 0); + + if (first_copy == MAP_FAILED){ + close (shm_fd); // cleanup + perror ("gr_vmcircbuf_mmap_shm_open: mmap (1)"); + throw std::runtime_error ("gr_vmcircbuf_mmap_shm_open"); + } + + // unmap the 2nd half + if (munmap ((char *) first_copy + size, size) == -1){ + close (shm_fd); // cleanup + perror ("gr_vmcircbuf_mmap_shm_open: munmap (1)"); + throw std::runtime_error ("gr_vmcircbuf_mmap_shm_open"); + } + + // map the first half into the now available hole where the + // second half used to be. + + void *second_copy = mmap ((char *) first_copy + size, size, + PROT_READ | PROT_WRITE, MAP_SHARED, + shm_fd, (off_t) 0); + + if (second_copy == MAP_FAILED){ + close (shm_fd); // cleanup + perror ("gr_vmcircbuf_mmap_shm_open: mmap (2)"); + throw std::runtime_error ("gr_vmcircbuf_mmap_shm_open"); + } + +#if 0 // OS/X doesn't allow you to resize the segment + + // cut the shared memory segment down to size + if (ftruncate (shm_fd, (off_t) size) == -1){ + close (shm_fd); // cleanup + perror ("gr_vmcircbuf_mmap_shm_open: ftruncate (2)"); + throw std::runtime_error ("gr_vmcircbuf_mmap_shm_open"); + } +#endif + + close (shm_fd); // fd no longer needed. The mapping is retained. + + if (shm_unlink (seg_name) == -1){ // unlink the seg_name. + perror ("gr_vmcircbuf_mmap_shm_open: shm_unlink"); + throw std::runtime_error ("gr_vmcircbuf_mmap_shm_open"); + } + + // Now remember the important stuff + + d_base = (char *) first_copy; + d_size = size; +#endif +} + +gr_vmcircbuf_mmap_shm_open::~gr_vmcircbuf_mmap_shm_open () +{ +#if defined(HAVE_MMAP) + if (munmap (d_base, 2 * d_size) == -1){ + perror ("gr_vmcircbuf_mmap_shm_open: munmap (2)"); + } +#endif +} + +// ---------------------------------------------------------------- +// The factory interface +// ---------------------------------------------------------------- + + +gr_vmcircbuf_factory *gr_vmcircbuf_mmap_shm_open_factory::s_the_factory = 0; + +gr_vmcircbuf_factory * +gr_vmcircbuf_mmap_shm_open_factory::singleton () +{ + if (s_the_factory) + return s_the_factory; + + s_the_factory = new gr_vmcircbuf_mmap_shm_open_factory (); + return s_the_factory; +} + +int +gr_vmcircbuf_mmap_shm_open_factory::granularity () +{ + return gr_pagesize (); +} + +gr_vmcircbuf * +gr_vmcircbuf_mmap_shm_open_factory::make (int size) +{ + try { + return new gr_vmcircbuf_mmap_shm_open (size); + } + catch (...){ + return 0; + } +} diff --git a/gnuradio-core/src/lib/runtime/gr_vmcircbuf_mmap_shm_open.h b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_mmap_shm_open.h new file mode 100644 index 000000000..bcbbbac42 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_mmap_shm_open.h @@ -0,0 +1,67 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GR_VMCIRCBUF_MMAP_SHM_OPEN_H_ +#define _GR_VMCIRCBUF_MMAP_SHM_OPEN_H_ + +#include <gr_core_api.h> +#include <gr_vmcircbuf.h> + +/*! + * \brief concrete class to implement circular buffers with mmap and shm_open + * \ingroup internal + */ +class GR_CORE_API gr_vmcircbuf_mmap_shm_open : public gr_vmcircbuf { + public: + + // CREATORS + + gr_vmcircbuf_mmap_shm_open (int size); + virtual ~gr_vmcircbuf_mmap_shm_open (); +}; + +/*! + * \brief concrete factory for circular buffers built using mmap and shm_open + */ +class GR_CORE_API gr_vmcircbuf_mmap_shm_open_factory : public gr_vmcircbuf_factory { + private: + static gr_vmcircbuf_factory *s_the_factory; + + public: + static gr_vmcircbuf_factory *singleton (); + + virtual const char *name () const { return "gr_vmcircbuf_mmap_shm_open_factory"; } + + /*! + * \brief return granularity of mapping, typically equal to page size + */ + virtual int granularity (); + + /*! + * \brief return a gr_vmcircbuf, or 0 if unable. + * + * Call this to create a doubly mapped circular buffer. + */ + virtual gr_vmcircbuf *make (int size); +}; + +#endif /* _GR_VMCIRCBUF_MMAP_SHM_OPEN_H_ */ diff --git a/gnuradio-core/src/lib/runtime/gr_vmcircbuf_mmap_tmpfile.cc b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_mmap_tmpfile.cc new file mode 100644 index 000000000..35de64699 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_mmap_tmpfile.cc @@ -0,0 +1,197 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <gr_vmcircbuf_mmap_tmpfile.h> +#include <stdexcept> +#include <assert.h> +#include <unistd.h> +#include <stdlib.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <gr_pagesize.h> +#include <gr_sys_paths.h> + +gr_vmcircbuf_mmap_tmpfile::gr_vmcircbuf_mmap_tmpfile (int size) + : gr_vmcircbuf (size) +{ +#if !defined(HAVE_MMAP) + fprintf (stderr, "gr_vmcircbuf_mmap_tmpfile: mmap or mkstemp is not available\n"); + throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); +#else + + if (size <= 0 || (size % gr_pagesize ()) != 0){ + fprintf (stderr, "gr_vmcircbuf_mmap_tmpfile: invalid size = %d\n", size); + throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); + } + + int seg_fd = -1; + char seg_name[1024]; + + static int s_seg_counter = 0; + + + // open a temporary file that we'll map in a bit later + + while (1){ + snprintf (seg_name, sizeof (seg_name), + "%s/gnuradio-%d-%d-XXXXXX", gr_tmp_path (), getpid (), s_seg_counter); + s_seg_counter++; + + seg_fd = open (seg_name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (seg_fd == -1){ + if (errno == EEXIST) // File already exists (shouldn't happen). Try again + continue; + + char msg[1024]; + snprintf (msg, sizeof (msg), + "gr_vmcircbuf_mmap_tmpfile: open [%s]", seg_name); + perror (msg); + throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); + } + break; + } + + if (unlink (seg_name) == -1){ + perror ("gr_vmcircbuf_mmap_tmpfile: unlink"); + throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); + } + + // We've got a valid file descriptor to a tmp file. + // Now set it's length to 2x what we really want and mmap it in. + + if (ftruncate (seg_fd, (off_t) 2 * size) == -1){ + close (seg_fd); // cleanup + perror ("gr_vmcircbuf_mmap_tmpfile: ftruncate (1)"); + throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); + } + + void *first_copy = mmap (0, 2 * size, + PROT_READ | PROT_WRITE, MAP_SHARED, + seg_fd, (off_t) 0); + + if (first_copy == MAP_FAILED){ + close (seg_fd); // cleanup + perror ("gr_vmcircbuf_mmap_tmpfile: mmap (1)"); + throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); + } + + // unmap the 2nd half + if (munmap ((char *) first_copy + size, size) == -1){ + close (seg_fd); // cleanup + perror ("gr_vmcircbuf_mmap_tmpfile: munmap (1)"); + throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); + } + + // map the first half into the now available hole where the + // second half used to be. + + void *second_copy = mmap ((char *) first_copy + size, size, + PROT_READ | PROT_WRITE, MAP_SHARED, + seg_fd, (off_t) 0); + + if (second_copy == MAP_FAILED){ + munmap(first_copy, size); // cleanup + close (seg_fd); + perror ("gr_vmcircbuf_mmap_tmpfile: mmap (2)"); + throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); + } + + // check for contiguity + if ((char *) second_copy != (char *) first_copy + size){ + munmap(first_copy, size); // cleanup + munmap(second_copy, size); + close (seg_fd); + perror ("gr_vmcircbuf_mmap_tmpfile: non-contiguous second copy"); + throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); + } + + // cut the tmp file down to size + if (ftruncate (seg_fd, (off_t) size) == -1){ + munmap(first_copy, size); // cleanup + munmap(second_copy, size); + close (seg_fd); + perror ("gr_vmcircbuf_mmap_tmpfile: ftruncate (2)"); + throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); + } + + close (seg_fd); // fd no longer needed. The mapping is retained. + + // Now remember the important stuff + + d_base = (char *) first_copy; + d_size = size; +#endif +} + +gr_vmcircbuf_mmap_tmpfile::~gr_vmcircbuf_mmap_tmpfile () +{ +#if defined(HAVE_MMAP) + if (munmap (d_base, 2 * d_size) == -1){ + perror ("gr_vmcircbuf_mmap_tmpfile: munmap (2)"); + } +#endif +} + +// ---------------------------------------------------------------- +// The factory interface +// ---------------------------------------------------------------- + + +gr_vmcircbuf_factory *gr_vmcircbuf_mmap_tmpfile_factory::s_the_factory = 0; + +gr_vmcircbuf_factory * +gr_vmcircbuf_mmap_tmpfile_factory::singleton () +{ + if (s_the_factory) + return s_the_factory; + + s_the_factory = new gr_vmcircbuf_mmap_tmpfile_factory (); + return s_the_factory; +} + +int +gr_vmcircbuf_mmap_tmpfile_factory::granularity () +{ + return gr_pagesize (); +} + +gr_vmcircbuf * +gr_vmcircbuf_mmap_tmpfile_factory::make (int size) +{ + try { + return new gr_vmcircbuf_mmap_tmpfile (size); + } + catch (...){ + return 0; + } +} diff --git a/gnuradio-core/src/lib/runtime/gr_vmcircbuf_mmap_tmpfile.h b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_mmap_tmpfile.h new file mode 100644 index 000000000..28ff31490 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_mmap_tmpfile.h @@ -0,0 +1,67 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GR_VMCIRCBUF_MMAP_TMPFILE_H_ +#define _GR_VMCIRCBUF_MMAP_TMPFILE_H_ + +#include <gr_core_api.h> +#include <gr_vmcircbuf.h> + +/*! + * \brief concrete class to implement circular buffers with mmap and shm_open + * \ingroup internal + */ +class GR_CORE_API gr_vmcircbuf_mmap_tmpfile : public gr_vmcircbuf { + public: + + // CREATORS + + gr_vmcircbuf_mmap_tmpfile (int size); + virtual ~gr_vmcircbuf_mmap_tmpfile (); +}; + +/*! + * \brief concrete factory for circular buffers built using mmap and shm_open + */ +class GR_CORE_API gr_vmcircbuf_mmap_tmpfile_factory : public gr_vmcircbuf_factory { + private: + static gr_vmcircbuf_factory *s_the_factory; + + public: + static gr_vmcircbuf_factory *singleton (); + + virtual const char *name () const { return "gr_vmcircbuf_mmap_tmpfile_factory"; } + + /*! + * \brief return granularity of mapping, typically equal to page size + */ + virtual int granularity (); + + /*! + * \brief return a gr_vmcircbuf, or 0 if unable. + * + * Call this to create a doubly mapped circular buffer. + */ + virtual gr_vmcircbuf *make (int size); +}; + +#endif /* _GR_VMCIRCBUF_MMAP_TMPFILE_H_ */ diff --git a/gnuradio-core/src/lib/runtime/gr_vmcircbuf_sysv_shm.cc b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_sysv_shm.cc new file mode 100644 index 000000000..d9cf75e70 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_sysv_shm.cc @@ -0,0 +1,194 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <gr_vmcircbuf_sysv_shm.h> +#include <stdexcept> +#include <assert.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#ifdef HAVE_SYS_IPC_H +#include <sys/ipc.h> +#endif +#ifdef HAVE_SYS_SHM_H +#include <sys/shm.h> +#endif +#include <errno.h> +#include <stdio.h> +#include <gr_pagesize.h> + + +gr_vmcircbuf_sysv_shm::gr_vmcircbuf_sysv_shm (int size) + : gr_vmcircbuf (size) +{ +#if !defined(HAVE_SYS_SHM_H) + fprintf (stderr, "gr_vmcircbuf_sysv_shm: sysv shared memory is not available\n"); + throw std::runtime_error ("gr_vmcircbuf_sysv_shm"); +#else + + int pagesize = gr_pagesize(); + + if (size <= 0 || (size % pagesize) != 0){ + fprintf (stderr, "gr_vmcircbuf_sysv_shm: invalid size = %d\n", size); + throw std::runtime_error ("gr_vmcircbuf_sysv_shm"); + } + + int shmid_guard = -1; + int shmid1 = -1; + int shmid2 = -1; + + // We use this as a guard page. We'll map it read-only on both ends of the buffer. + // Ideally we'd map it no access, but I don't think that's possible with SysV + if ((shmid_guard = shmget (IPC_PRIVATE, pagesize, IPC_CREAT | 0400)) == -1){ + perror ("gr_vmcircbuf_sysv_shm: shmget (0)"); + throw std::runtime_error ("gr_vmcircbuf_sysv_shm"); + } + + if ((shmid2 = shmget (IPC_PRIVATE, 2 * size + 2 * pagesize, IPC_CREAT | 0700)) == -1){ + perror ("gr_vmcircbuf_sysv_shm: shmget (1)"); + shmctl (shmid_guard, IPC_RMID, 0); + throw std::runtime_error ("gr_vmcircbuf_sysv_shm"); + } + + if ((shmid1 = shmget (IPC_PRIVATE, size, IPC_CREAT | 0700)) == -1){ + perror ("gr_vmcircbuf_sysv_shm: shmget (2)"); + shmctl (shmid_guard, IPC_RMID, 0); + shmctl (shmid2, IPC_RMID, 0); + throw std::runtime_error ("gr_vmcircbuf_sysv_shm"); + } + + void *first_copy = shmat (shmid2, 0, 0); + if (first_copy == (void *) -1){ + perror ("gr_vmcircbuf_sysv_shm: shmat (1)"); + shmctl (shmid_guard, IPC_RMID, 0); + shmctl (shmid2, IPC_RMID, 0); + shmctl (shmid1, IPC_RMID, 0); + throw std::runtime_error ("gr_vmcircbuf_sysv_shm"); + } + + shmctl (shmid2, IPC_RMID, 0); + + // There may be a race between our detach and attach. + // + // If the system allocates all shared memory segments at the same + // virtual addresses in all processes and if the system allocates + // some other segment to first_copy or first_copoy + size between + // our detach and attach, the attaches below could fail [I've never + // seen it fail for this reason]. + + shmdt (first_copy); + + // first read-only guard page + if (shmat (shmid_guard, first_copy, SHM_RDONLY) == (void *) -1){ + perror ("gr_vmcircbuf_sysv_shm: shmat (2)"); + shmctl (shmid_guard, IPC_RMID, 0); + shmctl (shmid1, IPC_RMID, 0); + throw std::runtime_error ("gr_vmcircbuf_sysv_shm"); + } + + // first copy + if (shmat (shmid1, (char *) first_copy + pagesize, 0) == (void *) -1){ + perror ("gr_vmcircbuf_sysv_shm: shmat (3)"); + shmctl (shmid_guard, IPC_RMID, 0); + shmctl (shmid1, IPC_RMID, 0); + shmdt (first_copy); + throw std::runtime_error ("gr_vmcircbuf_sysv_shm"); + } + + // second copy + if (shmat (shmid1, (char *) first_copy + pagesize + size, 0) == (void *) -1){ + perror ("gr_vmcircbuf_sysv_shm: shmat (4)"); + shmctl (shmid_guard, IPC_RMID, 0); + shmctl (shmid1, IPC_RMID, 0); + shmdt ((char *)first_copy + pagesize); + throw std::runtime_error ("gr_vmcircbuf_sysv_shm"); + } + + // second read-only guard page + if (shmat (shmid_guard, (char *) first_copy + pagesize + 2 * size, SHM_RDONLY) == (void *) -1){ + perror ("gr_vmcircbuf_sysv_shm: shmat (5)"); + shmctl (shmid_guard, IPC_RMID, 0); + shmctl (shmid1, IPC_RMID, 0); + shmdt (first_copy); + shmdt ((char *)first_copy + pagesize); + shmdt ((char *)first_copy + pagesize + size); + throw std::runtime_error ("gr_vmcircbuf_sysv_shm"); + } + + shmctl (shmid1, IPC_RMID, 0); + shmctl (shmid_guard, IPC_RMID, 0); + + // Now remember the important stuff + + d_base = (char *) first_copy + pagesize; + d_size = size; +#endif +} + +gr_vmcircbuf_sysv_shm::~gr_vmcircbuf_sysv_shm () +{ +#if defined(HAVE_SYS_SHM_H) + if (shmdt (d_base - gr_pagesize()) == -1 + || shmdt (d_base) == -1 + || shmdt (d_base + d_size) == -1 + || shmdt (d_base + 2 * d_size) == -1){ + perror ("gr_vmcircbuf_sysv_shm: shmdt (2)"); + } +#endif +} + +// ---------------------------------------------------------------- +// The factory interface +// ---------------------------------------------------------------- + + +gr_vmcircbuf_factory *gr_vmcircbuf_sysv_shm_factory::s_the_factory = 0; + +gr_vmcircbuf_factory * +gr_vmcircbuf_sysv_shm_factory::singleton () +{ + if (s_the_factory) + return s_the_factory; + + s_the_factory = new gr_vmcircbuf_sysv_shm_factory (); + return s_the_factory; +} + +int +gr_vmcircbuf_sysv_shm_factory::granularity () +{ + return gr_pagesize (); +} + +gr_vmcircbuf * +gr_vmcircbuf_sysv_shm_factory::make (int size) +{ + try { + return new gr_vmcircbuf_sysv_shm (size); + } + catch (...){ + return 0; + } +} diff --git a/gnuradio-core/src/lib/runtime/gr_vmcircbuf_sysv_shm.h b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_sysv_shm.h new file mode 100644 index 000000000..9f5c04f0d --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_vmcircbuf_sysv_shm.h @@ -0,0 +1,67 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GR_VMCIRCBUF_SYSV_SHM_H_ +#define _GR_VMCIRCBUF_SYSV_SHM_H_ + +#include <gr_core_api.h> +#include <gr_vmcircbuf.h> + +/*! + * \brief concrete class to implement circular buffers with mmap and shm_open + * \ingroup internal + */ +class GR_CORE_API gr_vmcircbuf_sysv_shm : public gr_vmcircbuf { + public: + + // CREATORS + + gr_vmcircbuf_sysv_shm (int size); + virtual ~gr_vmcircbuf_sysv_shm (); +}; + +/*! + * \brief concrete factory for circular buffers built using mmap and shm_open + */ +class GR_CORE_API gr_vmcircbuf_sysv_shm_factory : public gr_vmcircbuf_factory { + private: + static gr_vmcircbuf_factory *s_the_factory; + + public: + static gr_vmcircbuf_factory *singleton (); + + virtual const char *name () const { return "gr_vmcircbuf_sysv_shm_factory"; } + + /*! + * \brief return granularity of mapping, typically equal to page size + */ + virtual int granularity (); + + /*! + * \brief return a gr_vmcircbuf, or 0 if unable. + * + * Call this to create a doubly mapped circular buffer. + */ + virtual gr_vmcircbuf *make (int size); +}; + +#endif /* _GR_VMCIRCBUF_SYSV_SHM_H_ */ diff --git a/gnuradio-core/src/lib/runtime/pmx_helper.hpp b/gnuradio-core/src/lib/runtime/pmx_helper.hpp new file mode 100644 index 000000000..44cc2997c --- /dev/null +++ b/gnuradio-core/src/lib/runtime/pmx_helper.hpp @@ -0,0 +1,268 @@ +/* + * Copyright 2012-2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_LIBGRAS_PMX_HELPER_HPP +#define INCLUDED_LIBGRAS_PMX_HELPER_HPP + +#include <PMC/PMC.hpp> +#include <PMC/Containers.hpp> +#include <gruel/pmt.h> +#include <boost/foreach.hpp> + +namespace pmt +{ + +inline pmt_t pmc_to_pmt(const PMCC &p) +{ + //the container is null + if (not p) return pmt::pmt_t(); + + #define decl_pmc_to_pmt(type, conv) if (p.is<type >()) return conv(p.as<type >()) + + //bool + decl_pmc_to_pmt(bool, pmt_from_bool); + + //string + decl_pmc_to_pmt(std::string, pmt_string_to_symbol); + + //numeric types + decl_pmc_to_pmt(int8_t, pmt_from_long); + decl_pmc_to_pmt(int16_t, pmt_from_long); + decl_pmc_to_pmt(int32_t, pmt_from_long); + decl_pmc_to_pmt(uint8_t, pmt_from_long); + decl_pmc_to_pmt(uint16_t, pmt_from_long); + decl_pmc_to_pmt(uint32_t, pmt_from_long); + decl_pmc_to_pmt(int64_t, pmt_from_uint64); + decl_pmc_to_pmt(uint64_t, pmt_from_uint64); + decl_pmc_to_pmt(float, pmt_from_double); + decl_pmc_to_pmt(double, pmt_from_double); + #define pmt_from_complex(x) pmt_make_rectangular((x).real(), (x).imag()) + decl_pmc_to_pmt(std::complex<float>, pmt_from_complex); + decl_pmc_to_pmt(std::complex<double>, pmt_from_complex); + + //pair container + if (p.is<PMCPair>()) + { + const PMCPair &pr = p.as<PMCPair>(); + return pmt_cons(pmc_to_pmt(pr.first), pmc_to_pmt(pr.second)); + } + + //fucking tuples +/* +for i in range(11): + args = list() + for j in range(i): + args.append('pmc_to_pmt(p.as<PMCTuple<%d> >()[%d])'%(i, j)) + print ' if (p.is<PMCTuple<%d> >())'%i + print ' return pmt_make_tuple(%s);'%(', '.join(args),) +*/ + if (p.is<PMCTuple<0> >()) + return pmt_make_tuple(); + if (p.is<PMCTuple<1> >()) + return pmt_make_tuple(pmc_to_pmt(p.as<PMCTuple<1> >()[0])); + if (p.is<PMCTuple<2> >()) + return pmt_make_tuple(pmc_to_pmt(p.as<PMCTuple<2> >()[0]), pmc_to_pmt(p.as<PMCTuple<2> >()[1])); + if (p.is<PMCTuple<3> >()) + return pmt_make_tuple(pmc_to_pmt(p.as<PMCTuple<3> >()[0]), pmc_to_pmt(p.as<PMCTuple<3> >()[1]), pmc_to_pmt(p.as<PMCTuple<3> >()[2])); + if (p.is<PMCTuple<4> >()) + return pmt_make_tuple(pmc_to_pmt(p.as<PMCTuple<4> >()[0]), pmc_to_pmt(p.as<PMCTuple<4> >()[1]), pmc_to_pmt(p.as<PMCTuple<4> >()[2]), pmc_to_pmt(p.as<PMCTuple<4> >()[3])); + if (p.is<PMCTuple<5> >()) + return pmt_make_tuple(pmc_to_pmt(p.as<PMCTuple<5> >()[0]), pmc_to_pmt(p.as<PMCTuple<5> >()[1]), pmc_to_pmt(p.as<PMCTuple<5> >()[2]), pmc_to_pmt(p.as<PMCTuple<5> >()[3]), pmc_to_pmt(p.as<PMCTuple<5> >()[4])); + if (p.is<PMCTuple<6> >()) + return pmt_make_tuple(pmc_to_pmt(p.as<PMCTuple<6> >()[0]), pmc_to_pmt(p.as<PMCTuple<6> >()[1]), pmc_to_pmt(p.as<PMCTuple<6> >()[2]), pmc_to_pmt(p.as<PMCTuple<6> >()[3]), pmc_to_pmt(p.as<PMCTuple<6> >()[4]), pmc_to_pmt(p.as<PMCTuple<6> >()[5])); + if (p.is<PMCTuple<7> >()) + return pmt_make_tuple(pmc_to_pmt(p.as<PMCTuple<7> >()[0]), pmc_to_pmt(p.as<PMCTuple<7> >()[1]), pmc_to_pmt(p.as<PMCTuple<7> >()[2]), pmc_to_pmt(p.as<PMCTuple<7> >()[3]), pmc_to_pmt(p.as<PMCTuple<7> >()[4]), pmc_to_pmt(p.as<PMCTuple<7> >()[5]), pmc_to_pmt(p.as<PMCTuple<7> >()[6])); + if (p.is<PMCTuple<8> >()) + return pmt_make_tuple(pmc_to_pmt(p.as<PMCTuple<8> >()[0]), pmc_to_pmt(p.as<PMCTuple<8> >()[1]), pmc_to_pmt(p.as<PMCTuple<8> >()[2]), pmc_to_pmt(p.as<PMCTuple<8> >()[3]), pmc_to_pmt(p.as<PMCTuple<8> >()[4]), pmc_to_pmt(p.as<PMCTuple<8> >()[5]), pmc_to_pmt(p.as<PMCTuple<8> >()[6]), pmc_to_pmt(p.as<PMCTuple<8> >()[7])); + if (p.is<PMCTuple<9> >()) + return pmt_make_tuple(pmc_to_pmt(p.as<PMCTuple<9> >()[0]), pmc_to_pmt(p.as<PMCTuple<9> >()[1]), pmc_to_pmt(p.as<PMCTuple<9> >()[2]), pmc_to_pmt(p.as<PMCTuple<9> >()[3]), pmc_to_pmt(p.as<PMCTuple<9> >()[4]), pmc_to_pmt(p.as<PMCTuple<9> >()[5]), pmc_to_pmt(p.as<PMCTuple<9> >()[6]), pmc_to_pmt(p.as<PMCTuple<9> >()[7]), pmc_to_pmt(p.as<PMCTuple<9> >()[8])); + if (p.is<PMCTuple<10> >()) + return pmt_make_tuple(pmc_to_pmt(p.as<PMCTuple<10> >()[0]), pmc_to_pmt(p.as<PMCTuple<10> >()[1]), pmc_to_pmt(p.as<PMCTuple<10> >()[2]), pmc_to_pmt(p.as<PMCTuple<10> >()[3]), pmc_to_pmt(p.as<PMCTuple<10> >()[4]), pmc_to_pmt(p.as<PMCTuple<10> >()[5]), pmc_to_pmt(p.as<PMCTuple<10> >()[6]), pmc_to_pmt(p.as<PMCTuple<10> >()[7]), pmc_to_pmt(p.as<PMCTuple<10> >()[8]), pmc_to_pmt(p.as<PMCTuple<10> >()[9])); + + //vector container + if (p.is<PMCList>()) + { + const PMCList &l = p.as<PMCList>(); + pmt_t v = pmt_make_vector(l.size(), pmt_t()); + for (size_t i = 0; i < l.size(); i++) + { + pmt_vector_set(v, i, pmc_to_pmt(l[i])); + } + return v; + } + + //numeric arrays + #define decl_pmc_to_pmt_numeric_array(type, suffix) \ + if (p.is<std::vector<type> >()) return pmt_init_ ## suffix ## vector(p.as<std::vector<type> >().size(), &p.as<std::vector<type> >()[0]) + decl_pmc_to_pmt_numeric_array(uint8_t, u8); + decl_pmc_to_pmt_numeric_array(uint16_t, u16); + decl_pmc_to_pmt_numeric_array(uint32_t, u32); + decl_pmc_to_pmt_numeric_array(uint64_t, u64); + decl_pmc_to_pmt_numeric_array(int8_t, s8); + decl_pmc_to_pmt_numeric_array(int16_t, s16); + decl_pmc_to_pmt_numeric_array(int32_t, s32); + decl_pmc_to_pmt_numeric_array(int64_t, s64); + decl_pmc_to_pmt_numeric_array(float, f32); + decl_pmc_to_pmt_numeric_array(double, f64); + decl_pmc_to_pmt_numeric_array(std::complex<float>, c32); + decl_pmc_to_pmt_numeric_array(std::complex<double>, c64); + + //dictionary container + if (p.is<PMCDict>()) + { + const PMCDict &m = p.as<PMCDict>(); + pmt_t d = pmt_make_dict(); + BOOST_FOREACH(const PMCPair &pr, m) + { + d = pmt_dict_add(d, pmc_to_pmt(pr.first), pmc_to_pmt(pr.second)); + } + return d; + } + + //set container + if (p.is<PMCSet>()) + { + const PMCSet &s = p.as<PMCSet>(); + pmt_t l = PMT_NIL; + BOOST_FOREACH(const PMCC &elem, s) + { + l = pmt_list_add(l, pmc_to_pmt(elem)); + } + return l; + } + + //is it already a pmt? + if (p.is<pmt_t>()) return p.as<pmt_t>(); + + //backup plan... boost::any + return pmt_make_any(p); + +} + +inline PMCC pmt_to_pmc(const pmt_t &p) +{ + //if the container null? + if (not p) return PMC(); + + #define decl_pmt_to_pmc(check, conv) if (check(p)) return PMC_M(conv(p)) + + //bool + decl_pmt_to_pmc(pmt_is_bool, pmt_to_bool); + + //string (do object interning for strings) + decl_pmt_to_pmc(pmt_is_symbol, pmt_symbol_to_string).intern(); + + //numeric types + decl_pmt_to_pmc(pmt_is_integer, pmt_to_long); + decl_pmt_to_pmc(pmt_is_uint64, pmt_to_uint64); + decl_pmt_to_pmc(pmt_is_real, pmt_to_double); + decl_pmt_to_pmc(pmt_is_complex, pmt_to_complex); + + //is it a boost any holding a PMCC? + if (pmt_is_any(p)) + { + const boost::any a = pmt_any_ref(p); + if (a.type() == typeid(PMCC)) return boost::any_cast<PMCC>(a); + } + + //pair container + if (pmt_is_pair(p)) + { + PMCPair pr(pmt_to_pmc(pmt_car(p)), pmt_to_pmc(pmt_cdr(p))); + return PMC_M(pr); + } + + //fucking tuples + #define decl_pmt_to_pmc_tuple(n) \ + if (pmt_is_tuple(p) and pmt_length(p) == n) \ + { \ + PMCTuple<n> t; \ + for (size_t i = 0; i < n; i++) t[i] = pmt_to_pmc(pmt_tuple_ref(p, i)); \ + return PMC_M(t); \ + } + decl_pmt_to_pmc_tuple(0); + decl_pmt_to_pmc_tuple(1); + decl_pmt_to_pmc_tuple(2); + decl_pmt_to_pmc_tuple(3); + decl_pmt_to_pmc_tuple(4); + decl_pmt_to_pmc_tuple(5); + decl_pmt_to_pmc_tuple(6); + decl_pmt_to_pmc_tuple(7); + decl_pmt_to_pmc_tuple(8); + decl_pmt_to_pmc_tuple(9); + decl_pmt_to_pmc_tuple(10); + + //vector container + if (pmt_is_vector(p)) + { + PMCList l(pmt_length(p)); + for (size_t i = 0; i < l.size(); i++) + { + l[i] = pmt_to_pmc(pmt_vector_ref(p, i)); + } + return PMC_M(l); + } + + //numeric arrays + #define decl_pmt_to_pmc_numeric_array(type, suffix) \ + if (pmt_is_ ## suffix ## vector(p)) \ + { \ + size_t n; const type* i = pmt_ ## suffix ## vector_elements(p, n); \ + return PMC_M(std::vector<type>(i, i+n)); \ + } + decl_pmt_to_pmc_numeric_array(uint8_t, u8); + decl_pmt_to_pmc_numeric_array(uint16_t, u16); + decl_pmt_to_pmc_numeric_array(uint32_t, u32); + decl_pmt_to_pmc_numeric_array(uint64_t, u64); + decl_pmt_to_pmc_numeric_array(int8_t, s8); + decl_pmt_to_pmc_numeric_array(int16_t, s16); + decl_pmt_to_pmc_numeric_array(int32_t, s32); + decl_pmt_to_pmc_numeric_array(int64_t, s64); + decl_pmt_to_pmc_numeric_array(float, f32); + decl_pmt_to_pmc_numeric_array(double, f64); + decl_pmt_to_pmc_numeric_array(std::complex<float>, c32); + decl_pmt_to_pmc_numeric_array(std::complex<double>, c64); + + //dictionary container + if (pmt_is_dict(p)) + { + PMCDict m; + pmt_t items = pmt_dict_items(p); + for (size_t i = 0; i < pmt_length(items); i++) + { + pmt_t item = pmt_nth(i, items); + PMCC key = pmt_to_pmc(pmt_car(item)); + PMCC val = pmt_to_pmc(pmt_cdr(item)); + m[key] = val; + } + return PMC_M(m); + } + + //set container + //FIXME no pmt_is_list... + + //backup plan... store the pmt + return PMC_M(p); +} + +} + +#endif /*INCLUDED_LIBGRAS_PMX_HELPER_HPP*/ diff --git a/gnuradio-core/src/lib/runtime/qa_block_tags.cc b/gnuradio-core/src/lib/runtime/qa_block_tags.cc new file mode 100644 index 000000000..d6b1065e3 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_block_tags.cc @@ -0,0 +1,450 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <qa_block_tags.h> +#include <gr_block.h> +#include <gr_top_block.h> +#include <gr_null_source.h> +#include <gr_null_sink.h> +#include <gr_head.h> +#include <gr_annotator_alltoall.h> +#include <gr_annotator_1to1.h> +#include <gr_keep_one_in_n.h> +#include <gr_firdes.h> +#include <gr_tags.h> + + +// ---------------------------------------------------------------- + +using namespace pmt; + +// set to 1 to turn on debug output +// The debug output fully checks that the tags seen are what are expected. While +// this behavior currently works with our implementation, there is no guarentee +// that the tags will be coming in this specific order, so it's dangerous to +// rely on this as a test of the tag system working. We would really want to +// tags we know we should see and then test that they all occur once, but in no +// particular order. +#define QA_TAGS_DEBUG 0 + +void +qa_block_tags::t0 () +{ + unsigned int N = 1000; + gr_top_block_sptr tb = gr_make_top_block("top"); + gr_block_sptr src (gr_make_null_source(sizeof(int))); + gr_block_sptr head (gr_make_head(sizeof(int), N)); + gr_block_sptr snk (gr_make_null_sink(sizeof(int))); + + tb->connect(src, 0, head, 0); + tb->connect(head, 0, snk, 0); + + //CPPUNIT_ASSERT_THROW(src->nitems_read(0), std::runtime_error); + //CPPUNIT_ASSERT_THROW(src->nitems_written(0), std::runtime_error); + CPPUNIT_ASSERT_EQUAL(src->nitems_read(0), (uint64_t)0); + CPPUNIT_ASSERT_EQUAL(src->nitems_written(0), (uint64_t)0); + + tb->run(); + + CPPUNIT_ASSERT_THROW(src->nitems_read(0), std::invalid_argument); + CPPUNIT_ASSERT(src->nitems_written(0) >= N); + CPPUNIT_ASSERT_EQUAL(snk->nitems_read(0), (uint64_t)1000); + CPPUNIT_ASSERT_THROW(snk->nitems_written(0), std::invalid_argument); +} + + +void +qa_block_tags::t1 () +{ + int N = 40000; + gr_top_block_sptr tb = gr_make_top_block("top"); + gr_block_sptr src (gr_make_null_source(sizeof(int))); + gr_block_sptr head (gr_make_head(sizeof(int), N)); + gr_annotator_alltoall_sptr ann0 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann1 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann2 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann3 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann4 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_block_sptr snk0 (gr_make_null_sink(sizeof(int))); + gr_block_sptr snk1 (gr_make_null_sink(sizeof(int))); + + tb->connect(src, 0, head, 0); + tb->connect(head, 0, ann0, 0); + + tb->connect(ann0, 0, ann1, 0); + tb->connect(ann0, 1, ann2, 0); + tb->connect(ann1, 0, ann3, 0); + tb->connect(ann2, 0, ann4, 0); + + tb->connect(ann3, 0, snk0, 0); + tb->connect(ann4, 0, snk1, 0); + + tb->run(); + + std::vector<gr_tag_t> tags0 = ann0->data(); + std::vector<gr_tag_t> tags3 = ann3->data(); + std::vector<gr_tag_t> tags4 = ann4->data(); + + // The first annotator does not receive any tags from the null sink upstream + CPPUNIT_ASSERT_EQUAL(tags0.size(), (size_t)0); + CPPUNIT_ASSERT_EQUAL(tags3.size(), (size_t)8); + CPPUNIT_ASSERT_EQUAL(tags4.size(), (size_t)8); + +#if QA_TAGS_DEBUG + // Kludge together the tags that we know should result from the above graph + std::stringstream str0, str1, str2; + str0 << ann0->name() << ann0->unique_id(); + str1 << ann1->name() << ann1->unique_id(); + str2 << ann2->name() << ann2->unique_id(); + + pmt_t expected_tags3[8]; + expected_tags3[0] = mp(pmt_from_uint64(0), mp(str1.str()), mp("seq"), mp(0)); + expected_tags3[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(0)); + expected_tags3[2] = mp(pmt_from_uint64(10000), mp(str1.str()), mp("seq"), mp(1)); + expected_tags3[3] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(2)); + expected_tags3[4] = mp(pmt_from_uint64(20000), mp(str1.str()), mp("seq"), mp(2)); + expected_tags3[5] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(4)); + expected_tags3[6] = mp(pmt_from_uint64(30000), mp(str1.str()), mp("seq"), mp(3)); + expected_tags3[7] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(6)); + + pmt_t expected_tags4[8]; + expected_tags4[0] = mp(pmt_from_uint64(0), mp(str2.str()), mp("seq"), mp(0)); + expected_tags4[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(1)); + expected_tags4[2] = mp(pmt_from_uint64(10000), mp(str2.str()), mp("seq"), mp(1)); + expected_tags4[3] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(3)); + expected_tags4[4] = mp(pmt_from_uint64(20000), mp(str2.str()), mp("seq"), mp(2)); + expected_tags4[5] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(5)); + expected_tags4[6] = mp(pmt_from_uint64(30000), mp(str2.str()), mp("seq"), mp(3)); + expected_tags4[7] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(7)); + + std::cout << std::endl << "qa_block_tags::t1" << std::endl; + + // For annotator 3, we know it gets tags from ann0 and ann1, test this + for(size_t i = 0; i < tags3.size(); i++) { + std::cout << "tags3[" << i << "] = " << tags3[i] << "\t\t" << expected_tags3[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags3[i]), pmt_write_string(expected_tags3[i])); + } + + // For annotator 4, we know it gets tags from ann0 and ann2, test this + std::cout << std::endl; + for(size_t i = 0; i < tags4.size(); i++) { + std::cout << "tags4[" << i << "] = " << tags4[i] << "\t\t" << expected_tags4[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags4[i]), pmt_write_string(expected_tags4[i])); + } +#endif +} + +void +qa_block_tags::t2 () +{ + int N = 40000; + gr_top_block_sptr tb = gr_make_top_block("top"); + gr_block_sptr src (gr_make_null_source(sizeof(int))); + gr_block_sptr head (gr_make_head(sizeof(int), N)); + gr_annotator_alltoall_sptr ann0 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann1 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann2 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann3 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann4 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_block_sptr snk0 (gr_make_null_sink(sizeof(int))); + gr_block_sptr snk1 (gr_make_null_sink(sizeof(int))); + gr_block_sptr snk2 (gr_make_null_sink(sizeof(int))); + + tb->connect(src, 0, head, 0); + tb->connect(head, 0, ann0, 0); + + tb->connect(ann0, 0, ann1, 0); + tb->connect(ann0, 1, ann1, 1); + tb->connect(ann1, 0, ann2, 0); + tb->connect(ann1, 1, ann3, 0); + tb->connect(ann1, 2, ann4, 0); + + tb->connect(ann2, 0, snk0, 0); + tb->connect(ann3, 0, snk1, 0); + tb->connect(ann4, 0, snk2, 0); + + tb->run(); + + std::vector<gr_tag_t> tags0 = ann0->data(); + std::vector<gr_tag_t> tags1 = ann1->data(); + std::vector<gr_tag_t> tags2 = ann2->data(); + std::vector<gr_tag_t> tags3 = ann4->data(); + std::vector<gr_tag_t> tags4 = ann4->data(); + + // The first annotator does not receive any tags from the null sink upstream + CPPUNIT_ASSERT_EQUAL(tags0.size(), (size_t)0); + CPPUNIT_ASSERT_EQUAL(tags1.size(), (size_t)8); + + // Make sure the rest all have 12 tags + CPPUNIT_ASSERT_EQUAL(tags2.size(), (size_t)12); + CPPUNIT_ASSERT_EQUAL(tags3.size(), (size_t)12); + CPPUNIT_ASSERT_EQUAL(tags4.size(), (size_t)12); + + +#if QA_TAGS_DEBUG + // Kludge together the tags that we know should result from the above graph + std::stringstream str0, str1; + str0 << ann0->name() << ann0->unique_id(); + str1 << ann1->name() << ann1->unique_id(); + + pmt_t expected_tags2[12]; + expected_tags2[0] = mp(pmt_from_uint64(0), mp(str1.str()), mp("seq"), mp(0)); + expected_tags2[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(0)); + expected_tags2[2] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(1)); + expected_tags2[3] = mp(pmt_from_uint64(10000), mp(str1.str()), mp("seq"), mp(3)); + expected_tags2[4] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(2)); + expected_tags2[5] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(3)); + expected_tags2[6] = mp(pmt_from_uint64(20000), mp(str1.str()), mp("seq"), mp(6)); + expected_tags2[7] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(4)); + expected_tags2[8] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(5)); + expected_tags2[9] = mp(pmt_from_uint64(30000), mp(str1.str()), mp("seq"), mp(9)); + expected_tags2[10] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(6)); + expected_tags2[11] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(7)); + + pmt_t expected_tags4[12]; + expected_tags4[0] = mp(pmt_from_uint64(0), mp(str1.str()), mp("seq"), mp(2)); + expected_tags4[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(0)); + expected_tags4[2] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(1)); + expected_tags4[3] = mp(pmt_from_uint64(10000), mp(str1.str()), mp("seq"), mp(5)); + expected_tags4[4] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(2)); + expected_tags4[5] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(3)); + expected_tags4[6] = mp(pmt_from_uint64(20000), mp(str1.str()), mp("seq"), mp(8)); + expected_tags4[7] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(4)); + expected_tags4[8] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(5)); + expected_tags4[9] = mp(pmt_from_uint64(30000), mp(str1.str()), mp("seq"), mp(11)); + expected_tags4[10] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(6)); + expected_tags4[11] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(7)); + + std::cout << std::endl << "qa_block_tags::t2" << std::endl; + + // For annotator[2-4], we know it gets tags from ann0 and ann1 + // but the tags from the different outputs of ann1 are different for each. + // Just testing ann2 and ann4; if they are correct it would be + // inconceivable for ann3 to have it wrong. + for(size_t i = 0; i < tags2.size(); i++) { + std::cout << "tags2[" << i << "] = " << tags2[i] << "\t\t" << expected_tags2[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags2[i]), pmt_write_string(expected_tags2[i])); + } + + std::cout << std::endl; + for(size_t i = 0; i < tags4.size(); i++) { + std::cout << "tags2[" << i << "] = " << tags4[i] << "\t\t" << expected_tags4[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags4[i]), pmt_write_string(expected_tags4[i])); + } +#endif +} + + +void +qa_block_tags::t3 () +{ + int N = 40000; + gr_top_block_sptr tb = gr_make_top_block("top"); + gr_block_sptr src (gr_make_null_source(sizeof(int))); + gr_block_sptr head (gr_make_head(sizeof(int), N)); + gr_annotator_1to1_sptr ann0 (gr_make_annotator_1to1(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann1 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann2 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_1to1_sptr ann3 (gr_make_annotator_1to1(10000, sizeof(int))); + gr_annotator_1to1_sptr ann4 (gr_make_annotator_1to1(10000, sizeof(int))); + gr_block_sptr snk0 (gr_make_null_sink(sizeof(int))); + gr_block_sptr snk1 (gr_make_null_sink(sizeof(int))); + + tb->connect(src, 0, head, 0); + tb->connect(head, 0, ann0, 0); + tb->connect(head, 0, ann0, 1); + + tb->connect(ann0, 0, ann1, 0); + tb->connect(ann0, 1, ann2, 0); + tb->connect(ann1, 0, ann3, 0); + tb->connect(ann2, 0, ann4, 0); + + tb->connect(ann3, 0, snk0, 0); + tb->connect(ann4, 0, snk1, 0); + + tb->run(); + + + std::vector<gr_tag_t> tags0 = ann0->data(); + std::vector<gr_tag_t> tags3 = ann3->data(); + std::vector<gr_tag_t> tags4 = ann4->data(); + + // The first annotator does not receive any tags from the null sink upstream + CPPUNIT_ASSERT_EQUAL(tags0.size(), (size_t)0); + CPPUNIT_ASSERT_EQUAL(tags3.size(), (size_t)8); + CPPUNIT_ASSERT_EQUAL(tags4.size(), (size_t)8); + +#if QA_TAGS_DEBUG + // Kludge together the tags that we know should result from the above graph + std::stringstream str0, str1, str2; + str0 << ann0->name() << ann0->unique_id(); + str1 << ann1->name() << ann1->unique_id(); + str2 << ann2->name() << ann2->unique_id(); + + pmt_t expected_tags3[8]; + expected_tags3[0] = mp(pmt_from_uint64(0), mp(str1.str()), mp("seq"), mp(0)); + expected_tags3[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(0)); + expected_tags3[2] = mp(pmt_from_uint64(10000), mp(str1.str()), mp("seq"), mp(1)); + expected_tags3[3] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(2)); + expected_tags3[4] = mp(pmt_from_uint64(20000), mp(str1.str()), mp("seq"), mp(2)); + expected_tags3[5] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(4)); + expected_tags3[6] = mp(pmt_from_uint64(30000), mp(str1.str()), mp("seq"), mp(3)); + expected_tags3[7] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(6)); + + pmt_t expected_tags4[8]; + expected_tags4[0] = mp(pmt_from_uint64(0), mp(str2.str()), mp("seq"), mp(0)); + expected_tags4[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(1)); + expected_tags4[2] = mp(pmt_from_uint64(10000), mp(str2.str()), mp("seq"), mp(1)); + expected_tags4[3] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(3)); + expected_tags4[4] = mp(pmt_from_uint64(20000), mp(str2.str()), mp("seq"), mp(2)); + expected_tags4[5] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(5)); + expected_tags4[6] = mp(pmt_from_uint64(30000), mp(str2.str()), mp("seq"), mp(3)); + expected_tags4[7] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(7)); + + std::cout << std::endl << "qa_block_tags::t3" << std::endl; + + // For annotator 3, we know it gets tags from ann0 and ann1, test this + for(size_t i = 0; i < tags3.size(); i++) { + std::cout << "tags3[" << i << "] = " << tags3[i] << "\t\t" << expected_tags3[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags3[i]), pmt_write_string(expected_tags3[i])); + } + + // For annotator 4, we know it gets tags from ann0 and ann2, test this + std::cout << std::endl; + for(size_t i = 0; i < tags4.size(); i++) { + std::cout << "tags4[" << i << "] = " << tags4[i] << "\t\t" << expected_tags4[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags4[i]), pmt_write_string(expected_tags4[i])); + } +#endif +} + + +void +qa_block_tags::t4 () +{ + int N = 40000; + gr_top_block_sptr tb = gr_make_top_block("top"); + gr_block_sptr src (gr_make_null_source(sizeof(int))); + gr_block_sptr head (gr_make_head(sizeof(int), N)); + gr_annotator_1to1_sptr ann0 (gr_make_annotator_1to1(10000, sizeof(int))); + gr_annotator_1to1_sptr ann1 (gr_make_annotator_1to1(10000, sizeof(int))); + gr_annotator_1to1_sptr ann2 (gr_make_annotator_1to1(10000, sizeof(int))); + gr_block_sptr snk0 (gr_make_null_sink(sizeof(int))); + gr_block_sptr snk1 (gr_make_null_sink(sizeof(int))); + + // using 1-to-1 tag propagation without having equal number of + // ins and outs. Make sure this works; will just exit run early. + tb->connect(src, 0, head, 0); + tb->connect(head, 0, ann0, 0); + tb->connect(ann0, 0, ann1, 0); + tb->connect(ann0, 1, ann2, 0); + tb->connect(ann1, 0, snk0, 0); + tb->connect(ann2, 0, snk1, 0); + + std::cerr << std::endl + << "NOTE: This is supposed to produce an error from gr_block_executor" + << std::endl; + tb->run(); +} + + +void +qa_block_tags::t5 () +{ + int N = 40000; + gr_top_block_sptr tb = gr_make_top_block("top"); + gr_block_sptr src (gr_make_null_source(sizeof(float))); + gr_block_sptr head (gr_make_head(sizeof(float), N)); + gr_annotator_alltoall_sptr ann0 (gr_make_annotator_alltoall(10000, sizeof(float))); + gr_annotator_alltoall_sptr ann1 (gr_make_annotator_alltoall(10000, sizeof(float))); + gr_annotator_alltoall_sptr ann2 (gr_make_annotator_alltoall(1000, sizeof(float))); + gr_block_sptr snk0 (gr_make_null_sink(sizeof(float))); + + // Rate change blocks + gr_keep_one_in_n_sptr dec10 (gr_make_keep_one_in_n(sizeof(float), 10)); + + tb->connect(src, 0, head, 0); + tb->connect(head, 0, ann0, 0); + tb->connect(ann0, 0, ann1, 0); + tb->connect(ann1, 0, dec10, 0); + tb->connect(dec10, 0, ann2, 0); + tb->connect(ann2, 0, snk0, 0); + + tb->run(); + + std::vector<gr_tag_t> tags0 = ann0->data(); + std::vector<gr_tag_t> tags1 = ann1->data(); + std::vector<gr_tag_t> tags2 = ann2->data(); + + // The first annotator does not receive any tags from the null sink upstream + CPPUNIT_ASSERT_EQUAL(tags0.size(), (size_t)0); + CPPUNIT_ASSERT_EQUAL(tags1.size(), (size_t)4); + CPPUNIT_ASSERT_EQUAL(tags2.size(), (size_t)8); + + +#if QA_TAGS_DEBUG + // Kludge together the tags that we know should result from the above graph + std::stringstream str0, str1, str2; + str0 << ann0->name() << ann0->unique_id(); + str1 << ann1->name() << ann1->unique_id(); + str2 << ann2->name() << ann2->unique_id(); + + pmt_t expected_tags1[5]; + expected_tags1[0] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(0)); + expected_tags1[1] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(1)); + expected_tags1[2] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(2)); + expected_tags1[3] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(3)); + + pmt_t expected_tags2[10]; + expected_tags2[0] = mp(pmt_from_uint64(0), mp(str1.str()), mp("seq"), mp(0)); + expected_tags2[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(0)); + expected_tags2[2] = mp(pmt_from_uint64(1000), mp(str1.str()), mp("seq"), mp(1)); + expected_tags2[3] = mp(pmt_from_uint64(1000), mp(str0.str()), mp("seq"), mp(1)); + expected_tags2[4] = mp(pmt_from_uint64(2000), mp(str1.str()), mp("seq"), mp(2)); + expected_tags2[5] = mp(pmt_from_uint64(2000), mp(str0.str()), mp("seq"), mp(2)); + expected_tags2[6] = mp(pmt_from_uint64(3000), mp(str1.str()), mp("seq"), mp(3)); + expected_tags2[7] = mp(pmt_from_uint64(3000), mp(str0.str()), mp("seq"), mp(3)); + expected_tags2[8] = mp(pmt_from_uint64(4000), mp(str1.str()), mp("seq"), mp(4)); + expected_tags2[9] = mp(pmt_from_uint64(4000), mp(str0.str()), mp("seq"), mp(4)); + + std::cout << std::endl << "qa_block_tags::t5" << std::endl; + + // annotator 1 gets tags from annotator 0 + std::cout << "tags1.size(): " << tags1.size() << std::endl; + for(size_t i = 0; i < tags1.size(); i++) { + std::cout << "tags1[" << i << "] = " << tags1[i] << "\t\t" << expected_tags1[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags1[i]), pmt_write_string(expected_tags1[i])); + } + + // annotator 2 gets tags from annotators 0 and 1 + std::cout << std::endl; + std::cout << "tags2.size(): " << tags2.size() << std::endl; + for(size_t i = 0; i < tags2.size(); i++) { + std::cout << "tags2[" << i << "] = " << tags2[i] << "\t\t" << expected_tags2[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags2[i]), pmt_write_string(expected_tags2[i])); + } +#endif +} + diff --git a/gnuradio-core/src/lib/runtime/qa_block_tags.h b/gnuradio-core/src/lib/runtime/qa_block_tags.h new file mode 100644 index 000000000..6b7e5975d --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_block_tags.h @@ -0,0 +1,52 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QA_BLOCK_TAGS_H +#define INCLUDED_QA_BLOCK_TAGS_H + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> +#include <stdexcept> + +class qa_block_tags : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE (qa_block_tags); + CPPUNIT_TEST (t0); + CPPUNIT_TEST (t1); + CPPUNIT_TEST (t2); + CPPUNIT_TEST (t3); + CPPUNIT_TEST (t4); + CPPUNIT_TEST (t5); + CPPUNIT_TEST_SUITE_END (); + + private: + void t0 (); + void t1 (); + void t2 (); + void t3 (); + void t4 (); + void t5 (); + +}; + + +#endif /* INCLUDED_QA_BLOCK_TAGS_H */ diff --git a/gnuradio-core/src/lib/runtime/qa_gr_block.cc b/gnuradio-core/src/lib/runtime/qa_gr_block.cc new file mode 100644 index 000000000..aeab5b74a --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_block.cc @@ -0,0 +1,88 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <qa_gr_block.h> +#include <gr_block.h> +#include <gr_io_signature.h> +#include <gr_null_sink.h> +#include <gr_null_source.h> + + +// ---------------------------------------------------------------- + + +void +qa_gr_block::t0 () +{ + // test creation of sources + gr_block_sptr src1 (gr_make_null_source (sizeof (int))); + CPPUNIT_ASSERT_EQUAL (std::string ("null_source"), src1->name ()); + CPPUNIT_ASSERT_EQUAL (0, src1->input_signature()->max_streams ()); + CPPUNIT_ASSERT_EQUAL (1, src1->output_signature()->min_streams ()); + CPPUNIT_ASSERT_EQUAL (1, src1->output_signature()->max_streams ()); + CPPUNIT_ASSERT_EQUAL ((int) sizeof(int), + src1->output_signature()->sizeof_stream_item (0)); + + gr_block_sptr src2 (gr_make_null_source (sizeof (short))); + CPPUNIT_ASSERT_EQUAL (std::string ("null_source"), src2->name ()); + CPPUNIT_ASSERT_EQUAL (0, src2->input_signature()->max_streams ()); + CPPUNIT_ASSERT_EQUAL (1, src2->output_signature()->min_streams ()); + CPPUNIT_ASSERT_EQUAL (1, src2->output_signature()->max_streams ()); + CPPUNIT_ASSERT_EQUAL ((int) sizeof (short), + src2->output_signature()->sizeof_stream_item (0)); +} + + +void +qa_gr_block::t1 () +{ + // test creation of sinks + gr_block_sptr dst1 (gr_make_null_sink (sizeof (int))); + CPPUNIT_ASSERT_EQUAL (std::string ("null_sink"), dst1->name ()); + CPPUNIT_ASSERT_EQUAL (1, dst1->input_signature()->min_streams ()); + CPPUNIT_ASSERT_EQUAL (1, dst1->input_signature()->max_streams ()); + CPPUNIT_ASSERT_EQUAL ((int) sizeof (int), + dst1->input_signature()->sizeof_stream_item (0)); + + CPPUNIT_ASSERT_EQUAL (0, dst1->output_signature()->max_streams ()); + + gr_block_sptr dst2 (gr_make_null_sink (sizeof (short))); + CPPUNIT_ASSERT_EQUAL (std::string ("null_sink"), dst2->name ()); + CPPUNIT_ASSERT_EQUAL (1, dst2->input_signature()->min_streams ()); + CPPUNIT_ASSERT_EQUAL (1, dst2->input_signature()->max_streams ()); + CPPUNIT_ASSERT_EQUAL ((int) sizeof (short), + dst2->input_signature()->sizeof_stream_item (0)); + CPPUNIT_ASSERT_EQUAL (0, dst2->output_signature()->max_streams ()); +} + +void +qa_gr_block::t2 () +{ +} + +void +qa_gr_block::t3 () +{ +} diff --git a/gnuradio-core/src/lib/runtime/qa_gr_block.h b/gnuradio-core/src/lib/runtime/qa_gr_block.h new file mode 100644 index 000000000..14c7c40d1 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_block.h @@ -0,0 +1,48 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QA_GR_BLOCK_H +#define INCLUDED_QA_GR_BLOCK_H + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> +#include <stdexcept> + +class qa_gr_block : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE (qa_gr_block); + CPPUNIT_TEST (t0); + CPPUNIT_TEST (t1); + CPPUNIT_TEST (t2); + CPPUNIT_TEST (t3); + CPPUNIT_TEST_SUITE_END (); + + private: + void t0 (); + void t1 (); + void t2 (); + void t3 (); + +}; + + +#endif /* INCLUDED_QA_GR_BLOCK_H */ diff --git a/gnuradio-core/src/lib/runtime/qa_gr_buffer.cc b/gnuradio-core/src/lib/runtime/qa_gr_buffer.cc new file mode 100644 index 000000000..c74baf398 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_buffer.cc @@ -0,0 +1,307 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <qa_gr_buffer.h> +#include <gr_buffer.h> +#include <cppunit/TestAssert.h> +#include <stdlib.h> +#include <gr_random.h> + +static void +leak_check (void f ()) +{ + long buffer_count = gr_buffer_ncurrently_allocated (); + long buffer_reader_count = gr_buffer_reader_ncurrently_allocated (); + + f (); + + CPPUNIT_ASSERT_EQUAL (buffer_reader_count, gr_buffer_reader_ncurrently_allocated ()); + CPPUNIT_ASSERT_EQUAL (buffer_count, gr_buffer_ncurrently_allocated ()); +} + + +// ---------------------------------------------------------------------------- +// test single writer, no readers... +// + +static void +t0_body () +{ + int nitems = 4000 / sizeof (int); + int counter = 0; + + gr_buffer_sptr buf(gr_make_buffer(nitems, sizeof (int), gr_block_sptr())); + + int last_sa; + int sa; + + sa = buf->space_available (); + CPPUNIT_ASSERT (sa > 0); + last_sa = sa; + + for (int i = 0; i < 5; i++){ + sa = buf->space_available (); + CPPUNIT_ASSERT_EQUAL (last_sa, sa); + last_sa = sa; + + int *p = (int *) buf->write_pointer (); + CPPUNIT_ASSERT (p != 0); + + for (int j = 0; j < sa; j++) + *p++ = counter++; + + buf->update_write_pointer (sa); + } +} + +// ---------------------------------------------------------------------------- +// test single writer, single reader +// + +static void +t1_body () + { + int nitems = 4000 / sizeof (int); + int write_counter = 0; + int read_counter = 0; + + gr_buffer_sptr buf(gr_make_buffer(nitems, sizeof (int), gr_block_sptr())); + gr_buffer_reader_sptr r1 (gr_buffer_add_reader (buf, 0, gr_block_sptr())); + + + int sa; + + // write 1/3 of buffer + + sa = buf->space_available (); + CPPUNIT_ASSERT (sa > 0); + + int *p = (int *) buf->write_pointer (); + CPPUNIT_ASSERT (p != 0); + + for (int j = 0; j < sa/3; j++){ + *p++ = write_counter++; + } + buf->update_write_pointer (sa/3); + + + // write the next 1/3 (1/2 of what's left) + + sa = buf->space_available (); + CPPUNIT_ASSERT (sa > 0); + + p = (int *) buf->write_pointer (); + CPPUNIT_ASSERT (p != 0); + + for (int j = 0; j < sa/2; j++){ + *p++ = write_counter++; + } + buf->update_write_pointer (sa/2); + + + // check that we can read it OK + + int ia = r1->items_available (); + CPPUNIT_ASSERT_EQUAL (write_counter, ia); + + int *rp = (int *) r1->read_pointer (); + CPPUNIT_ASSERT (rp != 0); + + for (int i = 0; i < ia/2; i++){ + CPPUNIT_ASSERT_EQUAL (read_counter, *rp); + read_counter++; + rp++; + } + r1->update_read_pointer (ia/2); + + // read the rest + + ia = r1->items_available (); + rp = (int *) r1->read_pointer (); + CPPUNIT_ASSERT (rp != 0); + + for (int i = 0; i < ia; i++){ + CPPUNIT_ASSERT_EQUAL (read_counter, *rp); + read_counter++; + rp++; + } + r1->update_read_pointer (ia); +} + +// ---------------------------------------------------------------------------- +// single writer, single reader: check wrap-around +// + +static void +t2_body () +{ + // 64K is the largest granularity we've seen so far (MS windows file mapping). + // This allows a bit of "white box testing" + + int nitems = (64 * (1L << 10)) / sizeof (int); // 64K worth of ints + + gr_buffer_sptr buf(gr_make_buffer (nitems, sizeof (int), gr_block_sptr())); + gr_buffer_reader_sptr r1 (gr_buffer_add_reader (buf, 0, gr_block_sptr())); + + int read_counter = 0; + int write_counter = 0; + int n; + int *wp = 0; + int *rp = 0; + + // Write 3/4 of buffer + + n = (int) (buf->space_available () * 0.75); + wp = (int *) buf->write_pointer (); + + for (int i = 0; i < n; i++) + *wp++ = write_counter++; + buf->update_write_pointer (n); + + // Now read it all + + int m = r1->items_available (); + CPPUNIT_ASSERT_EQUAL (n, m); + rp = (int *) r1->read_pointer (); + + for (int i = 0; i < m; i++){ + CPPUNIT_ASSERT_EQUAL (read_counter, *rp); + read_counter++; + rp++; + } + r1->update_read_pointer (m); + + // Now write as much as we can. + // This will wrap around the buffer + + n = buf->space_available (); + CPPUNIT_ASSERT_EQUAL (nitems - 1, n); // white box test + wp = (int *) buf->write_pointer (); + + for (int i = 0; i < n; i++) + *wp++ = write_counter++; + buf->update_write_pointer (n); + + // now read it all + + m = r1->items_available (); + CPPUNIT_ASSERT_EQUAL (n, m); + rp = (int *) r1->read_pointer (); + + for (int i = 0; i < m; i++){ + CPPUNIT_ASSERT_EQUAL (read_counter, *rp); + read_counter++; + rp++; + } + r1->update_read_pointer (m); + +} + +// ---------------------------------------------------------------------------- +// single writer, N readers, randomized order and lengths +// ---------------------------------------------------------------------------- + +static void +t3_body () +{ + int nitems = (64 * (1L << 10)) / sizeof (int); + + static const int N = 5; + gr_buffer_sptr buf(gr_make_buffer(nitems, sizeof (int), gr_block_sptr())); + gr_buffer_reader_sptr reader[N]; + int read_counter[N]; + int write_counter = 0; + gr_random random; + + for (int i = 0; i < N; i++){ + read_counter[i] = 0; + reader[i] = gr_buffer_add_reader (buf, 0, gr_block_sptr()); + } + + for (int lc = 0; lc < 1000; lc++){ + + // write some + + int n = (int) (buf->space_available () * random.ran1 ()); + int *wp = (int *) buf->write_pointer (); + + for (int i = 0; i < n; i++) + *wp++ = write_counter++; + + buf->update_write_pointer (n); + + // pick a random reader and read some + + int r = (int) (N * random.ran1 ()); + CPPUNIT_ASSERT (0 <= r && r < N); + + int m = reader[r]->items_available (); + int *rp = (int *) reader[r]->read_pointer (); + + for (int i = 0; i < m; i++){ + CPPUNIT_ASSERT_EQUAL (read_counter[r], *rp); + read_counter[r]++; + rp++; + } + reader[r]->update_read_pointer (m); + } +} + + +// ---------------------------------------------------------------------------- + +void +qa_gr_buffer::t0 () +{ + leak_check (t0_body); +} + +void +qa_gr_buffer::t1 () +{ + leak_check (t1_body); +} + +void +qa_gr_buffer::t2 () +{ + leak_check (t2_body); +} + +void +qa_gr_buffer::t3 () +{ + leak_check (t3_body); +} + +void +qa_gr_buffer::t4 () +{ +} + +void +qa_gr_buffer::t5 () +{ +} diff --git a/gnuradio-core/src/lib/runtime/qa_gr_buffer.h b/gnuradio-core/src/lib/runtime/qa_gr_buffer.h new file mode 100644 index 000000000..2937c24b6 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_buffer.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QA_GR_BUFFER_H +#define INCLUDED_QA_GR_BUFFER_H + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> + +class qa_gr_buffer : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE (qa_gr_buffer); + CPPUNIT_TEST (t0); + CPPUNIT_TEST (t1); + CPPUNIT_TEST (t2); + CPPUNIT_TEST (t3); + CPPUNIT_TEST (t4); + CPPUNIT_TEST (t5); + CPPUNIT_TEST_SUITE_END (); + + + private: + + void t0 (); + void t1 (); + void t2 (); + void t3 (); + void t4 (); + void t5 (); +}; + + + +#endif /* INCLUDED_QA_GR_BUFFER_H */ diff --git a/gnuradio-core/src/lib/runtime/qa_gr_flowgraph.cc b/gnuradio-core/src/lib/runtime/qa_gr_flowgraph.cc new file mode 100644 index 000000000..cce83cb0a --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_flowgraph.cc @@ -0,0 +1,245 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qa_gr_flowgraph.h> +#include <gr_flowgraph.h> +#include <gr_nop.h> +#include <gr_null_source.h> +#include <gr_null_sink.h> + +void qa_gr_flowgraph::t0() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + CPPUNIT_ASSERT(fg); +} + +void qa_gr_flowgraph::t1_connect() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr nop1 = gr_make_nop(sizeof(int)); + gr_block_sptr nop2 = gr_make_nop(sizeof(int)); + + fg->connect(nop1, 0, nop2, 0); +} + +void qa_gr_flowgraph::t2_connect_invalid_src_port_neg() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr nop1 = gr_make_nop(sizeof(int)); + gr_block_sptr nop2 = gr_make_nop(sizeof(int)); + + CPPUNIT_ASSERT_THROW(fg->connect(nop1, -1, nop2, 0), std::invalid_argument); +} + +void qa_gr_flowgraph::t3_connect_src_port_exceeds() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + CPPUNIT_ASSERT_THROW(fg->connect(src, 1, dst, 0), std::invalid_argument); +} + +void qa_gr_flowgraph::t4_connect_invalid_dst_port_neg() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr nop1 = gr_make_nop(sizeof(int)); + gr_block_sptr nop2 = gr_make_nop(sizeof(int)); + + CPPUNIT_ASSERT_THROW(fg->connect(nop1, 0, nop2, -1), std::invalid_argument); +} + +void qa_gr_flowgraph::t5_connect_dst_port_exceeds() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + CPPUNIT_ASSERT_THROW(fg->connect(src, 0, dst, 1), std::invalid_argument); +} + +void qa_gr_flowgraph::t6_connect_dst_in_use() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr src1 = gr_make_null_source(sizeof(int)); + gr_block_sptr src2 = gr_make_null_source(sizeof(int)); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + fg->connect(src1, 0, dst, 0); + CPPUNIT_ASSERT_THROW(fg->connect(src2, 0, dst, 0), std::invalid_argument); +} + +void qa_gr_flowgraph::t7_connect_one_src_two_dst() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_block_sptr dst1 = gr_make_null_sink(sizeof(int)); + gr_block_sptr dst2 = gr_make_null_sink(sizeof(int)); + + fg->connect(src, 0, dst1, 0); + fg->connect(src, 0, dst2, 0); +} + +void qa_gr_flowgraph::t8_connect_type_mismatch() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr nop1 = gr_make_nop(sizeof(char)); + gr_block_sptr nop2 = gr_make_nop(sizeof(int)); + + CPPUNIT_ASSERT_THROW(fg->connect(nop1, 0, nop2, 0), std::invalid_argument); +} + +void qa_gr_flowgraph::t9_disconnect() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr nop1 = gr_make_nop(sizeof(int)); + gr_block_sptr nop2 = gr_make_nop(sizeof(int)); + + fg->connect(nop1, 0, nop2, 0); + fg->disconnect(nop1, 0, nop2, 0); +} + +void qa_gr_flowgraph::t10_disconnect_unconnected_block() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr nop1 = gr_make_nop(sizeof(int)); + gr_block_sptr nop2 = gr_make_nop(sizeof(int)); + gr_block_sptr nop3 = gr_make_nop(sizeof(int)); + + fg->connect(nop1, 0, nop2, 0); + CPPUNIT_ASSERT_THROW(fg->disconnect(nop1, 0, nop3, 0), std::invalid_argument); +} + +void qa_gr_flowgraph::t11_disconnect_unconnected_port() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr nop1 = gr_make_nop(sizeof(int)); + gr_block_sptr nop2 = gr_make_nop(sizeof(int)); + + fg->connect(nop1, 0, nop2, 0); + CPPUNIT_ASSERT_THROW(fg->disconnect(nop1, 0, nop2, 1), std::invalid_argument); +} + +void qa_gr_flowgraph::t12_validate() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr nop1 = gr_make_nop(sizeof(int)); + gr_block_sptr nop2 = gr_make_nop(sizeof(int)); + + fg->connect(nop1, 0, nop2, 0); + fg->validate(); +} + +void qa_gr_flowgraph::t13_validate_missing_input_assignment() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr nop1 = gr_make_nop(sizeof(int)); + gr_block_sptr nop2 = gr_make_nop(sizeof(int)); + + fg->connect(nop1, 0, nop2, 0); + fg->connect(nop1, 0, nop2, 2); + CPPUNIT_ASSERT_THROW(fg->validate(), std::runtime_error); +} + +void qa_gr_flowgraph::t14_validate_missing_output_assignment() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr nop1 = gr_make_nop(sizeof(int)); + gr_block_sptr nop2 = gr_make_nop(sizeof(int)); + + fg->connect(nop1, 0, nop2, 0); + fg->connect(nop1, 2, nop2, 1); + CPPUNIT_ASSERT_THROW(fg->validate(), std::runtime_error); +} + +void qa_gr_flowgraph::t15_clear() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr nop1 = gr_make_nop(sizeof(int)); + gr_block_sptr nop2 = gr_make_nop(sizeof(int)); + + fg->connect(nop1, 0, nop2, 0); + + CPPUNIT_ASSERT(fg->edges().size() == 1); + CPPUNIT_ASSERT(fg->calc_used_blocks().size() == 2); + + fg->clear(); + + CPPUNIT_ASSERT(fg->edges().size() == 0); + CPPUNIT_ASSERT(fg->calc_used_blocks().size() == 0); +} + +void qa_gr_flowgraph::t16_partition() +{ + gr_flowgraph_sptr fg = gr_make_flowgraph(); + + gr_block_sptr nop11 = gr_make_nop(sizeof(int)); + gr_block_sptr nop12 = gr_make_nop(sizeof(int)); + gr_block_sptr nop13 = gr_make_nop(sizeof(int)); + gr_block_sptr nop14 = gr_make_nop(sizeof(int)); + + gr_block_sptr nop21 = gr_make_nop(sizeof(int)); + gr_block_sptr nop22 = gr_make_nop(sizeof(int)); + gr_block_sptr nop23 = gr_make_nop(sizeof(int)); + + gr_block_sptr nop31 = gr_make_nop(sizeof(int)); + gr_block_sptr nop32 = gr_make_nop(sizeof(int)); + + // Build disjoint graph #1 + fg->connect(nop11, 0, nop12, 0); + fg->connect(nop12, 0, nop13, 0); + fg->connect(nop13, 0, nop14, 0); + + // Build disjoint graph #2 + fg->connect(nop21, 0, nop22, 0); + fg->connect(nop22, 0, nop23, 0); + + // Build disjoint graph #3 + fg->connect(nop31, 0, nop32, 0); + + std::vector<gr_basic_block_vector_t> graphs = fg->partition(); + + CPPUNIT_ASSERT(graphs.size() == 3); + CPPUNIT_ASSERT(graphs[0].size() == 4); + CPPUNIT_ASSERT(graphs[1].size() == 3); + CPPUNIT_ASSERT(graphs[2].size() == 2); +} diff --git a/gnuradio-core/src/lib/runtime/qa_gr_flowgraph.h b/gnuradio-core/src/lib/runtime/qa_gr_flowgraph.h new file mode 100644 index 000000000..2c2686f71 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_flowgraph.h @@ -0,0 +1,75 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QA_GR_FLOWGRAPH_H +#define INCLUDED_QA_GR_FLOWGRAPH_H + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> +#include <stdexcept> + +class qa_gr_flowgraph : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(qa_gr_flowgraph); + + CPPUNIT_TEST(t0); + CPPUNIT_TEST(t1_connect); + CPPUNIT_TEST(t2_connect_invalid_src_port_neg); + CPPUNIT_TEST(t3_connect_src_port_exceeds); + CPPUNIT_TEST(t4_connect_invalid_dst_port_neg); + CPPUNIT_TEST(t5_connect_dst_port_exceeds); + CPPUNIT_TEST(t6_connect_dst_in_use); + CPPUNIT_TEST(t7_connect_one_src_two_dst); + CPPUNIT_TEST(t8_connect_type_mismatch); + CPPUNIT_TEST(t9_disconnect); + CPPUNIT_TEST(t10_disconnect_unconnected_block); + CPPUNIT_TEST(t11_disconnect_unconnected_port); + CPPUNIT_TEST(t12_validate); + CPPUNIT_TEST(t13_validate_missing_input_assignment); + CPPUNIT_TEST(t14_validate_missing_output_assignment); + CPPUNIT_TEST(t15_clear); + CPPUNIT_TEST(t16_partition); + + CPPUNIT_TEST_SUITE_END(); + +private: + + void t0(); + void t1_connect(); + void t2_connect_invalid_src_port_neg(); + void t3_connect_src_port_exceeds(); + void t4_connect_invalid_dst_port_neg(); + void t5_connect_dst_port_exceeds(); + void t6_connect_dst_in_use(); + void t7_connect_one_src_two_dst(); + void t8_connect_type_mismatch(); + void t9_disconnect(); + void t10_disconnect_unconnected_block(); + void t11_disconnect_unconnected_port(); + void t12_validate(); + void t13_validate_missing_input_assignment(); + void t14_validate_missing_output_assignment(); + void t15_clear(); + void t16_partition(); +}; + +#endif /* INCLUDED_QA_GR_FLOWGRAPH_H */ diff --git a/gnuradio-core/src/lib/runtime/qa_gr_hier_block2.cc b/gnuradio-core/src/lib/runtime/qa_gr_hier_block2.cc new file mode 100644 index 000000000..9844d3381 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_hier_block2.cc @@ -0,0 +1,57 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2008,2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qa_gr_hier_block2.h> +#include <gr_hier_block2.h> +#include <gr_io_signature.h> +#include <gr_null_source.h> +#include <gr_null_sink.h> + +void qa_gr_hier_block2::test_make() +{ + gr_hier_block2_sptr src1(gr_make_hier_block2("test", + gr_make_io_signature(1, 1, 1 * sizeof(int)), + gr_make_io_signature(1, 1, 1 * sizeof(int)))); + + CPPUNIT_ASSERT(src1); + CPPUNIT_ASSERT_EQUAL(std::string("test"), src1->name()); + + CPPUNIT_ASSERT_EQUAL(1 * (int) sizeof(int), + src1->input_signature()->sizeof_stream_item(0)); + + CPPUNIT_ASSERT_EQUAL(1, src1->input_signature()->min_streams()); + CPPUNIT_ASSERT_EQUAL(1, src1->input_signature()->max_streams()); + + + CPPUNIT_ASSERT_EQUAL(1 * (int) sizeof(int), + src1->output_signature()->sizeof_stream_item(0)); + + CPPUNIT_ASSERT_EQUAL(1, src1->output_signature()->min_streams()); + CPPUNIT_ASSERT_EQUAL(1, src1->output_signature()->max_streams()); + +} + + diff --git a/gnuradio-core/src/lib/runtime/qa_gr_hier_block2.h b/gnuradio-core/src/lib/runtime/qa_gr_hier_block2.h new file mode 100644 index 000000000..653cd2725 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_hier_block2.h @@ -0,0 +1,42 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QA_GR_HIER_BLOCK2_H +#define INCLUDED_QA_GR_HIER_BLOCK2_H + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> +#include <stdexcept> + +class qa_gr_hier_block2 : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(qa_gr_hier_block2); + + CPPUNIT_TEST(test_make); + + CPPUNIT_TEST_SUITE_END(); + +private: + void test_make(); +}; + +#endif /* INCLUDED_QA_GR_HIER_BLOCK2_H */ diff --git a/gnuradio-core/src/lib/runtime/qa_gr_hier_block2_derived.cc b/gnuradio-core/src/lib/runtime/qa_gr_hier_block2_derived.cc new file mode 100644 index 000000000..060c4e244 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_hier_block2_derived.cc @@ -0,0 +1,87 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qa_gr_hier_block2_derived.h> +#include <gr_top_block.h> +#include <gr_io_signature.h> +#include <gr_null_source.h> +#include <gr_null_sink.h> +#include <gr_head.h> +#include <gr_kludge_copy.h> + +// Declare a test C++ hierarchical block + +class gr_derived_block; +typedef boost::shared_ptr<gr_derived_block> gr_derived_block_sptr; +gr_derived_block_sptr gr_make_derived_block(); + +class gr_derived_block : public gr_hier_block2 +{ +private: + friend gr_derived_block_sptr gr_make_derived_block(); + gr_derived_block(); + +public: + ~gr_derived_block(); +}; + + +gr_derived_block_sptr +gr_make_derived_block() +{ + return gnuradio::get_initial_sptr(new gr_derived_block()); +} + +gr_derived_block::gr_derived_block() + : gr_hier_block2("gr_derived_block", + gr_make_io_signature(1, 1, sizeof(int)), // Input signature + gr_make_io_signature(1, 1, sizeof(int))) // Output signature +{ + gr_block_sptr copy(gr_make_kludge_copy(sizeof(int))); + + connect(self(), 0, copy, 0); + connect(copy, 0, self(), 0); +} + +gr_derived_block::~gr_derived_block() +{ +} + +void qa_gr_hier_block2_derived::test_1() +{ + gr_top_block_sptr tb(gr_make_top_block("test")); + + gr_block_sptr src(gr_make_null_source(sizeof(int))); + gr_block_sptr head(gr_make_head(sizeof(int), 1000)); + gr_derived_block_sptr blk(gr_make_derived_block()); + gr_block_sptr dst(gr_make_null_sink(sizeof(int))); + + tb->connect(src, 0, head, 0); + tb->connect(head, 0, blk, 0); + tb->connect(blk, 0, dst, 0); + + tb->run(); +} diff --git a/gnuradio-core/src/lib/runtime/qa_gr_hier_block2_derived.h b/gnuradio-core/src/lib/runtime/qa_gr_hier_block2_derived.h new file mode 100644 index 000000000..8e0a1880c --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_hier_block2_derived.h @@ -0,0 +1,41 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QA_GR_HIER_BLOCK2_DERIVED_H +#define INCLUDED_QA_GR_HIER_BLOCK2_DERIVED_H + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> +#include <stdexcept> + +// Declare a QA test case +class qa_gr_hier_block2_derived : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(qa_gr_hier_block2_derived); + CPPUNIT_TEST(test_1); + CPPUNIT_TEST_SUITE_END(); + +private: + void test_1(); +}; + +#endif /* INCLUDED_QA_GR_HIER_BLOCK2_DERIVED_H */ diff --git a/gnuradio-core/src/lib/runtime/qa_gr_io_signature.cc b/gnuradio-core/src/lib/runtime/qa_gr_io_signature.cc new file mode 100644 index 000000000..c1737ffb8 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_io_signature.cc @@ -0,0 +1,64 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <qa_gr_io_signature.h> +#include <gr_io_signature.h> + +void +qa_gr_io_signature::t0 () +{ + gr_make_io_signature (1, 1, sizeof (int)); +} + +void +qa_gr_io_signature::t1 () +{ + gr_make_io_signature (3, 1, sizeof (int)); // throws std::invalid_argument +} + +void +qa_gr_io_signature::t2 () +{ + gr_io_signature_sptr p = + gr_make_io_signature (3, gr_io_signature::IO_INFINITE, sizeof (int)); + + CPPUNIT_ASSERT_EQUAL (p->min_streams (), 3); + CPPUNIT_ASSERT_EQUAL (p->sizeof_stream_item (0), (int) sizeof (int)); +} + +void +qa_gr_io_signature::t3 () +{ + gr_io_signature_sptr p = + gr_make_io_signature3 (0, 5, 1, 2, 3); + + CPPUNIT_ASSERT_EQUAL (p->min_streams (), 0); + CPPUNIT_ASSERT_EQUAL (p->max_streams (), 5); + CPPUNIT_ASSERT_EQUAL (p->sizeof_stream_item(0), 1); + CPPUNIT_ASSERT_EQUAL (p->sizeof_stream_item(1), 2); + CPPUNIT_ASSERT_EQUAL (p->sizeof_stream_item(2), 3); + CPPUNIT_ASSERT_EQUAL (p->sizeof_stream_item(3), 3); + CPPUNIT_ASSERT_EQUAL (p->sizeof_stream_item(4), 3); +} diff --git a/gnuradio-core/src/lib/runtime/qa_gr_io_signature.h b/gnuradio-core/src/lib/runtime/qa_gr_io_signature.h new file mode 100644 index 000000000..9cd6bb524 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_io_signature.h @@ -0,0 +1,46 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QA_GR_IO_SIGNATURE_H +#define INCLUDED_QA_GR_IO_SIGNATURE_H + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> +#include <stdexcept> + +class qa_gr_io_signature : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE (qa_gr_io_signature); + CPPUNIT_TEST (t0); + CPPUNIT_TEST_EXCEPTION (t1, std::invalid_argument); + CPPUNIT_TEST (t2); + CPPUNIT_TEST (t3); + CPPUNIT_TEST_SUITE_END (); + + private: + void t0 (); + void t1 (); + void t2 (); + void t3 (); +}; + +#endif /* INCLUDED_QA_GR_IO_SIGNATURE_H */ diff --git a/gnuradio-core/src/lib/runtime/qa_gr_top_block.cc b/gnuradio-core/src/lib/runtime/qa_gr_top_block.cc new file mode 100644 index 000000000..6bbc9ceb8 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_top_block.cc @@ -0,0 +1,285 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qa_gr_top_block.h> +#include <gr_top_block.h> +#include <gr_head.h> +#include <gr_nop.h> +#include <gr_null_source.h> +#include <gr_null_sink.h> +#include <iostream> + +#define VERBOSE 0 + +void qa_gr_top_block::t0() +{ + if (VERBOSE) std::cout << "qa_gr_top_block::t0()\n"; + + gr_top_block_sptr tb = gr_make_top_block("top"); + + CPPUNIT_ASSERT(tb); +} + +void qa_gr_top_block::t1_run() +{ + if (VERBOSE) std::cout << "qa_gr_top_block::t1()\n"; + + gr_top_block_sptr tb = gr_make_top_block("top"); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_block_sptr head = gr_make_head(sizeof(int), 100000); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + tb->connect(src, 0, head, 0); + tb->connect(head, 0, dst, 0); + tb->run(); +} + +void qa_gr_top_block::t2_start_stop_wait() +{ + if (VERBOSE) std::cout << "qa_gr_top_block::t2()\n"; + + gr_top_block_sptr tb = gr_make_top_block("top"); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_block_sptr head = gr_make_head(sizeof(int), 100000); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + tb->connect(src, 0, head, 0); + tb->connect(head, 0, dst, 0); + + tb->start(); + tb->stop(); + tb->wait(); +} + +void qa_gr_top_block::t3_lock_unlock() +{ + if (VERBOSE) std::cout << "qa_gr_top_block::t3()\n"; + + gr_top_block_sptr tb = gr_make_top_block("top"); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + tb->connect(src, 0, dst, 0); + + tb->start(); + + tb->lock(); + tb->unlock(); + + tb->stop(); + tb->wait(); +} + +void qa_gr_top_block::t4_reconfigure() +{ + if (VERBOSE) std::cout << "qa_gr_top_block::t4()\n"; + + gr_top_block_sptr tb = gr_make_top_block("top"); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_block_sptr head = gr_make_head(sizeof(int), 100000); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + // Start infinite flowgraph + tb->connect(src, 0, dst, 0); + tb->start(); + + // Reconfigure with gr_head in the middle + tb->lock(); + tb->disconnect(src, 0, dst, 0); + tb->connect(src, 0, head, 0); + tb->connect(head, 0, dst, 0); + tb->unlock(); + + // Wait for flowgraph to end on its own + tb->wait(); +} + + +void qa_gr_top_block::t5_max_noutputs() +{ + if (VERBOSE) std::cout << "qa_gr_top_block::t5()\n"; + + gr_top_block_sptr tb = gr_make_top_block("top"); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_block_sptr head = gr_make_head(sizeof(int), 100000); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + // Start infinite flowgraph + tb->connect(src, 0, head, 0); + tb->connect(head, 0, dst, 0); + tb->start(100); + tb->wait(); +} + +void qa_gr_top_block::t6_reconfig_max_noutputs() +{ + if (VERBOSE) std::cout << "qa_gr_top_block::t6()\n"; + + gr_top_block_sptr tb = gr_make_top_block("top"); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_block_sptr head = gr_make_head(sizeof(int), 100000); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + // Start infinite flowgraph + tb->connect(src, 0, dst, 0); + tb->start(100); + + // Reconfigure with gr_head in the middle + tb->lock(); + tb->disconnect(src, 0, dst, 0); + tb->connect(src, 0, head, 0); + tb->connect(head, 0, dst, 0); + tb->set_max_noutput_items(1000); + head->set_max_noutput_items(500); + tb->unlock(); + + // Wait for flowgraph to end on its own + tb->wait(); +} + +void qa_gr_top_block::t7_max_noutputs_per_block() +{ + if (VERBOSE) std::cout << "qa_gr_top_block::t7()\n"; + + gr_top_block_sptr tb = gr_make_top_block("top"); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_block_sptr head = gr_make_head(sizeof(int), 100000); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + head->set_max_noutput_items(100); + + // Start infinite flowgraph + tb->connect(src, 0, head, 0); + tb->connect(head, 0, dst, 0); + tb->start(); + tb->wait(); +} + +void qa_gr_top_block::t8_reconfig_max_noutputs_per_block() +{ + if (VERBOSE) std::cout << "qa_gr_top_block::t8()\n"; + + gr_top_block_sptr tb = gr_make_top_block("top"); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_block_sptr head = gr_make_head(sizeof(int), 100000); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + head->set_max_noutput_items(99); + + // Start infinite flowgraph + tb->connect(src, 0, dst, 0); + tb->start(201); + + // Reconfigure with gr_head in the middle + tb->lock(); + tb->disconnect(src, 0, dst, 0); + tb->connect(src, 0, head, 0); + tb->connect(head, 0, dst, 0); + tb->set_max_noutput_items(1023); + head->set_max_noutput_items(513); + tb->unlock(); + + // Wait for flowgraph to end on its own + tb->wait(); +} + +void qa_gr_top_block::t9_max_output_buffer() +{ + if (VERBOSE) std::cout << "qa_gr_top_block::t9()\n"; + + gr_top_block_sptr tb = gr_make_top_block("top"); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_block_sptr head = gr_make_head(sizeof(int), 100000); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + head->set_max_output_buffer(1024); + + // Start infinite flowgraph + tb->connect(src, 0, head, 0); + tb->connect(head, 0, dst, 0); + tb->start(); + tb->wait(); +} + +void qa_gr_top_block::t10_reconfig_max_output_buffer() +{ + if (VERBOSE) std::cout << "qa_gr_top_block::t10()\n"; + + gr_top_block_sptr tb = gr_make_top_block("top"); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_block_sptr head = gr_make_head(sizeof(int), 100000); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + head->set_max_output_buffer(1000); + + // Start infinite flowgraph + tb->connect(src, 0, dst, 0); + tb->start(201); + + // Reconfigure with gr_head in the middle + tb->lock(); + gr_block_sptr nop = gr_make_nop(sizeof(int)); + nop->set_max_output_buffer(4000); + tb->disconnect(src, 0, dst, 0); + tb->connect(src, 0, head, 0); + tb->connect(head, 0, nop, 0); + tb->connect(nop, 0, dst, 0); + tb->unlock(); + + // Wait for flowgraph to end on its own + tb->wait(); +} + +void qa_gr_top_block::t11_set_block_affinity() +{ + gr_top_block_sptr tb = gr_make_top_block("top"); + gr_block_sptr src (gr_make_null_source(sizeof(float))); + gr_block_sptr snk (gr_make_null_sink(sizeof(float))); + + std::vector<int> set(1, 0), ret; + src->set_processor_affinity(set); + + tb->connect(src, 0, snk, 0); + tb->start(); + tb->stop(); + tb->wait(); + + ret = src->processor_affinity(); + + // We only set the core affinity to 0 because we always know at + // least one thread core exists to use. + CPPUNIT_ASSERT_EQUAL(set[0], ret[0]); +} diff --git a/gnuradio-core/src/lib/runtime/qa_gr_top_block.h b/gnuradio-core/src/lib/runtime/qa_gr_top_block.h new file mode 100644 index 000000000..634eeab1f --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_top_block.h @@ -0,0 +1,66 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QA_GR_TOP_BLOCK_H +#define INCLUDED_QA_GR_TOP_BLOCK_H + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> +#include <stdexcept> + +class qa_gr_top_block : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(qa_gr_top_block); + + CPPUNIT_TEST(t0); + CPPUNIT_TEST(t1_run); + CPPUNIT_TEST(t2_start_stop_wait); + CPPUNIT_TEST(t3_lock_unlock); + CPPUNIT_TEST(t4_reconfigure); // triggers 'join never returns' bug + CPPUNIT_TEST(t5_max_noutputs); + CPPUNIT_TEST(t6_reconfig_max_noutputs); + CPPUNIT_TEST(t7_max_noutputs_per_block); + CPPUNIT_TEST(t8_reconfig_max_noutputs_per_block); + CPPUNIT_TEST(t9_max_output_buffer); + CPPUNIT_TEST(t10_reconfig_max_output_buffer); + CPPUNIT_TEST(t11_set_block_affinity); + + CPPUNIT_TEST_SUITE_END(); + +private: + + void t0(); + void t1_run(); + void t2_start_stop_wait(); + void t3_lock_unlock(); + void t4_reconfigure(); + void t5_max_noutputs(); + void t6_reconfig_max_noutputs(); + void t7_max_noutputs_per_block(); + void t8_reconfig_max_noutputs_per_block(); + void t9_max_output_buffer(); + void t10_reconfig_max_output_buffer(); + void t11_set_block_affinity(); + +}; + +#endif /* INCLUDED_QA_GR_TOP_BLOCK_H */ diff --git a/gnuradio-core/src/lib/runtime/qa_gr_vmcircbuf.cc b/gnuradio-core/src/lib/runtime/qa_gr_vmcircbuf.cc new file mode 100644 index 000000000..e3b36d882 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_vmcircbuf.cc @@ -0,0 +1,40 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qa_gr_vmcircbuf.h> +#include <cppunit/TestAssert.h> +#include <gr_vmcircbuf.h> +#include <stdio.h> + +void +qa_gr_vmcircbuf::test_all () +{ + int verbose = 1; // summary + + bool ok = gr_vmcircbuf_sysconfig::test_all_factories (verbose); + + CPPUNIT_ASSERT_EQUAL (true, ok); +} diff --git a/gnuradio-core/src/lib/runtime/qa_gr_vmcircbuf.h b/gnuradio-core/src/lib/runtime/qa_gr_vmcircbuf.h new file mode 100644 index 000000000..3576660d5 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_gr_vmcircbuf.h @@ -0,0 +1,39 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef _QA_GR_VMCIRCBUF_H_ +#define _QA_GR_VMCIRCBUF_H_ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> + +class qa_gr_vmcircbuf : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE (qa_gr_vmcircbuf); + CPPUNIT_TEST (test_all); + CPPUNIT_TEST_SUITE_END (); + + private: + void test_all (); +}; + + +#endif /* _QA_GR_VMCIRCBUF_H_ */ diff --git a/gnuradio-core/src/lib/runtime/qa_runtime.cc b/gnuradio-core/src/lib/runtime/qa_runtime.cc new file mode 100644 index 000000000..9091d5c00 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_runtime.cc @@ -0,0 +1,61 @@ +/* + * Copyright 2002,2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * This class gathers together all the test cases for the gr + * directory into a single test suite. As you create new test cases, + * add them here. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <qa_runtime.h> +#include <qa_gr_vmcircbuf.h> +#include <qa_gr_io_signature.h> +#include <qa_gr_block.h> +#include <qa_gr_flowgraph.h> +#include <qa_gr_top_block.h> +#include <qa_gr_hier_block2.h> +#include <qa_gr_hier_block2_derived.h> +#include <qa_gr_buffer.h> +#include <qa_block_tags.h> +#include <qa_set_msg_handler.h> + +CppUnit::TestSuite * +qa_runtime::suite () +{ + CppUnit::TestSuite *s = new CppUnit::TestSuite ("runtime"); + + s->addTest (qa_gr_vmcircbuf::suite ()); + s->addTest (qa_gr_io_signature::suite ()); + s->addTest (qa_gr_block::suite ()); + //s->addTest (qa_gr_flowgraph::suite ()); + s->addTest (qa_gr_top_block::suite ()); + s->addTest (qa_gr_hier_block2::suite ()); + s->addTest (qa_gr_hier_block2_derived::suite ()); + s->addTest (qa_gr_buffer::suite ()); + //s->addTest (qa_block_tags::suite ()); + //s->addTest (qa_set_msg_handler::suite ()); + + return s; +} diff --git a/gnuradio-core/src/lib/runtime/qa_runtime.h b/gnuradio-core/src/lib/runtime/qa_runtime.h new file mode 100644 index 000000000..da71cbd0f --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_runtime.h @@ -0,0 +1,38 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _QA_RUNTIME_H_ +#define _QA_RUNTIME_H_ + +#include <gruel/attributes.h> +#include <cppunit/TestSuite.h> + +//! collect all the tests for the runtime directory + +class __GR_ATTR_EXPORT qa_runtime { + public: + //! return suite of tests for all of runtime directory + static CppUnit::TestSuite *suite (); +}; + + +#endif /* _QA_RUNTIME_H_ */ diff --git a/gnuradio-core/src/lib/runtime/qa_set_msg_handler.cc b/gnuradio-core/src/lib/runtime/qa_set_msg_handler.cc new file mode 100644 index 000000000..c84a219bd --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_set_msg_handler.cc @@ -0,0 +1,81 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qa_set_msg_handler.h> +#include <gr_top_block.h> +#include <gr_head.h> +#include <gr_null_source.h> +#include <gr_null_sink.h> +#include <gr_nop.h> +#include <gruel/msg_passing.h> +#include <iostream> +#include <boost/thread/thread.hpp> + + +#define VERBOSE 0 + +using namespace pmt; + +/* + * The gr_nop block has been instrumented so that it counts + * the number of messages sent to it. We use this feature + * to confirm that gr_nop's call to set_msg_handler is working + * correctly. + */ + +void qa_set_msg_handler::t0() +{ + static const int NMSGS = 10; + + if (VERBOSE) std::cout << "qa_set_msg_handler::t0()\n"; + + gr_top_block_sptr tb = gr_make_top_block("top"); + + gr_block_sptr src = gr_make_null_source(sizeof(int)); + gr_nop_sptr nop = gr_make_nop(sizeof(int)); + gr_block_sptr dst = gr_make_null_sink(sizeof(int)); + + tb->connect(src, 0, nop, 0); + tb->connect(nop, 0, dst, 0); + + // Must start graph before sending messages + tb->start(); + + // Send them... + pmt_t port(pmt_intern("port")); + for (int i = 0; i < NMSGS; i++){ + send(nop, port, mp(mp("example-msg"), mp(i))); + } + + // Give the messages a chance to be processed + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + + tb->stop(); + tb->wait(); + + // Confirm that the nop block received the right number of messages. + CPPUNIT_ASSERT_EQUAL(NMSGS, nop->nmsgs_received()); +} diff --git a/gnuradio-core/src/lib/runtime/qa_set_msg_handler.h b/gnuradio-core/src/lib/runtime/qa_set_msg_handler.h new file mode 100644 index 000000000..60277a12c --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_set_msg_handler.h @@ -0,0 +1,43 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QA_SET_MSG_HANDLER_H +#define INCLUDED_QA_SET_MSG_HANDLER_H + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> +#include <stdexcept> + +class qa_set_msg_handler : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(qa_set_msg_handler); + + CPPUNIT_TEST(t0); + + CPPUNIT_TEST_SUITE_END(); + +private: + + void t0(); +}; + +#endif /* INCLUDED_QA_SET_MSG_HANDLER_H */ diff --git a/gnuradio-core/src/lib/runtime/runtime.i b/gnuradio-core/src/lib/runtime/runtime.i new file mode 100644 index 000000000..64c28c8e1 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/runtime.i @@ -0,0 +1,123 @@ +/* + * Copyright 2012-2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#define GR_CORE_API + +//not here to fight you swig, reference() is ambigi with shared ptr, but whatevs +%ignore gri_agc_cc::reference(); +%ignore gri_agc2_ff::reference(); +%ignore gri_agc2_cc::reference(); + +//someone forgot about d_detail +%ignore gr_block::d_setlock; + +//dont export work overloads +%ignore general_work; +%ignore work; +%ignore forecast; + +%{ + +#include <gras/block.hpp> +#include <gras/hier_block.hpp> +#include <gras/top_block.hpp> +#include <gr_block.h> +#include <gr_top_block.h> +#include <gr_hier_block2.h> +#include <gr_message.h> +#include <gr_msg_handler.h> +#include <gr_msg_queue.h> +#include <gr_sync_block.h> +#include <gr_sync_decimator.h> +#include <gr_sync_interpolator.h> + +%} + +//const size types used by blocks in python +%constant int sizeof_char = sizeof(char); +%constant int sizeof_short = sizeof(short); +%constant int sizeof_int = sizeof(int); +%constant int sizeof_float = sizeof(float); +%constant int sizeof_double = sizeof(double); +%constant int sizeof_gr_complex = sizeof(gr_complex); + +%include <gr_message.i> +%include <gr_msg_handler.i> +%include <gr_msg_queue.i> +%include <gr_swig_block_magic.i> +%include <gr_io_signature.i> + +#ifdef SW_RUNTIME + +%import <gras/block.i> +%include <gr_block.h> +%include <gr_hier_block2.h> +%include <gr_top_block.h> +%include <gr_sync_block.h> +%include <gr_sync_decimator.h> +%include <gr_sync_interpolator.h> + +#else + +//the bare minimum block inheritance interface to make things work but keep swig cxx file size down +%include <gras/gras.hpp> +%import <gras/element.i> +namespace gras +{ + struct Block : gras::Element{}; + struct HierBlock : gras::Element{}; +} +struct gr_hier_block2 : gras::HierBlock{}; +struct gr_block : gras::Block +{ + gr_io_signature_sptr input_signature(void) const; + gr_io_signature_sptr output_signature(void) const; + + unsigned history () const; + + int output_multiple () const; + double relative_rate () const; + + bool start(); + bool stop(); + + uint64_t nitems_read(unsigned int which_input); + uint64_t nitems_written(unsigned int which_output); + + // Methods to manage the block's max_noutput_items size. + int max_noutput_items(); + void set_max_noutput_items(int m); + void unset_max_noutput_items(); + bool is_set_max_noutput_items(); + + // Methods to manage block's min/max buffer sizes. + long max_output_buffer(int i); + void set_max_output_buffer(long max_output_buffer); + void set_max_output_buffer(int port, long max_output_buffer); + long min_output_buffer(int i); + void set_min_output_buffer(long min_output_buffer); + void set_min_output_buffer(int port, long min_output_buffer); +}; +struct gr_sync_block : gr_block{}; +struct gr_sync_interpolator : gr_sync_block{}; +struct gr_sync_decimator : gr_sync_block{}; + +#endif diff --git a/gnuradio-core/src/lib/runtime/test_shared_block_ptr.cc b/gnuradio-core/src/lib/runtime/test_shared_block_ptr.cc new file mode 100644 index 000000000..bb4e86322 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/test_shared_block_ptr.cc @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <gr_shared_block_sptr.h> +#include <gr_vector_source_i.h> + +gr_block_sptr +foo (gr_vector_source_i_sptr s) +{ + return gr_block_sptr (s); +} + +typedef gr_shared_block_sptr<gr_vector_source_i> gr_vector_source_i_ptrX; +//typedef boost::shared_ptr<gr_vector_source_i> gr_vector_source_i_ptrX; + +gr_vector_source_i_ptrX +bar (gr_vector_source_i *s) +{ + return gr_vector_source_i_ptrX (s); +} + +gr_block_sptr +baz_1 (gr_vector_source_i_ptrX s) +{ + return gr_block_sptr (s); +} + +#if 0 +gr_block_sptr +baz_2 (gr_vector_source_i_ptrX s) +{ + return s.block_sptr (); +} +#endif |