diff options
Diffstat (limited to 'host/lib/transport/udp_zero_copy_asio.cpp')
-rw-r--r-- | host/lib/transport/udp_zero_copy_asio.cpp | 144 |
1 files changed, 77 insertions, 67 deletions
diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index ee44803f4..f8a222475 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -16,75 +16,69 @@ // #include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/utils/assert.hpp> #include <boost/cstdint.hpp> #include <boost/asio.hpp> -#include <boost/thread.hpp> #include <boost/format.hpp> #include <iostream> 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 double RECV_TIMEOUT = 0.1; // 100 ms + +/*********************************************************************** * Managed receive buffer implementation for udp zero-copy asio: - * Frees the memory held by the const buffer on done. **********************************************************************/ class managed_recv_buffer_impl : public managed_recv_buffer{ public: managed_recv_buffer_impl(const boost::asio::const_buffer &buff) : _buff(buff){ - _done = false; + /* NOP */ } ~managed_recv_buffer_impl(void){ - if (not _done) this->done(); - } - - void done(void){ - _done = true; - delete [] boost::asio::buffer_cast<const boost::uint32_t *>(_buff); + delete [] this->cast<const boost::uint8_t *>(); } private: - const boost::asio::const_buffer &get(void){ + const boost::asio::const_buffer &get(void) const{ return _buff; } const boost::asio::const_buffer _buff; - bool _done; }; /*********************************************************************** * Managed send buffer implementation for udp zero-copy asio: - * Sends and frees the memory held by the mutable buffer on done. **********************************************************************/ class managed_send_buffer_impl : public managed_send_buffer{ public: managed_send_buffer_impl( const boost::asio::mutable_buffer &buff, boost::asio::ip::udp::socket *socket - ) : _buff(buff){ - _done = false; - _socket = socket; + ) : _buff(buff), _socket(socket){ + /* NOP */ } ~managed_send_buffer_impl(void){ - if (not _done) this->done(0); + /* NOP */ } - void done(size_t num_bytes){ - _done = true; - boost::uint32_t *mem = boost::asio::buffer_cast<boost::uint32_t *>(_buff); - _socket->send(boost::asio::buffer(mem, num_bytes)); - delete [] mem; + void commit(size_t num_bytes){ + _socket->send(boost::asio::buffer(_buff, num_bytes)); } private: - const boost::asio::mutable_buffer &get(void){ + const boost::asio::mutable_buffer &get(void) const{ return _buff; } const boost::asio::mutable_buffer _buff; boost::asio::ip::udp::socket *_socket; - bool _done; }; /*********************************************************************** @@ -96,6 +90,8 @@ private: **********************************************************************/ class udp_zero_copy_impl : public udp_zero_copy{ public: + typedef boost::shared_ptr<udp_zero_copy_impl> sptr; + //structors udp_zero_copy_impl(const std::string &addr, const std::string &port); ~udp_zero_copy_impl(void); @@ -104,23 +100,27 @@ public: managed_recv_buffer::sptr get_recv_buff(void); managed_send_buffer::sptr get_send_buff(void); - //resize - size_t resize_recv_buff(size_t num_bytes){ - boost::asio::socket_base::receive_buffer_size option(num_bytes); - _socket->set_option(option); + //manage buffer + template <typename Opt> size_t get_buff_size(void){ + Opt option; _socket->get_option(option); return option.value(); } - size_t resize_send_buff(size_t num_bytes){ - boost::asio::socket_base::send_buffer_size option(num_bytes); + + template <typename Opt> size_t resize_buff(size_t num_bytes){ + Opt option(num_bytes); _socket->set_option(option); - _socket->get_option(option); - return option.value(); + return get_buff_size<Opt>(); } private: boost::asio::ip::udp::socket *_socket; boost::asio::io_service _io_service; + + //send and recv buffer memory (allocated once) + boost::uint8_t _send_mem[MIN_SOCK_BUFF_SIZE]; + + managed_send_buffer::sptr _send_buff; }; udp_zero_copy_impl::udp_zero_copy_impl(const std::string &addr, const std::string &port){ @@ -131,10 +131,25 @@ udp_zero_copy_impl::udp_zero_copy_impl(const std::string &addr, const std::strin 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 + // 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); + + // create the managed send buff (just once) + _send_buff = managed_send_buffer::sptr(new managed_send_buffer_impl( + boost::asio::buffer(_send_mem, MIN_SOCK_BUFF_SIZE), _socket + )); + + // 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){ @@ -142,61 +157,56 @@ udp_zero_copy_impl::~udp_zero_copy_impl(void){ } managed_recv_buffer::sptr udp_zero_copy_impl::get_recv_buff(void){ - //implement timeout through polling and sleeping - size_t available = 0; - boost::asio::deadline_timer timer(_socket->get_io_service()); - timer.expires_from_now(boost::posix_time::milliseconds(100)); - while (not ((available = _socket->available()) or timer.expires_from_now().is_negative())){ - boost::this_thread::sleep(boost::posix_time::milliseconds(1)); - } + //allocate memory + boost::uint8_t *recv_mem = new boost::uint8_t[MAX_DGRAM_SIZE]; - //receive only if data is available - boost::uint32_t *buff_mem = new boost::uint32_t[available/sizeof(boost::uint32_t)]; - if (available){ - available = _socket->receive(boost::asio::buffer(buff_mem, available)); - } + //call recv() with timeout option + size_t num_bytes = _socket->receive(boost::asio::buffer(recv_mem, MIN_SOCK_BUFF_SIZE)); //create a new managed buffer to house the data return managed_recv_buffer::sptr( - new managed_recv_buffer_impl(boost::asio::buffer(buff_mem, available)) + new managed_recv_buffer_impl(boost::asio::buffer(recv_mem, num_bytes)) ); } managed_send_buffer::sptr udp_zero_copy_impl::get_send_buff(void){ - boost::uint32_t *buff_mem = new boost::uint32_t[2000/sizeof(boost::uint32_t)]; - return managed_send_buffer::sptr( - new managed_send_buffer_impl(boost::asio::buffer(buff_mem, 2000), _socket) - ); + return _send_buff; //FIXME there is only ever one send buff, we assume that the caller doesnt hang onto these } /*********************************************************************** * UDP zero copy make function **********************************************************************/ +template<typename Opt> static inline void resize_buff_helper( + udp_zero_copy_impl::sptr udp_trans, + size_t target_size, + const std::string &name +){ + //resize the buffer if size was provided + if (target_size > 0){ + size_t actual_size = udp_trans->resize_buff<Opt>(target_size); + if (target_size != actual_size) std::cout << boost::format( + "Target %s buffer size: %d\n" + "Actual %s byffer size: %d" + ) % name % target_size % name % actual_size << std::endl; + } + + //otherwise, ensure that the buffer is at least the minimum size + else if (udp_trans->get_buff_size<Opt>() < MIN_SOCK_BUFF_SIZE){ + resize_buff_helper<Opt>(udp_trans, MIN_SOCK_BUFF_SIZE, name); + } +} + udp_zero_copy::sptr udp_zero_copy::make( const std::string &addr, const std::string &port, size_t recv_buff_size, size_t send_buff_size ){ - boost::shared_ptr<udp_zero_copy_impl> udp_trans(new udp_zero_copy_impl(addr, port)); - - //resize the recv buffer if size was provided - if (recv_buff_size > 0){ - size_t actual_bytes = udp_trans->resize_recv_buff(recv_buff_size); - if (recv_buff_size != actual_bytes) std::cout << boost::format( - "Target recv buffer size: %d\n" - "Actual recv byffer size: %d" - ) % recv_buff_size % actual_bytes << std::endl; - } + udp_zero_copy_impl::sptr udp_trans(new udp_zero_copy_impl(addr, port)); - //resize the send buffer if size was provided - if (send_buff_size > 0){ - size_t actual_bytes = udp_trans->resize_send_buff(send_buff_size); - if (send_buff_size != actual_bytes) std::cout << boost::format( - "Target send buffer size: %d\n" - "Actual send byffer size: %d" - ) % send_buff_size % actual_bytes << std::endl; - } + //call the helper to resize send and recv buffers + resize_buff_helper<boost::asio::socket_base::receive_buffer_size>(udp_trans, recv_buff_size, "recv"); + resize_buff_helper<boost::asio::socket_base::send_buffer_size> (udp_trans, send_buff_size, "send"); return udp_trans; } |