From e78b3c6b142cdda00a1d2042c56b47c5e31cfb27 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Fri, 28 May 2010 18:43:26 -0700 Subject: Made a phony zero-copy interface for those interfaces that are actual copy-interfaces. Using interface in the udp asio transport. --- host/include/uhd/transport/udp_zero_copy.hpp | 2 +- host/include/uhd/transport/zero_copy.hpp | 228 +++++++++++++++++---------- 2 files changed, 145 insertions(+), 85 deletions(-) (limited to 'host/include') diff --git a/host/include/uhd/transport/udp_zero_copy.hpp b/host/include/uhd/transport/udp_zero_copy.hpp index 525606a9f..818709973 100644 --- a/host/include/uhd/transport/udp_zero_copy.hpp +++ b/host/include/uhd/transport/udp_zero_copy.hpp @@ -34,7 +34,7 @@ namespace uhd{ namespace transport{ * If no platform specific solution is available, make returns a boost asio * implementation that wraps the functionality around a standard send/recv calls. */ -class UHD_API udp_zero_copy : public zero_copy_if{ +class UHD_API udp_zero_copy : public virtual zero_copy_if{ public: typedef boost::shared_ptr sptr; diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index 52c6d4143..2efabaccf 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -25,109 +25,169 @@ namespace uhd{ namespace transport{ -/*! - * A managed receive buffer: - * Contains a reference to transport-managed memory, - * and a method to release the memory after reading. - */ -class UHD_API managed_recv_buffer : boost::noncopyable{ -public: - typedef boost::shared_ptr sptr; - /*! - * Managed recv buffer destructor: - * Signal to the transport that we are done with the buffer. - * This should be called to release the buffer to the transport. - * After calling, the referenced memory should be considered invalid. + * A managed receive buffer: + * Contains a reference to transport-managed memory, + * and a method to release the memory after reading. */ - virtual ~managed_recv_buffer(void){}; + class UHD_API managed_recv_buffer : boost::noncopyable{ + public: + typedef boost::shared_ptr sptr; + + /*! + * Managed recv buffer destructor: + * Signal to the transport that we are done with the buffer. + * This should be called to release the buffer to the transport. + * After calling, the referenced memory should be considered invalid. + */ + virtual ~managed_recv_buffer(void) = 0; + + /*! + * Get the size of the underlying buffer. + * \return the number of bytes + */ + size_t size(void) const{ + return boost::asio::buffer_size(this->get()); + } + + /*! + * Get a pointer to the underlying buffer. + * \return a pointer into memory + */ + template T cast(void) const{ + return boost::asio::buffer_cast(this->get()); + } + + private: + /*! + * Get a reference to the internal const buffer. + * The buffer has a reference to memory and a size. + * \return a boost asio const buffer + */ + virtual const boost::asio::const_buffer &get(void) const = 0; + }; /*! - * Get the size of the underlying buffer. - * \return the number of bytes + * A managed send buffer: + * Contains a reference to transport-managed memory, + * and a method to release the memory after writing. */ - size_t size(void) const{ - return boost::asio::buffer_size(this->get()); - } + class UHD_API managed_send_buffer : boost::noncopyable{ + public: + typedef boost::shared_ptr sptr; + + /*! + * Signal to the transport that we are done with the buffer. + * This should be called to commit the write to the transport object. + * After calling, the referenced memory should be considered invalid. + * \param num_bytes the number of bytes written into the buffer + */ + virtual void commit(size_t num_bytes) = 0; + + /*! + * Get the size of the underlying buffer. + * \return the number of bytes + */ + size_t size(void) const{ + return boost::asio::buffer_size(this->get()); + } + + /*! + * Get a pointer to the underlying buffer. + * \return a pointer into memory + */ + template T cast(void) const{ + return boost::asio::buffer_cast(this->get()); + } + + private: + /*! + * Get a reference to the internal mutable buffer. + * The buffer has a reference to memory and a size. + * \return a boost asio mutable buffer + */ + virtual const boost::asio::mutable_buffer &get(void) const = 0; + }; /*! - * Get a pointer to the underlying buffer. - * \return a pointer into memory + * A zero-copy interface for transport objects. + * Provides a way to get send and receive buffers + * with memory managed by the transport object. */ - template T cast(void) const{ - return boost::asio::buffer_cast(this->get()); - } + class UHD_API zero_copy_if : boost::noncopyable{ + public: + typedef boost::shared_ptr sptr; -private: - /*! - * Get a reference to the internal const buffer. - * The buffer has a reference to memory and a size. - * \return a boost asio const buffer - */ - virtual const boost::asio::const_buffer &get(void) const = 0; -}; - -/*! - * A managed send buffer: - * Contains a reference to transport-managed memory, - * and a method to release the memory after writing. - */ -class UHD_API managed_send_buffer : boost::noncopyable{ -public: - typedef boost::shared_ptr sptr; + /*! + * Get a new receive buffer from this transport object. + */ + virtual managed_recv_buffer::sptr get_recv_buff(void) = 0; - /*! - * Signal to the transport that we are done with the buffer. - * This should be called to commit the write to the transport object. - * After calling, the referenced memory should be considered invalid. - * \param num_bytes the number of bytes written into the buffer - */ - virtual void commit(size_t num_bytes) = 0; + /*! + * Get a new send buffer from this transport object. + */ + virtual managed_send_buffer::sptr get_send_buff(void) = 0; + }; /*! - * Get the size of the underlying buffer. - * \return the number of bytes + * A phony-zero-copy interface for transport objects that + * provides a zero-copy interface on top of copying transport. + * This interface implements the get managed recv buffer, + * the base class must implement the private recv method. */ - size_t size(void) const{ - return boost::asio::buffer_size(this->get()); - } + class UHD_API phony_zero_copy_recv_if : public virtual zero_copy_if{ + public: - /*! - * Get a pointer to the underlying buffer. - * \return a pointer into memory - */ - template T cast(void) const{ - return boost::asio::buffer_cast(this->get()); - } + //! structors + phony_zero_copy_recv_if(size_t max_buff_size); + ~phony_zero_copy_recv_if(void); -private: - /*! - * Get a reference to the internal mutable buffer. - * The buffer has a reference to memory and a size. - * \return a boost asio mutable buffer - */ - virtual const boost::asio::mutable_buffer &get(void) const = 0; -}; - -/*! - * A zero-copy interface for transport objects. - * Provides a way to get send and receive buffers - * with memory managed by the transport object. - */ -class UHD_API zero_copy_if : boost::noncopyable{ -public: - typedef boost::shared_ptr sptr; + /*! + * Get a new receive buffer from this transport object. + */ + managed_recv_buffer::sptr get_recv_buff(void); - /*! - * Get a new receive buffer from this transport object. - */ - virtual managed_recv_buffer::sptr get_recv_buff(void) = 0; + private: + + /*! + * Perform a private copying recv. + * \param buff the buffer to write data into + * \return the number of bytes written to buff + */ + virtual size_t recv(const boost::asio::mutable_buffer &buff) = 0; + + struct impl; impl *_impl; //private implementation details + }; /*! - * Get a new send buffer from this transport object. + * A phony-zero-copy interface for transport objects that + * provides a zero-copy interface on top of copying transport. + * This interface implements the get managed send buffer, + * the base class must implement the private send method. */ - virtual managed_send_buffer::sptr get_send_buff(void) = 0; -}; + class UHD_API phony_zero_copy_send_if : public virtual zero_copy_if{ + public: + + //! structors + phony_zero_copy_send_if(size_t max_buff_size); + ~phony_zero_copy_send_if(void); + + /*! + * Get a new send buffer from this transport object. + */ + managed_send_buffer::sptr get_send_buff(void); + + private: + + /*! + * Perform a private copying send. + * \param buff the buffer to read data from + * \return the number of bytes read from buff + */ + virtual size_t send(const boost::asio::const_buffer &buff) = 0; + + struct impl; impl *_impl; //private implementation details + }; }} //namespace -- cgit v1.2.3 From 4c133daee30e04d7740ca02a307f8e956de6dec6 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Thu, 27 May 2010 12:58:37 -0700 Subject: work on buffers for recv --- host/include/uhd/transport/CMakeLists.txt | 2 + host/include/uhd/transport/alignment_buffer.hpp | 136 +++++++++++++++++++++++ host/include/uhd/transport/bounded_buffer.hpp | 141 ++++++++++++++++++++++++ host/test/CMakeLists.txt | 1 + host/test/bounded_buffer_test.cpp | 46 ++++++++ 5 files changed, 326 insertions(+) create mode 100644 host/include/uhd/transport/alignment_buffer.hpp create mode 100644 host/include/uhd/transport/bounded_buffer.hpp create mode 100644 host/test/bounded_buffer_test.cpp (limited to 'host/include') diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt index 4cefffa24..23a4aae94 100644 --- a/host/include/uhd/transport/CMakeLists.txt +++ b/host/include/uhd/transport/CMakeLists.txt @@ -17,6 +17,8 @@ INSTALL(FILES + alignment_buffer.hpp + bounded_buffer.hpp convert_types.hpp if_addrs.hpp udp_simple.hpp diff --git a/host/include/uhd/transport/alignment_buffer.hpp b/host/include/uhd/transport/alignment_buffer.hpp new file mode 100644 index 000000000..7fa4f2694 --- /dev/null +++ b/host/include/uhd/transport/alignment_buffer.hpp @@ -0,0 +1,136 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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 of the License, or +// (at your option) any later version. +// +// This program 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, see . +// + +#ifndef INCLUDED_UHD_TRANSPORT_ALIGNMENT_BUFFER_HPP +#define INCLUDED_UHD_TRANSPORT_ALIGNMENT_BUFFER_HPP + +#include +#include +#include +#include +#include +#include + +namespace uhd{ namespace transport{ + + /*! + * Imlement a templated alignment buffer: + * Used for aligning asynchronously pushed elements with matching ids. + */ + template class alignment_buffer{ + public: + typedef boost::shared_ptr > sptr; + + /*! + * Create the alignment buffer. + * \param capacity the maximum elements per index + * \param width the number of elements to align + */ + alignment_buffer(size_t capacity, size_t width){ + _buffs.resize(width); + for (size_t i = 0; i < width; i++){ + _buffs[i].buff = bounded_buffer_sptr(new bounded_buffer_type(capacity)); + _buffs[i].has_popped_element = false; + } + } + + /*! + * Destroy this alignment buffer. + */ + ~alignment_buffer(void){ + /* NOP */ + } + + /*! + * Push a single element into the buffer specified by index. + * Notify the condition variable for a thread blocked in pop. + * \param elem the element to push + * \param seq the sequence identifier + * \param index the buffer index + */ + void push_elem_with_wait(const elem_type &elem, const seq_type &seq, size_t index){ + _buffs[index].buff.push_with_wait(buff_contents_type(elem, seq)); + _pushed_cond.notify_one(); + } + + /*! + * Pop an aligned set of elements from this alignment buffer. + * \param elems a collection to store the aligned elements + */ + template + void pop_elems_with_wait(elems_type &elems){ + //TODO................................ + buff_contents_type buff_contents_tmp; + for (size_t i = 0; i < _buffs.size();){ + if (_buffs[i].has_popped_element){ + i++: + continue; + } + _buffs[i].pop_with_wait(buff_contents_tmp); + if (buff_contents_tmp.second == _expected_seq_id){ + _buffs[i].has_popped_element = true; + i++; + continue; + } + + //if the sequence number is older, pop until we get the current sequence number + //do this by setting has popped element false and continuing on the same condition + if (buff_contents_tmp.second < _expected_seq_id){ + _buffs[i].has_popped_element = false; + continue; + } + + //if the sequence number is newer, start from scratch at the new sequence number + //do this by setting all has popped elements false and restarting on index zero + if (buff_contents_tmp.second > _expected_seq_id){ + _expected_seq_id = buff_contents_tmp.second; + for (size_t j = 0; j < i; j++){ + _buffs[j].has_popped_element = false; + } + i = 0; + continue; + } + } + //if aligned + for (size_t i = 0; i < _buffs.size(); i++){ + elems[i] = _buffs[i].popped_element; + _buffs[i].has_popped_element = false; + } + } + + private: + //a vector of bounded buffers for each index + typedef std::pair buff_contents_type; + typedef bounded_buffer bounded_buffer_type; + typedef boost::shared_ptr bounded_buffer_sptr; + struct buff_type{ + bounded_buffer_sptr buff; + elem_type popped_element; + bool has_popped_element; + }; + std::vector _buffs; + + //the seq identifier to align with + seq_type _expected_seq_id; + + //a condition to notify when a new element is pushed + boost::condition_variable _pushed_cond; + }; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_ALIGNMENT_BUFFER_HPP */ diff --git a/host/include/uhd/transport/bounded_buffer.hpp b/host/include/uhd/transport/bounded_buffer.hpp new file mode 100644 index 000000000..26fc9c0a0 --- /dev/null +++ b/host/include/uhd/transport/bounded_buffer.hpp @@ -0,0 +1,141 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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 of the License, or +// (at your option) any later version. +// +// This program 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, see . +// + +#ifndef INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_HPP +#define INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_HPP + +#include +#include +#include +#include +#include + +namespace uhd{ namespace transport{ + + /*! + * Imlement a templated bounded buffer: + * Used for passing elements between threads in a producer-consumer model. + * The bounded buffer implemented waits and timed waits with condition variables. + * The pop operation blocks on the bounded_buffer to become non empty. + * The push operation blocks on the bounded_buffer to become non full. + */ + template class bounded_buffer{ + public: + typedef boost::shared_ptr > sptr; + + /*! + * Create a new bounded_buffer of a given size. + * \param capacity the bounded_buffer capacity + */ + bounded_buffer(size_t capacity) : _buffer(capacity), _size(0){ + /* NOP */ + } + + /*! + * Destroy this bounded_buffer. + */ + ~bounded_buffer(void){ + /* NOP */ + } + + /*! + * Is the bounded_buffer buffer not full? + * \return true for not full + */ + bool is_not_full(void) const{ + return _size != _buffer.capacity(); + } + + /*! + * Is the bounded_buffer buffer not empty? + * \return true for not empty + */ + bool is_not_empty(void) const{ + return _size != 0; + } + + /*! + * Push a new element into the bounded_buffer. + * Wait until the bounded_buffer becomes non-full. + * \param elem the new element to push + */ + UHD_INLINE void push_with_wait(const elem_type &elem){ + boost::unique_lock lock(_mutex); + _full_cond.wait(lock, boost::bind(&bounded_buffer::is_not_full, this)); + _buffer.push_front(elem); ++_size; + lock.unlock(); + _empty_cond.notify_one(); + } + + /*! + * Push a new element into the bounded_buffer. + * Wait until the bounded_buffer becomes non-full or timeout. + * \param elem the new element to push + * \param time the timeout time + * \return false when the operation times out + */ + template UHD_INLINE + bool push_with_timed_wait(const elem_type &elem, const time_type &time){ + boost::unique_lock lock(_mutex); + if (not _full_cond.timed_wait(lock, time, boost::bind(&bounded_buffer::is_not_full, this))) return false; + _buffer.push_front(elem); ++_size; + lock.unlock(); + _empty_cond.notify_one(); + return true; + } + + /*! + * Pop an element from the bounded_buffer. + * Wait until the bounded_buffer becomes non-empty. + * \param elem the element reference pop to + */ + UHD_INLINE void pop_with_wait(elem_type &elem){ + boost::unique_lock lock(_mutex); + _empty_cond.wait(lock, boost::bind(&bounded_buffer::is_not_empty, this)); + elem = _buffer[--_size]; + lock.unlock(); + _full_cond.notify_one(); + } + + /*! + * Pop an element from the bounded_buffer. + * Wait until the bounded_buffer becomes non-empty or timeout. + * \param elem the element reference pop to + * \param time the timeout time + * \return false when the operation times out + */ + template UHD_INLINE + bool pop_with_timed_wait(elem_type &elem, const time_type &time){ + boost::unique_lock lock(_mutex); + if (not _empty_cond.timed_wait(lock, time, boost::bind(&bounded_buffer::is_not_empty, this))) return false; + elem = _buffer[--_size]; + lock.unlock(); + _full_cond.notify_one(); + return true; + } + + private: + boost::mutex _mutex; + boost::condition_variable _empty_cond, _full_cond; + boost::circular_buffer _buffer; + size_t _size; + + }; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_HPP */ diff --git a/host/test/CMakeLists.txt b/host/test/CMakeLists.txt index 61b0b503d..c7c6d7fad 100644 --- a/host/test/CMakeLists.txt +++ b/host/test/CMakeLists.txt @@ -21,6 +21,7 @@ ADD_EXECUTABLE(main_test main_test.cpp addr_test.cpp + bounded_buffer_test.cpp dict_test.cpp error_test.cpp gain_handler_test.cpp diff --git a/host/test/bounded_buffer_test.cpp b/host/test/bounded_buffer_test.cpp new file mode 100644 index 000000000..5d6b6faec --- /dev/null +++ b/host/test/bounded_buffer_test.cpp @@ -0,0 +1,46 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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 of the License, or +// (at your option) any later version. +// +// This program 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, see . +// + +#include +#include + +//test #Include +#include + +using namespace uhd::transport; + +static const boost::posix_time::milliseconds timeout(10); + +BOOST_AUTO_TEST_CASE(test_bounded_buffer){ + bounded_buffer::sptr bb(new bounded_buffer(3)); + + //push elements, check for timeout + BOOST_CHECK(bb->push_with_timed_wait(0, timeout)); + BOOST_CHECK(bb->push_with_timed_wait(1, timeout)); + BOOST_CHECK(bb->push_with_timed_wait(2, timeout)); + BOOST_CHECK(not bb->push_with_timed_wait(3, timeout)); + + int val; + //pop elements, check for timeout and check values + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 0); + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 1); + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 2); + BOOST_CHECK(not bb->pop_with_timed_wait(val, timeout)); +} -- cgit v1.2.3 From 6665d7eb90264f12abdce86302fffe968879d94d Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Thu, 27 May 2010 18:47:27 -0700 Subject: work on bounded and alignment buffer with unit testing --- host/include/uhd/transport/alignment_buffer.hpp | 97 +++++++++++++------------ host/include/uhd/transport/bounded_buffer.hpp | 53 ++++++++------ host/test/bounded_buffer_test.cpp | 79 +++++++++++++++++++- 3 files changed, 153 insertions(+), 76 deletions(-) (limited to 'host/include') diff --git a/host/include/uhd/transport/alignment_buffer.hpp b/host/include/uhd/transport/alignment_buffer.hpp index 7fa4f2694..b33b80da9 100644 --- a/host/include/uhd/transport/alignment_buffer.hpp +++ b/host/include/uhd/transport/alignment_buffer.hpp @@ -41,10 +41,9 @@ namespace uhd{ namespace transport{ * \param width the number of elements to align */ alignment_buffer(size_t capacity, size_t width){ - _buffs.resize(width); for (size_t i = 0; i < width; i++){ - _buffs[i].buff = bounded_buffer_sptr(new bounded_buffer_type(capacity)); - _buffs[i].has_popped_element = false; + _buffs.push_back(bounded_buffer_sptr(new bounded_buffer_type(capacity))); + _all_indexes.push_back(i); } } @@ -56,60 +55,72 @@ namespace uhd{ namespace transport{ } /*! - * Push a single element into the buffer specified by index. - * Notify the condition variable for a thread blocked in pop. + * Push an element with sequence id into the buffer at index. * \param elem the element to push * \param seq the sequence identifier * \param index the buffer index + * \return true if the element fit without popping for space */ - void push_elem_with_wait(const elem_type &elem, const seq_type &seq, size_t index){ - _buffs[index].buff.push_with_wait(buff_contents_type(elem, seq)); - _pushed_cond.notify_one(); + UHD_INLINE bool push_with_pop_on_full( + const elem_type &elem, + const seq_type &seq, + size_t index + ){ + return _buffs[index]->push_with_pop_on_full(buff_contents_type(elem, seq)); } /*! * Pop an aligned set of elements from this alignment buffer. * \param elems a collection to store the aligned elements + * \param time the timeout time + * \return false when the operation times out */ - template - void pop_elems_with_wait(elems_type &elems){ - //TODO................................ + template + bool pop_elems_with_timed_wait(elems_type &elems, const time_type &time){ buff_contents_type buff_contents_tmp; - for (size_t i = 0; i < _buffs.size();){ - if (_buffs[i].has_popped_element){ - i++: - continue; + std::list indexes_to_do(_all_indexes); + + //the seq identifier to align with + seq_type expected_seq_id = seq_type(); + bool expected_seq_id_valid = false; + + //get an aligned set of elements from the buffers: + while(indexes_to_do.size() != 0){ + size_t index = indexes_to_do.back(); + + //pop an element off for this index + if (not _buffs[index]->pop_with_timed_wait(buff_contents_tmp, time)) return false; + + //grab the current sequence id if not valid + if (not expected_seq_id_valid){ + expected_seq_id_valid = true; + expected_seq_id = buff_contents_tmp.second; } - _buffs[i].pop_with_wait(buff_contents_tmp); - if (buff_contents_tmp.second == _expected_seq_id){ - _buffs[i].has_popped_element = true; - i++; + + //if the sequence id matches: + // store the popped element into the output, + // remove this index from the list and continue + if (buff_contents_tmp.second == expected_seq_id){ + elems[index] = buff_contents_tmp.first; + indexes_to_do.pop_back(); continue; } - //if the sequence number is older, pop until we get the current sequence number - //do this by setting has popped element false and continuing on the same condition - if (buff_contents_tmp.second < _expected_seq_id){ - _buffs[i].has_popped_element = false; + //if the sequence id is older: + // continue with the same index to try again + if (buff_contents_tmp.second < expected_seq_id){ continue; } - //if the sequence number is newer, start from scratch at the new sequence number - //do this by setting all has popped elements false and restarting on index zero - if (buff_contents_tmp.second > _expected_seq_id){ - _expected_seq_id = buff_contents_tmp.second; - for (size_t j = 0; j < i; j++){ - _buffs[j].has_popped_element = false; - } - i = 0; + //if the sequence id is newer: + // start from scratch at the new sequence number + if (buff_contents_tmp.second > expected_seq_id){ + expected_seq_id = buff_contents_tmp.second; + indexes_to_do = _all_indexes; continue; } } - //if aligned - for (size_t i = 0; i < _buffs.size(); i++){ - elems[i] = _buffs[i].popped_element; - _buffs[i].has_popped_element = false; - } + return true; } private: @@ -117,18 +128,8 @@ namespace uhd{ namespace transport{ typedef std::pair buff_contents_type; typedef bounded_buffer bounded_buffer_type; typedef boost::shared_ptr bounded_buffer_sptr; - struct buff_type{ - bounded_buffer_sptr buff; - elem_type popped_element; - bool has_popped_element; - }; - std::vector _buffs; - - //the seq identifier to align with - seq_type _expected_seq_id; - - //a condition to notify when a new element is pushed - boost::condition_variable _pushed_cond; + std::vector _buffs; + std::list _all_indexes; }; }} //namespace diff --git a/host/include/uhd/transport/bounded_buffer.hpp b/host/include/uhd/transport/bounded_buffer.hpp index 26fc9c0a0..c50a626fb 100644 --- a/host/include/uhd/transport/bounded_buffer.hpp +++ b/host/include/uhd/transport/bounded_buffer.hpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include namespace uhd{ namespace transport{ @@ -41,7 +41,7 @@ namespace uhd{ namespace transport{ * Create a new bounded_buffer of a given size. * \param capacity the bounded_buffer capacity */ - bounded_buffer(size_t capacity) : _buffer(capacity), _size(0){ + bounded_buffer(size_t capacity) : _buffer(capacity){ /* NOP */ } @@ -53,19 +53,23 @@ namespace uhd{ namespace transport{ } /*! - * Is the bounded_buffer buffer not full? - * \return true for not full - */ - bool is_not_full(void) const{ - return _size != _buffer.capacity(); - } - - /*! - * Is the bounded_buffer buffer not empty? - * \return true for not empty + * Push a new element into the bounded buffer. + * If the buffer is full prior to the push, + * make room by poping the oldest element. + * \param elem the new element to push + * \return true if the element fit without popping for space */ - bool is_not_empty(void) const{ - return _size != 0; + UHD_INLINE bool push_with_pop_on_full(const elem_type &elem){ + boost::unique_lock lock(_mutex); + if(_buffer.full()){ + _buffer.pop_back(); + _buffer.push_front(elem); + return false; + } + else{ + _buffer.push_front(elem); + return true; + } } /*! @@ -75,8 +79,8 @@ namespace uhd{ namespace transport{ */ UHD_INLINE void push_with_wait(const elem_type &elem){ boost::unique_lock lock(_mutex); - _full_cond.wait(lock, boost::bind(&bounded_buffer::is_not_full, this)); - _buffer.push_front(elem); ++_size; + _full_cond.wait(lock, boost::bind(&bounded_buffer::not_full, this)); + _buffer.push_front(elem); lock.unlock(); _empty_cond.notify_one(); } @@ -91,8 +95,8 @@ namespace uhd{ namespace transport{ template UHD_INLINE bool push_with_timed_wait(const elem_type &elem, const time_type &time){ boost::unique_lock lock(_mutex); - if (not _full_cond.timed_wait(lock, time, boost::bind(&bounded_buffer::is_not_full, this))) return false; - _buffer.push_front(elem); ++_size; + if (not _full_cond.timed_wait(lock, time, boost::bind(&bounded_buffer::not_full, this))) return false; + _buffer.push_front(elem); lock.unlock(); _empty_cond.notify_one(); return true; @@ -105,8 +109,8 @@ namespace uhd{ namespace transport{ */ UHD_INLINE void pop_with_wait(elem_type &elem){ boost::unique_lock lock(_mutex); - _empty_cond.wait(lock, boost::bind(&bounded_buffer::is_not_empty, this)); - elem = _buffer[--_size]; + _empty_cond.wait(lock, boost::bind(&bounded_buffer::not_empty, this)); + elem = _buffer.back(); _buffer.pop_back(); lock.unlock(); _full_cond.notify_one(); } @@ -121,8 +125,8 @@ namespace uhd{ namespace transport{ template UHD_INLINE bool pop_with_timed_wait(elem_type &elem, const time_type &time){ boost::unique_lock lock(_mutex); - if (not _empty_cond.timed_wait(lock, time, boost::bind(&bounded_buffer::is_not_empty, this))) return false; - elem = _buffer[--_size]; + if (not _empty_cond.timed_wait(lock, time, boost::bind(&bounded_buffer::not_empty, this))) return false; + elem = _buffer.back(); _buffer.pop_back(); lock.unlock(); _full_cond.notify_one(); return true; @@ -130,10 +134,11 @@ namespace uhd{ namespace transport{ private: boost::mutex _mutex; - boost::condition_variable _empty_cond, _full_cond; + boost::condition _empty_cond, _full_cond; boost::circular_buffer _buffer; - size_t _size; + bool not_full(void) const{return not _buffer.full();} + bool not_empty(void) const{return not _buffer.empty();} }; }} //namespace diff --git a/host/test/bounded_buffer_test.cpp b/host/test/bounded_buffer_test.cpp index 5d6b6faec..dba1a4258 100644 --- a/host/test/bounded_buffer_test.cpp +++ b/host/test/bounded_buffer_test.cpp @@ -18,14 +18,11 @@ #include #include -//test #Include -#include - using namespace uhd::transport; static const boost::posix_time::milliseconds timeout(10); -BOOST_AUTO_TEST_CASE(test_bounded_buffer){ +BOOST_AUTO_TEST_CASE(test_bounded_buffer_with_timed_wait){ bounded_buffer::sptr bb(new bounded_buffer(3)); //push elements, check for timeout @@ -44,3 +41,77 @@ BOOST_AUTO_TEST_CASE(test_bounded_buffer){ BOOST_CHECK_EQUAL(val, 2); BOOST_CHECK(not bb->pop_with_timed_wait(val, timeout)); } + +BOOST_AUTO_TEST_CASE(test_bounded_buffer_with_pop_on_full){ + bounded_buffer::sptr bb(new bounded_buffer(3)); + + //push elements, check for timeout + BOOST_CHECK(bb->push_with_pop_on_full(0)); + BOOST_CHECK(bb->push_with_pop_on_full(1)); + BOOST_CHECK(bb->push_with_pop_on_full(2)); + BOOST_CHECK(not bb->push_with_pop_on_full(3)); + + int val; + //pop elements, check for timeout and check values + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 1); + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 2); + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 3); +} + +#include +#include + +using namespace boost::assign; + +BOOST_AUTO_TEST_CASE(test_alignment_buffer_tmp){ + alignment_buffer::sptr ab(new alignment_buffer(5, 3)); + //load index 0 with all good seq numbers + BOOST_CHECK(ab->push_with_pop_on_full(0, 0, 0)); + BOOST_CHECK(ab->push_with_pop_on_full(1, 1, 0)); + BOOST_CHECK(ab->push_with_pop_on_full(2, 2, 0)); + BOOST_CHECK(ab->push_with_pop_on_full(3, 3, 0)); + BOOST_CHECK(ab->push_with_pop_on_full(4, 4, 0)); + + //load index 1 with some skipped seq numbers + BOOST_CHECK(ab->push_with_pop_on_full(10, 0, 1)); + BOOST_CHECK(ab->push_with_pop_on_full(11, 1, 1)); + BOOST_CHECK(ab->push_with_pop_on_full(14, 4, 1)); + BOOST_CHECK(ab->push_with_pop_on_full(15, 5, 1)); + BOOST_CHECK(ab->push_with_pop_on_full(16, 6, 1)); + + //load index 2 with all good seq numbers + BOOST_CHECK(ab->push_with_pop_on_full(20, 0, 2)); + BOOST_CHECK(ab->push_with_pop_on_full(21, 1, 2)); + BOOST_CHECK(ab->push_with_pop_on_full(22, 2, 2)); + BOOST_CHECK(ab->push_with_pop_on_full(23, 3, 2)); + BOOST_CHECK(ab->push_with_pop_on_full(24, 4, 2)); + + //readback aligned values + std::vector aligned_elems(3); + + std::vector expected_elems0 = list_of(0)(10)(20); + BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); + BOOST_CHECK_EQUAL_COLLECTIONS( + aligned_elems.begin(), aligned_elems.end(), + expected_elems0.begin(), expected_elems0.end() + ); + + std::vector expected_elems1 = list_of(1)(11)(21); + BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); + BOOST_CHECK_EQUAL_COLLECTIONS( + aligned_elems.begin(), aligned_elems.end(), + expected_elems1.begin(), expected_elems1.end() + ); + + //there was a skip now find 4 + + std::vector expected_elems4 = list_of(4)(14)(24); + BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); + BOOST_CHECK_EQUAL_COLLECTIONS( + aligned_elems.begin(), aligned_elems.end(), + expected_elems4.begin(), expected_elems4.end() + ); +} -- cgit v1.2.3 From 8c0759df03520f010203fdeb3979f82fc41b72f7 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Thu, 27 May 2010 21:53:17 -0700 Subject: work on alignment buffer, got unit test working --- host/include/uhd/transport/alignment_buffer.hpp | 49 +++++----- host/include/uhd/transport/bounded_buffer.hpp | 16 ++-- host/test/CMakeLists.txt | 2 +- host/test/bounded_buffer_test.cpp | 117 ------------------------ host/test/buffer_test.cpp | 115 +++++++++++++++++++++++ 5 files changed, 145 insertions(+), 154 deletions(-) delete mode 100644 host/test/bounded_buffer_test.cpp create mode 100644 host/test/buffer_test.cpp (limited to 'host/include') diff --git a/host/include/uhd/transport/alignment_buffer.hpp b/host/include/uhd/transport/alignment_buffer.hpp index b33b80da9..dc6ccc3ed 100644 --- a/host/include/uhd/transport/alignment_buffer.hpp +++ b/host/include/uhd/transport/alignment_buffer.hpp @@ -36,22 +36,12 @@ namespace uhd{ namespace transport{ typedef boost::shared_ptr > sptr; /*! - * Create the alignment buffer. + * Make a new alignment buffer object. * \param capacity the maximum elements per index * \param width the number of elements to align */ - alignment_buffer(size_t capacity, size_t width){ - for (size_t i = 0; i < width; i++){ - _buffs.push_back(bounded_buffer_sptr(new bounded_buffer_type(capacity))); - _all_indexes.push_back(i); - } - } - - /*! - * Destroy this alignment buffer. - */ - ~alignment_buffer(void){ - /* NOP */ + static sptr make(size_t capacity, size_t width){ + return sptr(new alignment_buffer(capacity, width)); } /*! @@ -80,29 +70,25 @@ namespace uhd{ namespace transport{ buff_contents_type buff_contents_tmp; std::list indexes_to_do(_all_indexes); - //the seq identifier to align with - seq_type expected_seq_id = seq_type(); - bool expected_seq_id_valid = false; + //do an initial pop to load an initial sequence id + size_t index = indexes_to_do.front(); + if (not _buffs[index]->pop_with_timed_wait(buff_contents_tmp, time)) return false; + elems[index] = buff_contents_tmp.first; + seq_type expected_seq_id = buff_contents_tmp.second; + indexes_to_do.pop_front(); //get an aligned set of elements from the buffers: while(indexes_to_do.size() != 0){ - size_t index = indexes_to_do.back(); - //pop an element off for this index + index = indexes_to_do.front(); if (not _buffs[index]->pop_with_timed_wait(buff_contents_tmp, time)) return false; - //grab the current sequence id if not valid - if (not expected_seq_id_valid){ - expected_seq_id_valid = true; - expected_seq_id = buff_contents_tmp.second; - } - //if the sequence id matches: // store the popped element into the output, // remove this index from the list and continue if (buff_contents_tmp.second == expected_seq_id){ elems[index] = buff_contents_tmp.first; - indexes_to_do.pop_back(); + indexes_to_do.pop_front(); continue; } @@ -113,10 +99,13 @@ namespace uhd{ namespace transport{ } //if the sequence id is newer: - // start from scratch at the new sequence number + // store the popped element into the output, + // add all other indexes back into the list if (buff_contents_tmp.second > expected_seq_id){ + elems[index] = buff_contents_tmp.first; expected_seq_id = buff_contents_tmp.second; indexes_to_do = _all_indexes; + indexes_to_do.remove(index); continue; } } @@ -130,6 +119,14 @@ namespace uhd{ namespace transport{ typedef boost::shared_ptr bounded_buffer_sptr; std::vector _buffs; std::list _all_indexes; + + //private constructor + alignment_buffer(size_t capacity, size_t width){ + for (size_t i = 0; i < width; i++){ + _buffs.push_back(bounded_buffer_type::make(capacity)); + _all_indexes.push_back(i); + } + } }; }} //namespace diff --git a/host/include/uhd/transport/bounded_buffer.hpp b/host/include/uhd/transport/bounded_buffer.hpp index c50a626fb..cdec05849 100644 --- a/host/include/uhd/transport/bounded_buffer.hpp +++ b/host/include/uhd/transport/bounded_buffer.hpp @@ -38,18 +38,11 @@ namespace uhd{ namespace transport{ typedef boost::shared_ptr > sptr; /*! - * Create a new bounded_buffer of a given size. + * Make a new bounded buffer object. * \param capacity the bounded_buffer capacity */ - bounded_buffer(size_t capacity) : _buffer(capacity){ - /* NOP */ - } - - /*! - * Destroy this bounded_buffer. - */ - ~bounded_buffer(void){ - /* NOP */ + static sptr make(size_t capacity){ + return sptr(new bounded_buffer(capacity)); } /*! @@ -139,6 +132,9 @@ namespace uhd{ namespace transport{ bool not_full(void) const{return not _buffer.full();} bool not_empty(void) const{return not _buffer.empty();} + + //private constructor + bounded_buffer(size_t capacity) : _buffer(capacity){} }; }} //namespace diff --git a/host/test/CMakeLists.txt b/host/test/CMakeLists.txt index c7c6d7fad..24778d13e 100644 --- a/host/test/CMakeLists.txt +++ b/host/test/CMakeLists.txt @@ -21,7 +21,7 @@ ADD_EXECUTABLE(main_test main_test.cpp addr_test.cpp - bounded_buffer_test.cpp + buffer_test.cpp dict_test.cpp error_test.cpp gain_handler_test.cpp diff --git a/host/test/bounded_buffer_test.cpp b/host/test/bounded_buffer_test.cpp deleted file mode 100644 index dba1a4258..000000000 --- a/host/test/bounded_buffer_test.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program 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 of the License, or -// (at your option) any later version. -// -// This program 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, see . -// - -#include -#include - -using namespace uhd::transport; - -static const boost::posix_time::milliseconds timeout(10); - -BOOST_AUTO_TEST_CASE(test_bounded_buffer_with_timed_wait){ - bounded_buffer::sptr bb(new bounded_buffer(3)); - - //push elements, check for timeout - BOOST_CHECK(bb->push_with_timed_wait(0, timeout)); - BOOST_CHECK(bb->push_with_timed_wait(1, timeout)); - BOOST_CHECK(bb->push_with_timed_wait(2, timeout)); - BOOST_CHECK(not bb->push_with_timed_wait(3, timeout)); - - int val; - //pop elements, check for timeout and check values - BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); - BOOST_CHECK_EQUAL(val, 0); - BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); - BOOST_CHECK_EQUAL(val, 1); - BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); - BOOST_CHECK_EQUAL(val, 2); - BOOST_CHECK(not bb->pop_with_timed_wait(val, timeout)); -} - -BOOST_AUTO_TEST_CASE(test_bounded_buffer_with_pop_on_full){ - bounded_buffer::sptr bb(new bounded_buffer(3)); - - //push elements, check for timeout - BOOST_CHECK(bb->push_with_pop_on_full(0)); - BOOST_CHECK(bb->push_with_pop_on_full(1)); - BOOST_CHECK(bb->push_with_pop_on_full(2)); - BOOST_CHECK(not bb->push_with_pop_on_full(3)); - - int val; - //pop elements, check for timeout and check values - BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); - BOOST_CHECK_EQUAL(val, 1); - BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); - BOOST_CHECK_EQUAL(val, 2); - BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); - BOOST_CHECK_EQUAL(val, 3); -} - -#include -#include - -using namespace boost::assign; - -BOOST_AUTO_TEST_CASE(test_alignment_buffer_tmp){ - alignment_buffer::sptr ab(new alignment_buffer(5, 3)); - //load index 0 with all good seq numbers - BOOST_CHECK(ab->push_with_pop_on_full(0, 0, 0)); - BOOST_CHECK(ab->push_with_pop_on_full(1, 1, 0)); - BOOST_CHECK(ab->push_with_pop_on_full(2, 2, 0)); - BOOST_CHECK(ab->push_with_pop_on_full(3, 3, 0)); - BOOST_CHECK(ab->push_with_pop_on_full(4, 4, 0)); - - //load index 1 with some skipped seq numbers - BOOST_CHECK(ab->push_with_pop_on_full(10, 0, 1)); - BOOST_CHECK(ab->push_with_pop_on_full(11, 1, 1)); - BOOST_CHECK(ab->push_with_pop_on_full(14, 4, 1)); - BOOST_CHECK(ab->push_with_pop_on_full(15, 5, 1)); - BOOST_CHECK(ab->push_with_pop_on_full(16, 6, 1)); - - //load index 2 with all good seq numbers - BOOST_CHECK(ab->push_with_pop_on_full(20, 0, 2)); - BOOST_CHECK(ab->push_with_pop_on_full(21, 1, 2)); - BOOST_CHECK(ab->push_with_pop_on_full(22, 2, 2)); - BOOST_CHECK(ab->push_with_pop_on_full(23, 3, 2)); - BOOST_CHECK(ab->push_with_pop_on_full(24, 4, 2)); - - //readback aligned values - std::vector aligned_elems(3); - - std::vector expected_elems0 = list_of(0)(10)(20); - BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); - BOOST_CHECK_EQUAL_COLLECTIONS( - aligned_elems.begin(), aligned_elems.end(), - expected_elems0.begin(), expected_elems0.end() - ); - - std::vector expected_elems1 = list_of(1)(11)(21); - BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); - BOOST_CHECK_EQUAL_COLLECTIONS( - aligned_elems.begin(), aligned_elems.end(), - expected_elems1.begin(), expected_elems1.end() - ); - - //there was a skip now find 4 - - std::vector expected_elems4 = list_of(4)(14)(24); - BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); - BOOST_CHECK_EQUAL_COLLECTIONS( - aligned_elems.begin(), aligned_elems.end(), - expected_elems4.begin(), expected_elems4.end() - ); -} diff --git a/host/test/buffer_test.cpp b/host/test/buffer_test.cpp new file mode 100644 index 000000000..aadb3f951 --- /dev/null +++ b/host/test/buffer_test.cpp @@ -0,0 +1,115 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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 of the License, or +// (at your option) any later version. +// +// This program 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, see . +// + +#include +#include +#include +#include + +using namespace boost::assign; +using namespace uhd::transport; + +static const boost::posix_time::milliseconds timeout(10); + +BOOST_AUTO_TEST_CASE(test_bounded_buffer_with_timed_wait){ + bounded_buffer::sptr bb(bounded_buffer::make(3)); + + //push elements, check for timeout + BOOST_CHECK(bb->push_with_timed_wait(0, timeout)); + BOOST_CHECK(bb->push_with_timed_wait(1, timeout)); + BOOST_CHECK(bb->push_with_timed_wait(2, timeout)); + BOOST_CHECK(not bb->push_with_timed_wait(3, timeout)); + + int val; + //pop elements, check for timeout and check values + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 0); + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 1); + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 2); + BOOST_CHECK(not bb->pop_with_timed_wait(val, timeout)); +} + +BOOST_AUTO_TEST_CASE(test_bounded_buffer_with_pop_on_full){ + bounded_buffer::sptr bb(bounded_buffer::make(3)); + + //push elements, check for timeout + BOOST_CHECK(bb->push_with_pop_on_full(0)); + BOOST_CHECK(bb->push_with_pop_on_full(1)); + BOOST_CHECK(bb->push_with_pop_on_full(2)); + BOOST_CHECK(not bb->push_with_pop_on_full(3)); + + int val; + //pop elements, check for timeout and check values + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 1); + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 2); + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 3); +} + +BOOST_AUTO_TEST_CASE(test_alignment_buffer){ + alignment_buffer::sptr ab(alignment_buffer::make(7, 3)); + //load index 0 with all good seq numbers + BOOST_CHECK(ab->push_with_pop_on_full(0, 0, 0)); + BOOST_CHECK(ab->push_with_pop_on_full(1, 1, 0)); + BOOST_CHECK(ab->push_with_pop_on_full(2, 2, 0)); + BOOST_CHECK(ab->push_with_pop_on_full(3, 3, 0)); + BOOST_CHECK(ab->push_with_pop_on_full(4, 4, 0)); + + //load index 1 with some skipped seq numbers + BOOST_CHECK(ab->push_with_pop_on_full(10, 0, 1)); + BOOST_CHECK(ab->push_with_pop_on_full(11, 1, 1)); + BOOST_CHECK(ab->push_with_pop_on_full(14, 4, 1)); + BOOST_CHECK(ab->push_with_pop_on_full(15, 5, 1)); + BOOST_CHECK(ab->push_with_pop_on_full(16, 6, 1)); + + //load index 2 with all good seq numbers + BOOST_CHECK(ab->push_with_pop_on_full(20, 0, 2)); + BOOST_CHECK(ab->push_with_pop_on_full(21, 1, 2)); + BOOST_CHECK(ab->push_with_pop_on_full(22, 2, 2)); + BOOST_CHECK(ab->push_with_pop_on_full(23, 3, 2)); + BOOST_CHECK(ab->push_with_pop_on_full(24, 4, 2)); + + //readback aligned values + std::vector aligned_elems(3); + + static const std::vector expected_elems0 = list_of(0)(10)(20); + BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); + BOOST_CHECK_EQUAL_COLLECTIONS( + aligned_elems.begin(), aligned_elems.end(), + expected_elems0.begin(), expected_elems0.end() + ); + + static const std::vector expected_elems1 = list_of(1)(11)(21); + BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); + BOOST_CHECK_EQUAL_COLLECTIONS( + aligned_elems.begin(), aligned_elems.end(), + expected_elems1.begin(), expected_elems1.end() + ); + + //there was a skip now find 4 + + static const std::vector expected_elems4 = list_of(4)(14)(24); + BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); + BOOST_CHECK_EQUAL_COLLECTIONS( + aligned_elems.begin(), aligned_elems.end(), + expected_elems4.begin(), expected_elems4.end() + ); +} -- cgit v1.2.3 From 0b609f776494ecff50c7a2a4d9880d3186fe2421 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sat, 29 May 2010 00:50:29 -0700 Subject: added frame count call to zero-copy iface, tweaks for udp asio impl --- host/include/uhd/transport/zero_copy.hpp | 19 +++++++ host/lib/transport/udp_zero_copy_asio.cpp | 85 ++++++++++++++++++------------- 2 files changed, 68 insertions(+), 36 deletions(-) (limited to 'host/include') diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index 2efabaccf..d6eb89a91 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -123,10 +123,29 @@ namespace uhd{ namespace transport{ */ virtual managed_recv_buffer::sptr get_recv_buff(void) = 0; + /*! + * Get the maximum number of receive frames: + * The maximum number of valid managed recv buffers, + * or the maximum number of frames in the ring buffer, + * depending upon the underlying implementation. + * \return number of frames + */ + virtual size_t get_num_recv_frames(void) const = 0; + /*! * Get a new send buffer from this transport object. */ virtual managed_send_buffer::sptr get_send_buff(void) = 0; + + /*! + * Get the maximum number of send frames: + * The maximum number of valid managed send buffers, + * or the maximum number of frames in the ring buffer, + * depending upon the underlying implementation. + * \return number of frames + */ + virtual size_t get_num_send_frames(void) const = 0; + }; /*! diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index 0c604811a..4402437f3 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -46,12 +46,42 @@ class udp_zero_copy_impl: public: typedef boost::shared_ptr sptr; - //structors - udp_zero_copy_impl(const std::string &addr, const std::string &port); - ~udp_zero_copy_impl(void); + udp_zero_copy_impl( + const std::string &addr, + const std::string &port + ): + phony_zero_copy_recv_if(MAX_DGRAM_SIZE), + phony_zero_copy_send_if(MAX_DGRAM_SIZE) + { + //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + + // resolve the address + boost::asio::ip::udp::resolver resolver(_io_service); + boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); + boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + + // create, open, and connect the socket + _socket = new boost::asio::ip::udp::socket(_io_service); + _socket->open(boost::asio::ip::udp::v4()); + _socket->connect(receiver_endpoint); + + // set recv timeout + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = size_t(RECV_TIMEOUT*1e6); + UHD_ASSERT_THROW(setsockopt( + _socket->native(), + SOL_SOCKET, SO_RCVTIMEO, + (const char *)&tv, sizeof(timeval) + ) == 0); + } + + ~udp_zero_copy_impl(void){ + delete _socket; + } //get size for internal socket buffer - template size_t get_buff_size(void){ + template size_t get_buff_size(void) const{ Opt option; _socket->get_option(option); return option.value(); @@ -64,6 +94,20 @@ public: return get_buff_size(); } + + //The number of frames is approximately the buffer size divided by the max datagram size. + //In reality, this is a phony zero-copy interface and the number of frames is infinite. + //However, its sensible to advertise a frame count that is approximate to buffer size. + //This way, the transport caller will have an idea about how much buffering to create. + + size_t get_num_recv_frames(void) const{ + return this->get_buff_size()/MAX_DGRAM_SIZE; + } + + size_t get_num_send_frames(void) const{ + return this->get_buff_size()/MAX_DGRAM_SIZE; + } + private: boost::asio::ip::udp::socket *_socket; boost::asio::io_service _io_service; @@ -77,41 +121,10 @@ private: } }; -udp_zero_copy_impl::udp_zero_copy_impl(const std::string &addr, const std::string &port): - phony_zero_copy_recv_if(MAX_DGRAM_SIZE), - phony_zero_copy_send_if(MAX_DGRAM_SIZE) -{ - //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; - - // resolve the address - boost::asio::ip::udp::resolver resolver(_io_service); - boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); - boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); - - // create, open, and connect the socket - _socket = new boost::asio::ip::udp::socket(_io_service); - _socket->open(boost::asio::ip::udp::v4()); - _socket->connect(receiver_endpoint); - - // set recv timeout - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = size_t(RECV_TIMEOUT*1e6); - UHD_ASSERT_THROW(setsockopt( - _socket->native(), - SOL_SOCKET, SO_RCVTIMEO, - (const char *)&tv, sizeof(timeval) - ) == 0); -} - -udp_zero_copy_impl::~udp_zero_copy_impl(void){ - delete _socket; -} - /*********************************************************************** * UDP zero copy make function **********************************************************************/ -template static inline void resize_buff_helper( +template static void resize_buff_helper( udp_zero_copy_impl::sptr udp_trans, size_t target_size, const std::string &name -- cgit v1.2.3 From 212159ca3bc00d233464cd6f9f454e5ac6e08f88 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Tue, 1 Jun 2010 17:51:26 -0700 Subject: Implemented pirate thread, moved io impl details into io impl cpp file. Fixed bug in bounded buffer push with pop on full. --- host/include/uhd/transport/bounded_buffer.hpp | 4 ++ host/lib/transport/CMakeLists.txt | 1 - host/lib/transport/udp_zero_copy_asio.cpp | 2 +- host/lib/transport/vrt_packet_handler.hpp | 29 +++++++++- host/lib/transport/vrt_packet_handler_state.hpp | 56 ------------------- host/lib/usrp/usrp2/io_impl.cpp | 73 ++++++++++++++++++++++--- host/lib/usrp/usrp2/usrp2_impl.cpp | 3 +- host/lib/usrp/usrp2/usrp2_impl.hpp | 5 +- 8 files changed, 103 insertions(+), 70 deletions(-) delete mode 100644 host/lib/transport/vrt_packet_handler_state.hpp (limited to 'host/include') diff --git a/host/include/uhd/transport/bounded_buffer.hpp b/host/include/uhd/transport/bounded_buffer.hpp index cdec05849..baecd6382 100644 --- a/host/include/uhd/transport/bounded_buffer.hpp +++ b/host/include/uhd/transport/bounded_buffer.hpp @@ -57,10 +57,14 @@ namespace uhd{ namespace transport{ if(_buffer.full()){ _buffer.pop_back(); _buffer.push_front(elem); + lock.unlock(); + _empty_cond.notify_one(); return false; } else{ _buffer.push_front(elem); + lock.unlock(); + _empty_cond.notify_one(); return true; } } diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 32644a6c0..a74f7d527 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -50,6 +50,5 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_SOURCE_DIR}/lib/transport/udp_simple.cpp ${CMAKE_SOURCE_DIR}/lib/transport/udp_zero_copy_asio.cpp ${CMAKE_SOURCE_DIR}/lib/transport/vrt_packet_handler.hpp - ${CMAKE_SOURCE_DIR}/lib/transport/vrt_packet_handler_state.hpp ${CMAKE_SOURCE_DIR}/lib/transport/zero_copy.cpp ) diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index 4402437f3..190e3f9ce 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -28,7 +28,7 @@ using namespace uhd::transport; * Constants **********************************************************************/ static const size_t MIN_SOCK_BUFF_SIZE = size_t(100e3); -static const size_t MAX_DGRAM_SIZE = 2048; //assume max size on send and recv +static const size_t MAX_DGRAM_SIZE = 1500; //assume max size on send and recv static const double RECV_TIMEOUT = 0.1; // 100 ms /*********************************************************************** diff --git a/host/lib/transport/vrt_packet_handler.hpp b/host/lib/transport/vrt_packet_handler.hpp index a179731ca..d6b863040 100644 --- a/host/lib/transport/vrt_packet_handler.hpp +++ b/host/lib/transport/vrt_packet_handler.hpp @@ -18,7 +18,6 @@ #ifndef INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_HPP #define INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_HPP -#include "vrt_packet_handler_state.hpp" #include #include #include @@ -37,6 +36,24 @@ namespace vrt_packet_handler{ /*********************************************************************** * vrt packet handler for recv **********************************************************************/ + struct recv_state{ + //init the expected seq number + size_t next_packet_seq; + + //state variables to handle fragments + uhd::transport::managed_recv_buffer::sptr managed_buff; + boost::asio::const_buffer copy_buff; + size_t fragment_offset_in_samps; + + recv_state(void){ + //first expected seq is zero + next_packet_seq = 0; + + //initially empty copy buffer + copy_buff = boost::asio::buffer("", 0); + } + }; + typedef boost::function get_recv_buff_t; typedef boost::function recv_cb_t; @@ -106,6 +123,7 @@ namespace vrt_packet_handler{ if (boost::asio::buffer_size(state.copy_buff) == 0){ state.fragment_offset_in_samps = 0; state.managed_buff = get_recv_buff(); + if (state.managed_buff.get() == NULL) return 0; recv_cb(state.managed_buff); //callback before vrt unpack try{ _recv1_helper( @@ -210,6 +228,15 @@ namespace vrt_packet_handler{ /*********************************************************************** * vrt packet handler for send **********************************************************************/ + struct send_state{ + //init the expected seq number + size_t next_packet_seq; + + send_state(void){ + next_packet_seq = 0; + } + }; + typedef boost::function get_send_buff_t; typedef boost::function send_cb_t; diff --git a/host/lib/transport/vrt_packet_handler_state.hpp b/host/lib/transport/vrt_packet_handler_state.hpp deleted file mode 100644 index 2320a3b8e..000000000 --- a/host/lib/transport/vrt_packet_handler_state.hpp +++ /dev/null @@ -1,56 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program 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 of the License, or -// (at your option) any later version. -// -// This program 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, see . -// - -#ifndef INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_STATE_HPP -#define INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_STATE_HPP - -#include -#include -#include - -namespace vrt_packet_handler{ - - struct recv_state{ - //init the expected seq number - size_t next_packet_seq; - - //state variables to handle fragments - uhd::transport::managed_recv_buffer::sptr managed_buff; - boost::asio::const_buffer copy_buff; - size_t fragment_offset_in_samps; - - recv_state(void){ - //first expected seq is zero - next_packet_seq = 0; - - //initially empty copy buffer - copy_buff = boost::asio::buffer("", 0); - } - }; - - struct send_state{ - //init the expected seq number - size_t next_packet_seq; - - send_state(void){ - next_packet_seq = 0; - } - }; - -} //namespace vrt_packet_handler - -#endif /* INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_STATE_HPP */ diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 5a082bf13..efd64d4ab 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -19,9 +19,11 @@ #include "usrp2_impl.hpp" #include "usrp2_regs.hpp" #include +#include #include #include //htonl and ntohl #include +#include #include using namespace uhd; @@ -29,6 +31,60 @@ using namespace uhd::usrp; using namespace uhd::transport; namespace asio = boost::asio; +/*********************************************************************** + * io impl details (internal to this file) + **********************************************************************/ +struct usrp2_impl::io_impl{ + + io_impl(zero_copy_if::sptr zc_if); + ~io_impl(void); + + managed_recv_buffer::sptr get_recv_buff(void); + + //state management for the vrt packet handler code + vrt_packet_handler::recv_state packet_handler_recv_state; + vrt_packet_handler::send_state packet_handler_send_state; + + //methods and variables for the recv pirate + void recv_pirate_loop(zero_copy_if::sptr zc_if); + boost::thread *recv_pirate_thread; bool recv_pirate_running; + bounded_buffer::sptr recv_pirate_booty; +}; + +usrp2_impl::io_impl::io_impl(zero_copy_if::sptr zc_if){ + //create a large enough booty + size_t num_frames = zc_if->get_num_recv_frames(); + std::cout << "Recv pirate num frames: " << num_frames << std::endl; + recv_pirate_booty = bounded_buffer::make(num_frames); + + //create a new pirate thread (yarr!!) + recv_pirate_thread = new boost::thread( + boost::bind(&usrp2_impl::io_impl::recv_pirate_loop, this, zc_if) + ); +} + +usrp2_impl::io_impl::~io_impl(void){ + recv_pirate_running = false; + recv_pirate_thread->interrupt(); + recv_pirate_thread->join(); + delete recv_pirate_thread; +} + +managed_recv_buffer::sptr usrp2_impl::io_impl::get_recv_buff(void){ + managed_recv_buffer::sptr buff; + recv_pirate_booty->pop_with_timed_wait(buff, boost::posix_time::milliseconds(100)); + //timeout means a null sptr... + return buff; +} + +void usrp2_impl::io_impl::recv_pirate_loop(zero_copy_if::sptr zc_if){ + recv_pirate_running = true; + while(recv_pirate_running){ + managed_recv_buffer::sptr buff = zc_if->get_recv_buff(); + if (buff->size()) recv_pirate_booty->push_with_pop_on_full(buff); + } +} + /*********************************************************************** * Helper Functions **********************************************************************/ @@ -62,6 +118,13 @@ void usrp2_impl::io_init(void){ ); _iface->poke32(FR_RX_CTRL_VRT_STREAM_ID, 0); _iface->poke32(FR_RX_CTRL_VRT_TRAILER, 0); + + //create new io impl + _io_impl = new io_impl(_data_transport); +} + +void usrp2_impl::io_done(void){ + delete _io_impl; } /*********************************************************************** @@ -78,7 +141,7 @@ size_t usrp2_impl::send( send_mode_t send_mode ){ return vrt_packet_handler::send( - _packet_handler_send_state, //last state of the send handler + _io_impl->packet_handler_send_state, //last state of the send handler buff, metadata, send_mode, //buffer to empty and samples metadata io_type, _tx_otw_type, //input and output types to convert get_master_clock_freq(), //master clock tick rate @@ -90,10 +153,6 @@ size_t usrp2_impl::send( /*********************************************************************** * Receive Data **********************************************************************/ -static inline managed_recv_buffer::sptr get_recv_buff(zero_copy_if::sptr zc_if){ - return zc_if->get_recv_buff(); -} - size_t usrp2_impl::recv( const asio::mutable_buffer &buff, rx_metadata_t &metadata, @@ -101,10 +160,10 @@ size_t usrp2_impl::recv( recv_mode_t recv_mode ){ return vrt_packet_handler::recv( - _packet_handler_recv_state, //last state of the recv handler + _io_impl->packet_handler_recv_state, //last state of the recv handler buff, metadata, recv_mode, //buffer to fill and samples metadata io_type, _rx_otw_type, //input and output types to convert get_master_clock_freq(), //master clock tick rate - boost::bind(get_recv_buff, _data_transport) + boost::bind(&usrp2_impl::io_impl::get_recv_buff, _io_impl) ); } diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index af3ec216a..7f79c483b 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -185,7 +185,8 @@ usrp2_impl::usrp2_impl( } usrp2_impl::~usrp2_impl(void){ - /* NOP */ + //cleanup the send and recv io + io_done(); } /*********************************************************************** diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 6a2a09349..4b6805217 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -33,7 +33,6 @@ #include #include #include -#include "../../transport/vrt_packet_handler_state.hpp" /*! * Make a usrp2 dboard interface. @@ -153,10 +152,10 @@ private: uhd::transport::vrt::max_header_words32*sizeof(boost::uint32_t) ; - vrt_packet_handler::recv_state _packet_handler_recv_state; - vrt_packet_handler::send_state _packet_handler_send_state; uhd::otw_type_t _rx_otw_type, _tx_otw_type; + struct io_impl; io_impl *_io_impl; void io_init(void); + void io_done(void); //udp transports for control and data uhd::transport::udp_zero_copy::sptr _data_transport; -- cgit v1.2.3 From 906b93b28c59b3b704d47c617eabd1b4448fc4e5 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Wed, 2 Jun 2010 14:31:11 -0700 Subject: removed some windows warnings --- host/include/uhd/config.hpp | 3 ++- host/lib/transport/udp_zero_copy_asio.cpp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'host/include') diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp index fea95145c..b23a4dc00 100644 --- a/host/include/uhd/config.hpp +++ b/host/include/uhd/config.hpp @@ -31,10 +31,11 @@ # pragma warning(disable: 4251) // class 'A' needs to have dll-interface to be used by clients of class 'B' //# pragma warning(disable: 4127) // conditional expression is constant //# pragma warning(disable: 4290) // C++ exception specification ignored except to ... -//# pragma warning(disable: 4180) // qualifier applied to function type has no meaning; ignored +# pragma warning(disable: 4180) // qualifier applied to function type has no meaning; ignored # pragma warning(disable: 4275) // non dll-interface class ... used as base for dll-interface class ... //# pragma warning(disable: 4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data //# pragma warning(disable: 4511) // 'class' : copy constructor could not be generated +# pragma warning(disable: 4250) // 'class' : inherits 'method' via dominance #endif //define logical operators diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index 830bfd75e..ced606777 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -39,9 +39,9 @@ static const double RECV_TIMEOUT = 0.1; //100 ms * send and recv requires a copy operation to/from userspace. **********************************************************************/ class udp_zero_copy_impl: - public virtual phony_zero_copy_recv_if, - public virtual phony_zero_copy_send_if, - public virtual udp_zero_copy + public phony_zero_copy_recv_if, + public phony_zero_copy_send_if, + public udp_zero_copy { public: typedef boost::shared_ptr sptr; -- cgit v1.2.3 From 33193a06c1bc197a603f831109533230dc14b4c9 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Wed, 2 Jun 2010 15:49:53 -0700 Subject: replaced the assert falses with an invalid code path exception --- host/include/uhd/utils/exception.hpp | 7 +++++++ host/lib/usrp/dboard/db_rfx.cpp | 4 ++-- host/lib/usrp/dboard/db_wbx.cpp | 4 ++-- host/lib/usrp/dboard/db_xcvr2450.cpp | 6 +++--- host/lib/usrp/dboard_manager.cpp | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) (limited to 'host/include') diff --git a/host/include/uhd/utils/exception.hpp b/host/include/uhd/utils/exception.hpp index 40e81fae0..e74c19b9c 100644 --- a/host/include/uhd/utils/exception.hpp +++ b/host/include/uhd/utils/exception.hpp @@ -35,4 +35,11 @@ " at " + std::string(__FILE__) + ":" + BOOST_STRINGIZE(__LINE__) + "\n" \ ) +/*! + * Throws an invalid code path exception with throw-site information. + * Use this macro in places that code execution is not supposed to go. + */ +#define UHD_THROW_INVALID_CODE_PATH() \ + throw std::runtime_error(UHD_THROW_SITE_INFO("invalid code path")) + #endif /* INCLUDED_UHD_UTILS_EXCEPTION_HPP */ diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index 5b90bbbad..17fc00d24 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -247,7 +247,7 @@ static float rx_pga0_gain_to_dac_volts(float &gain){ void rfx_xcvr::set_tx_gain(float, const std::string &name){ assert_has(rfx_tx_gain_ranges.keys(), name, "rfx tx gain name"); - UHD_ASSERT_THROW(false); //no gains to set + UHD_THROW_INVALID_CODE_PATH(); //no gains to set } void rfx_xcvr::set_rx_gain(float gain, const std::string &name){ @@ -259,7 +259,7 @@ void rfx_xcvr::set_rx_gain(float gain, const std::string &name){ //write the new voltage to the aux dac this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, 1, dac_volts); } - else UHD_ASSERT_THROW(false); + else UHD_THROW_INVALID_CODE_PATH(); } /*********************************************************************** diff --git a/host/lib/usrp/dboard/db_wbx.cpp b/host/lib/usrp/dboard/db_wbx.cpp index 4cf168c6b..95dcb3802 100644 --- a/host/lib/usrp/dboard/db_wbx.cpp +++ b/host/lib/usrp/dboard/db_wbx.cpp @@ -248,7 +248,7 @@ void wbx_xcvr::set_tx_gain(float gain, const std::string &name){ //write the new voltage to the aux dac this->get_iface()->write_aux_dac(dboard_iface::UNIT_TX, 0, dac_volts); } - else UHD_ASSERT_THROW(false); + else UHD_THROW_INVALID_CODE_PATH(); } void wbx_xcvr::set_rx_gain(float gain, const std::string &name){ @@ -260,7 +260,7 @@ void wbx_xcvr::set_rx_gain(float gain, const std::string &name){ //write the new gain to atr regs update_atr(); } - else UHD_ASSERT_THROW(false); + else UHD_THROW_INVALID_CODE_PATH(); } /*********************************************************************** diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index d4d5f184e..974a378bd 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -375,7 +375,7 @@ static max2829_regs_t::tx_baseband_gain_t gain_to_tx_bb_reg(float &gain){ gain = 5; return max2829_regs_t::TX_BASEBAND_GAIN_5DB; } - UHD_ASSERT_THROW(false); + UHD_THROW_INVALID_CODE_PATH(); } /*! @@ -417,7 +417,7 @@ void xcvr2450::set_tx_gain(float gain, const std::string &name){ _max2829_regs.tx_baseband_gain = gain_to_tx_bb_reg(gain); send_reg(0x9); } - else UHD_ASSERT_THROW(false); + else UHD_THROW_INVALID_CODE_PATH(); _tx_gains[name] = gain; } @@ -431,7 +431,7 @@ void xcvr2450::set_rx_gain(float gain, const std::string &name){ _max2829_regs.rx_lna_gain = gain_to_rx_lna_reg(gain); send_reg(0xB); } - else UHD_ASSERT_THROW(false); + else UHD_THROW_INVALID_CODE_PATH(); _rx_gains[name] = gain; } diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index 01352039e..35ddfc4ee 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -181,7 +181,7 @@ static args_t get_dboard_args( switch(unit){ case dboard_iface::UNIT_RX: return get_dboard_args(unit, 0x0001); case dboard_iface::UNIT_TX: return get_dboard_args(unit, 0x0000); - default: UHD_ASSERT_THROW(false); + default: UHD_THROW_INVALID_CODE_PATH(); } } -- cgit v1.2.3 From b2054a45d45ba85e30ff601159b18f5ebd15dd76 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Wed, 2 Jun 2010 17:44:20 -0700 Subject: Created macros for dealing with pimpls and implemented in code. --- host/include/uhd/transport/zero_copy.hpp | 27 ++++++++++------ host/include/uhd/usrp/dboard_base.hpp | 4 +-- host/include/uhd/utils/CMakeLists.txt | 1 + host/include/uhd/utils/pimpl.hpp | 55 ++++++++++++++++++++++++++++++++ host/lib/transport/zero_copy.cpp | 7 ++-- host/lib/usrp/dboard_base.cpp | 8 ++--- host/lib/usrp/usrp2/io_impl.cpp | 2 +- host/lib/usrp/usrp2/usrp2_impl.hpp | 3 +- 8 files changed, 85 insertions(+), 22 deletions(-) create mode 100644 host/include/uhd/utils/pimpl.hpp (limited to 'host/include') diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index d6eb89a91..2815e3189 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -19,6 +19,7 @@ #define INCLUDED_UHD_TRANSPORT_ZERO_COPY_HPP #include +#include #include #include #include @@ -156,10 +157,14 @@ namespace uhd{ namespace transport{ */ class UHD_API phony_zero_copy_recv_if : public virtual zero_copy_if{ public: - - //! structors + /*! + * Create a phony zero copy recv interface. + * \param max_buff_size max buffer size in bytes + */ phony_zero_copy_recv_if(size_t max_buff_size); - ~phony_zero_copy_recv_if(void); + + //! destructor + virtual ~phony_zero_copy_recv_if(void); /*! * Get a new receive buffer from this transport object. @@ -167,7 +172,6 @@ namespace uhd{ namespace transport{ managed_recv_buffer::sptr get_recv_buff(void); private: - /*! * Perform a private copying recv. * \param buff the buffer to write data into @@ -175,7 +179,7 @@ namespace uhd{ namespace transport{ */ virtual size_t recv(const boost::asio::mutable_buffer &buff) = 0; - struct impl; impl *_impl; //private implementation details + UHD_PIMPL_DECL(impl) _impl; }; /*! @@ -186,10 +190,14 @@ namespace uhd{ namespace transport{ */ class UHD_API phony_zero_copy_send_if : public virtual zero_copy_if{ public: - - //! structors + /*! + * Create a phony zero copy send interface. + * \param max_buff_size max buffer size in bytes + */ phony_zero_copy_send_if(size_t max_buff_size); - ~phony_zero_copy_send_if(void); + + //! destructor + virtual ~phony_zero_copy_send_if(void); /*! * Get a new send buffer from this transport object. @@ -197,7 +205,6 @@ namespace uhd{ namespace transport{ managed_send_buffer::sptr get_send_buff(void); private: - /*! * Perform a private copying send. * \param buff the buffer to read data from @@ -205,7 +212,7 @@ namespace uhd{ namespace transport{ */ virtual size_t send(const boost::asio::const_buffer &buff) = 0; - struct impl; impl *_impl; //private implementation details + UHD_PIMPL_DECL(impl) _impl; }; }} //namespace diff --git a/host/include/uhd/usrp/dboard_base.hpp b/host/include/uhd/usrp/dboard_base.hpp index 28bf2ae66..e88d39876 100644 --- a/host/include/uhd/usrp/dboard_base.hpp +++ b/host/include/uhd/usrp/dboard_base.hpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -58,8 +59,7 @@ protected: dboard_id_t get_tx_id(void); private: - struct dboard_base_impl; - dboard_base_impl *_impl; + UHD_PIMPL_DECL(impl) _impl; }; /*! diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index f588c6310..391e684c8 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -20,6 +20,7 @@ INSTALL(FILES assert.hpp exception.hpp gain_handler.hpp + pimpl.hpp props.hpp safe_main.hpp static.hpp diff --git a/host/include/uhd/utils/pimpl.hpp b/host/include/uhd/utils/pimpl.hpp new file mode 100644 index 000000000..09bf0c0a2 --- /dev/null +++ b/host/include/uhd/utils/pimpl.hpp @@ -0,0 +1,55 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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 of the License, or +// (at your option) any later version. +// +// This program 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, see . +// + +#ifndef INCLUDED_UHD_UTILS_PIMPL_HPP +#define INCLUDED_UHD_UTILS_PIMPL_HPP + +#include +#include + +/*! \file pimpl.hpp + * "Pimpl idiom" (pointer to implementation idiom). + * The UHD_PIMPL_* macros simplify code overhead for declaring and making pimpls. + * + * Each pimpl is implemented as a shared pointer to the implementation: + * - The container class will not have to deallocate the pimpl. + * - The container class will use the pimpl as a regular pointer. + * - Usage: _impl->method(arg0, arg1) + * - Usage: _impl->member = value; + * + * \see http://en.wikipedia.org/wiki/Opaque_pointer + */ + +/*! + * Make a declaration for a pimpl in a header file. + * - Usage: UHD_PIMPL_DECL(impl) _impl; + * \param _name the name of the pimpl class + */ +#define UHD_PIMPL_DECL(_name) \ + struct _name; boost::shared_ptr<_name> + +/*! + * Make an instance of a pimpl in a source file. + * - Usage: _impl = UHD_PIMPL_MAKE(impl, ()); + * - Usage: _impl = UHD_PIMPL_MAKE(impl, (a0, a1)); + * \param _name the name of the pimpl class + * \param _args the constructor args for the pimpl + */ +#define UHD_PIMPL_MAKE(_name, _args) \ + boost::shared_ptr<_name>(new _name _args) + +#endif /* INCLUDED_UHD_UTILS_PIMPL_HPP */ diff --git a/host/lib/transport/zero_copy.cpp b/host/lib/transport/zero_copy.cpp index f69fd2774..27f41329b 100644 --- a/host/lib/transport/zero_copy.cpp +++ b/host/lib/transport/zero_copy.cpp @@ -58,12 +58,12 @@ struct phony_zero_copy_recv_if::impl{ }; phony_zero_copy_recv_if::phony_zero_copy_recv_if(size_t max_buff_size){ - _impl = new impl; + _impl = UHD_PIMPL_MAKE(impl, ()); _impl->max_buff_size = max_buff_size; } phony_zero_copy_recv_if::~phony_zero_copy_recv_if(void){ - delete _impl; + /* NOP */ } managed_recv_buffer::sptr phony_zero_copy_recv_if::get_recv_buff(void){ @@ -122,7 +122,7 @@ struct phony_zero_copy_send_if::impl{ }; phony_zero_copy_send_if::phony_zero_copy_send_if(size_t max_buff_size){ - _impl = new impl; + _impl = UHD_PIMPL_MAKE(impl, ()); _impl->send_mem = new boost::uint8_t[max_buff_size]; _impl->send_buff = managed_send_buffer::sptr(new managed_send_buffer_impl( boost::asio::buffer(_impl->send_mem, max_buff_size), @@ -132,7 +132,6 @@ phony_zero_copy_send_if::phony_zero_copy_send_if(size_t max_buff_size){ phony_zero_copy_send_if::~phony_zero_copy_send_if(void){ delete [] _impl->send_mem; - delete _impl; } managed_send_buffer::sptr phony_zero_copy_send_if::get_send_buff(void){ diff --git a/host/lib/usrp/dboard_base.cpp b/host/lib/usrp/dboard_base.cpp index bd4b37ef3..eafb8897f 100644 --- a/host/lib/usrp/dboard_base.cpp +++ b/host/lib/usrp/dboard_base.cpp @@ -25,17 +25,17 @@ using namespace uhd::usrp; /*********************************************************************** * dboard_base dboard dboard_base class **********************************************************************/ -struct dboard_base::dboard_base_impl{ +struct dboard_base::impl{ ctor_args_impl args; - dboard_base_impl(ctor_args_t args) : args(*args){} + impl(ctor_args_t args) : args(*args){} }; dboard_base::dboard_base(ctor_args_t args){ - _impl = new dboard_base_impl(args); + _impl = UHD_PIMPL_MAKE(impl, (args)); } dboard_base::~dboard_base(void){ - delete _impl; + /* NOP */ } std::string dboard_base::get_subdev_name(void){ diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 5d425bd8a..18f2d013f 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -122,7 +122,7 @@ void usrp2_impl::io_init(void){ std::cout << "TX samples per packet: " << get_max_send_samps_per_packet() << std::endl; //create new io impl - _io_impl = boost::shared_ptr(new io_impl(_data_transport)); + _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport)); } /*********************************************************************** diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index bb4554e8d..40c193866 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -23,6 +23,7 @@ #include "codec_ctrl.hpp" #include "serdes_ctrl.hpp" #include +#include #include #include #include @@ -153,7 +154,7 @@ private: ; uhd::otw_type_t _rx_otw_type, _tx_otw_type; - struct io_impl; boost::shared_ptr _io_impl; + UHD_PIMPL_DECL(io_impl) _io_impl; void io_init(void); //udp transports for control and data -- cgit v1.2.3