aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/transport
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/transport')
-rw-r--r--host/lib/transport/CMakeLists.txt2
-rw-r--r--host/lib/transport/libusb1_base.cpp16
-rw-r--r--host/lib/transport/libusb1_zero_copy.cpp212
-rw-r--r--host/lib/transport/udp_zero_copy_asio.cpp209
-rw-r--r--host/lib/transport/vrt_packet_handler.hpp53
-rw-r--r--host/lib/transport/zero_copy.cpp121
6 files changed, 293 insertions, 320 deletions
diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt
index e9e82932c..eafdd57d2 100644
--- a/host/lib/transport/CMakeLists.txt
+++ b/host/lib/transport/CMakeLists.txt
@@ -36,6 +36,8 @@ IF(LIBUSB_FOUND)
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/transport/msvc)
ENDIF(MSVC)
SET(HAVE_USB_SUPPORT TRUE)
+ELSE(LIBUSB_FOUND)
+ #TODO dummy usb
ENDIF(LIBUSB_FOUND)
IF(HAVE_USB_SUPPORT)
diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp
index 49f524a32..910b04fc8 100644
--- a/host/lib/transport/libusb1_base.cpp
+++ b/host/lib/transport/libusb1_base.cpp
@@ -213,15 +213,21 @@ private:
libusb::device_handle::sptr libusb::device_handle::get_cached_handle(device::sptr dev){
static uhd::dict<libusb_device *, boost::weak_ptr<device_handle> > handles;
- //not expired -> get existing session
+ //not expired -> get existing handle
if (handles.has_key(dev->get()) and not handles[dev->get()].expired()){
return handles[dev->get()].lock();
}
- //create a new global session
- sptr new_handle(new libusb_device_handle_impl(dev));
- handles[dev->get()] = new_handle;
- return new_handle;
+ //create a new cached handle
+ try{
+ sptr new_handle(new libusb_device_handle_impl(dev));
+ handles[dev->get()] = new_handle;
+ return new_handle;
+ }
+ catch(const std::exception &e){
+ std::cerr << "USB open failed: see the application notes for your device." << std::endl;
+ throw std::runtime_error(e.what());
+ }
}
/***********************************************************************
diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp
index f9beb0b4c..ab48e4fc4 100644
--- a/host/lib/transport/libusb1_zero_copy.cpp
+++ b/host/lib/transport/libusb1_zero_copy.cpp
@@ -24,12 +24,10 @@
#include <boost/thread.hpp>
#include <vector>
#include <iostream>
-#include <iomanip>
using namespace uhd::transport;
-const int libusb_timeout = 0;
-
+static const double CLEANUP_TIMEOUT = 0.2; //seconds
static const size_t DEFAULT_NUM_XFERS = 16; //num xfers
static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes
@@ -84,10 +82,10 @@ public:
* Get an available transfer:
* For inputs, this is a just filled transfer.
* For outputs, this is a just emptied transfer.
- * \param timeout_ms the timeout to wait for a lut
+ * \param timeout the timeout to wait for a lut
* \return the transfer pointer or NULL if timeout
*/
- libusb_transfer *get_lut_with_wait(size_t timeout_ms = 100);
+ libusb_transfer *get_lut_with_wait(double timeout);
//Callback use only
void callback_handle_transfer(libusb_transfer *lut);
@@ -97,21 +95,18 @@ private:
int _endpoint;
bool _input;
- size_t _transfer_size;
- size_t _num_transfers;
-
//! hold a bounded buffer of completed transfers
typedef bounded_buffer<libusb_transfer *> lut_buff_type;
lut_buff_type::sptr _completed_list;
//! a list of all transfer structs we allocated
- std::vector<libusb_transfer *> _all_luts;
+ std::vector<libusb_transfer *> _all_luts;
- //! a list of shared arrays for the transfer buffers
- std::vector<boost::shared_array<boost::uint8_t> > _buffers;
+ //! a block of memory for the transfer buffers
+ boost::shared_array<char> _buffer;
// Calls for processing asynchronous I/O
- libusb_transfer *allocate_transfer(int buff_len);
+ libusb_transfer *allocate_transfer(void *mem, size_t len);
void print_transfer_status(libusb_transfer *lut);
};
@@ -156,14 +151,12 @@ usb_endpoint::usb_endpoint(
):
_handle(handle),
_endpoint(endpoint),
- _input(input),
- _transfer_size(transfer_size),
- _num_transfers(num_transfers)
+ _input(input)
{
_completed_list = lut_buff_type::make(num_transfers);
-
- for (size_t i = 0; i < _num_transfers; i++){
- _all_luts.push_back(allocate_transfer(_transfer_size));
+ _buffer = boost::shared_array<char>(new char[num_transfers*transfer_size]);
+ for (size_t i = 0; i < num_transfers; i++){
+ _all_luts.push_back(allocate_transfer(_buffer.get() + i*transfer_size, transfer_size));
//input luts are immediately submitted to be filled
//output luts go into the completed list as free buffers
@@ -187,7 +180,7 @@ usb_endpoint::~usb_endpoint(void){
}
//collect canceled transfers (drain the queue)
- while (this->get_lut_with_wait() != NULL){};
+ while (this->get_lut_with_wait(CLEANUP_TIMEOUT) != NULL){};
//free all transfers
BOOST_FOREACH(libusb_transfer *lut, _all_luts){
@@ -200,23 +193,23 @@ usb_endpoint::~usb_endpoint(void){
* Allocate a libusb transfer
* The allocated transfer - and buffer it contains - is repeatedly
* submitted, reaped, and reused and should not be freed until shutdown.
- * \param buff_len size of the individual buffer held by each transfer
+ * \param mem a pointer to the buffer memory
+ * \param len size of the individual buffer
* \return pointer to an allocated libusb_transfer
*/
-libusb_transfer *usb_endpoint::allocate_transfer(int buff_len){
+libusb_transfer *usb_endpoint::allocate_transfer(void *mem, size_t len){
libusb_transfer *lut = libusb_alloc_transfer(0);
-
- boost::shared_array<boost::uint8_t> buff(new boost::uint8_t[buff_len]);
- _buffers.push_back(buff); //store a reference to this shared array
+ UHD_ASSERT_THROW(lut != NULL);
unsigned int endpoint = ((_endpoint & 0x7f) | (_input ? 0x80 : 0));
+ unsigned char *buff = reinterpret_cast<unsigned char *>(mem);
libusb_transfer_cb_fn lut_callback = libusb_transfer_cb_fn(&callback);
libusb_fill_bulk_transfer(lut, // transfer
_handle->get(), // dev_handle
endpoint, // endpoint
- buff.get(), // buffer
- buff_len, // length
+ buff, // buffer
+ len, // length
lut_callback, // callback
this, // user_data
0); // timeout
@@ -239,6 +232,7 @@ void usb_endpoint::submit(libusb_transfer *lut){
* \param lut pointer to an libusb_transfer
*/
void usb_endpoint::print_transfer_status(libusb_transfer *lut){
+ std::cout << "here " << lut->status << std::endl;
switch (lut->status) {
case LIBUSB_TRANSFER_COMPLETED:
if (lut->actual_length < lut->length) {
@@ -274,136 +268,52 @@ void usb_endpoint::print_transfer_status(libusb_transfer *lut){
}
}
-libusb_transfer *usb_endpoint::get_lut_with_wait(size_t timeout_ms){
+libusb_transfer *usb_endpoint::get_lut_with_wait(double timeout){
boost::this_thread::disable_interruption di; //disable because the wait can throw
libusb_transfer *lut;
- if (_completed_list->pop_with_timed_wait(
- lut, boost::posix_time::milliseconds(timeout_ms)
- )) return lut;
+ if (_completed_list->pop_with_timed_wait(lut, timeout)) return lut;
return NULL;
}
/***********************************************************************
- * Managed buffers
+ * USB zero_copy device class
**********************************************************************/
-/*
- * Libusb managed receive buffer
- * Construct a recv buffer from a libusb transfer. The memory held by
- * the libusb transfer is exposed through the managed buffer interface.
- * Upon destruction, the transfer and buffer are resubmitted to the
- * endpoint for further use.
- */
-class libusb_managed_recv_buffer_impl : public managed_recv_buffer {
+class libusb_zero_copy_impl : public usb_zero_copy, public boost::enable_shared_from_this<libusb_zero_copy_impl> {
public:
- libusb_managed_recv_buffer_impl(libusb_transfer *lut,
- usb_endpoint::sptr endpoint)
- : _buff(lut->buffer, lut->length)
- {
- _lut = lut;
- _endpoint = endpoint;
- }
-
- ~libusb_managed_recv_buffer_impl(void){
- _endpoint->submit(_lut);
- }
+ typedef boost::shared_ptr<libusb_zero_copy_impl> sptr;
-private:
- const boost::asio::const_buffer &get(void) const{
- return _buff;
- }
+ libusb_zero_copy_impl(
+ libusb::device_handle::sptr handle,
+ unsigned int recv_endpoint, unsigned int send_endpoint,
+ size_t recv_xfer_size, size_t recv_num_xfers,
+ size_t send_xfer_size, size_t send_num_xfers
+ );
- libusb_transfer *_lut;
- usb_endpoint::sptr _endpoint;
- const boost::asio::const_buffer _buff;
-};
+ managed_recv_buffer::sptr get_recv_buff(double);
+ managed_send_buffer::sptr get_send_buff(double);
-/*
- * Libusb managed send buffer
- * Construct a send buffer from a libusb transfer. The memory held by
- * the libusb transfer is exposed through the managed buffer interface.
- * Committing the buffer will set the data length and submit the buffer
- * to the endpoint. Submitting a buffer multiple times or destroying
- * the buffer before committing is an error. For the latter, the transfer
- * is returned to the endpoint with no data for reuse.
- */
-class libusb_managed_send_buffer_impl : public managed_send_buffer {
-public:
- libusb_managed_send_buffer_impl(libusb_transfer *lut,
- usb_endpoint::sptr endpoint)
- : _buff(lut->buffer, lut->length), _committed(false)
- {
- _lut = lut;
- _endpoint = endpoint;
- }
+ size_t get_num_recv_frames(void) const { return _recv_num_frames; }
+ size_t get_num_send_frames(void) const { return _send_num_frames; }
- ~libusb_managed_send_buffer_impl(void){
- if (!_committed) {
- _lut->length = 0;
- _lut->actual_length = 0;
- _endpoint->submit(_lut);
- }
+private:
+ void release(libusb_transfer *lut){
+ _recv_ep->submit(lut);
}
- ssize_t commit(size_t num_bytes)
- {
- if (_committed) {
- std::cerr << "UHD: send buffer already committed" << std::endl;
- return 0;
- }
-
- UHD_ASSERT_THROW(num_bytes <= boost::asio::buffer_size(_buff));
-
- _lut->length = num_bytes;
- _lut->actual_length = 0;
-
+ void commit(libusb_transfer *lut, size_t num_bytes){
+ lut->length = num_bytes;
try{
- _endpoint->submit(_lut);
- _committed = true;
- return num_bytes;
+ _send_ep->submit(lut);
}
catch(const std::exception &e){
std::cerr << "Error in commit: " << e.what() << std::endl;
- return -1;
}
}
-private:
- const boost::asio::mutable_buffer &get(void) const{
- return _buff;
- }
-
- libusb_transfer *_lut;
- usb_endpoint::sptr _endpoint;
- const boost::asio::mutable_buffer _buff;
- bool _committed;
-};
-
-
-/***********************************************************************
- * USB zero_copy device class
- **********************************************************************/
-class libusb_zero_copy_impl : public usb_zero_copy
-{
-private:
libusb::device_handle::sptr _handle;
+ size_t _recv_xfer_size, _send_xfer_size;
size_t _recv_num_frames, _send_num_frames;
usb_endpoint::sptr _recv_ep, _send_ep;
-
-public:
- typedef boost::shared_ptr<libusb_zero_copy_impl> sptr;
-
- libusb_zero_copy_impl(
- libusb::device_handle::sptr handle,
- unsigned int recv_endpoint, unsigned int send_endpoint,
- size_t recv_xfer_size, size_t recv_num_xfers,
- size_t send_xfer_size, size_t send_num_xfers
- );
-
- managed_recv_buffer::sptr get_recv_buff(size_t timeout_ms);
- managed_send_buffer::sptr get_send_buff(void);
-
- size_t get_num_recv_frames(void) const { return _recv_num_frames; }
- size_t get_num_send_frames(void) const { return _send_num_frames; }
};
/*
@@ -430,7 +340,9 @@ libusb_zero_copy_impl::libusb_zero_copy_impl(
UHD_ASSERT_THROW(send_xfer_size % 512 == 0);
//store the num xfers for the num frames count
+ _recv_xfer_size = recv_xfer_size;
_recv_num_frames = recv_num_xfers;
+ _send_xfer_size = send_xfer_size;
_send_num_frames = send_num_xfers;
_handle->claim_interface(2 /*in interface*/);
@@ -459,15 +371,16 @@ libusb_zero_copy_impl::libusb_zero_copy_impl(
* Return empty pointer if no transfer is available (timeout or error).
* \return pointer to a managed receive buffer
*/
-managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff(size_t timeout_ms){
- libusb_transfer *lut = _recv_ep->get_lut_with_wait(timeout_ms);
+managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff(double timeout){
+ libusb_transfer *lut = _recv_ep->get_lut_with_wait(timeout);
if (lut == NULL) {
return managed_recv_buffer::sptr();
}
else {
- return managed_recv_buffer::sptr(
- new libusb_managed_recv_buffer_impl(lut,
- _recv_ep));
+ return managed_recv_buffer::make_safe(
+ boost::asio::const_buffer(lut->buffer, lut->actual_length),
+ boost::bind(&libusb_zero_copy_impl::release, shared_from_this(), lut)
+ );
}
}
@@ -478,15 +391,16 @@ managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff(size_t timeout_ms
* (timeout or error).
* \return pointer to a managed send buffer
*/
-managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff(void){
- libusb_transfer *lut = _send_ep->get_lut_with_wait(/* TODO timeout API */);
+managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff(double timeout){
+ libusb_transfer *lut = _send_ep->get_lut_with_wait(timeout);
if (lut == NULL) {
return managed_send_buffer::sptr();
}
else {
- return managed_send_buffer::sptr(
- new libusb_managed_send_buffer_impl(lut,
- _send_ep));
+ return managed_send_buffer::make_safe(
+ boost::asio::mutable_buffer(lut->buffer, _send_xfer_size),
+ boost::bind(&libusb_zero_copy_impl::commit, shared_from_this(), lut, _1)
+ );
}
}
@@ -494,18 +408,18 @@ managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff(void){
* USB zero_copy make functions
**********************************************************************/
usb_zero_copy::sptr usb_zero_copy::make(
- usb_device_handle::sptr handle,
+ usb_device_handle::sptr handle,
unsigned int recv_endpoint, unsigned int send_endpoint,
- size_t recv_xfer_size, size_t recv_num_xfers,
- size_t send_xfer_size, size_t send_num_xfers
+ size_t recv_xfer_size, size_t recv_num_xfers,
+ size_t send_xfer_size, size_t send_num_xfers
){
libusb::device_handle::sptr dev_handle(libusb::device_handle::get_cached_handle(
boost::static_pointer_cast<libusb::special_handle>(handle)->get_device()
));
return sptr(new libusb_zero_copy_impl(
- dev_handle,
- recv_endpoint, send_endpoint,
- recv_xfer_size, recv_num_xfers,
- send_xfer_size, send_num_xfers
+ dev_handle,
+ recv_endpoint, send_endpoint,
+ recv_xfer_size, recv_num_xfers,
+ send_xfer_size, send_num_xfers
));
}
diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp
index 0a6c9f2af..2cf7bde18 100644
--- a/host/lib/transport/udp_zero_copy_asio.cpp
+++ b/host/lib/transport/udp_zero_copy_asio.cpp
@@ -17,25 +17,37 @@
#include <uhd/transport/udp_zero_copy.hpp>
#include <uhd/transport/udp_simple.hpp> //mtu
+#include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/utils/thread_priority.hpp>
#include <uhd/utils/assert.hpp>
#include <uhd/utils/warning.hpp>
-#include <boost/cstdint.hpp>
+#include <boost/shared_array.hpp>
#include <boost/asio.hpp>
#include <boost/format.hpp>
+#include <boost/thread.hpp>
#include <iostream>
+using namespace uhd;
using namespace uhd::transport;
+namespace asio = boost::asio;
/***********************************************************************
* Constants
**********************************************************************/
//enough buffering for half a second of samples at full rate on usrp2
-static const size_t MIN_RECV_SOCK_BUFF_SIZE = size_t(sizeof(boost::uint32_t) * 25e6 * 0.5);
+static const size_t MIN_RECV_SOCK_BUFF_SIZE = size_t(4 * 25e6 * 0.5);
+
//Large buffers cause more underflow at high rates.
//Perhaps this is due to the kernel scheduling,
//but may change with host-based flow control.
static const size_t MIN_SEND_SOCK_BUFF_SIZE = size_t(10e3);
+//the number of async frames to allocate for each send and recv
+static const size_t DEFAULT_NUM_ASYNC_FRAMES = 32;
+
+//a single concurrent thread for io_service seems to be the fastest
+static const size_t CONCURRENCY_HINT = 1;
+
/***********************************************************************
* Zero Copy UDP implementation with ASIO:
* This is the portable zero copy implementation for systems
@@ -43,36 +55,57 @@ static const size_t MIN_SEND_SOCK_BUFF_SIZE = size_t(10e3);
* However, it is not a true zero copy implementation as each
* send and recv requires a copy operation to/from userspace.
**********************************************************************/
-class udp_zero_copy_impl:
- public phony_zero_copy_recv_if,
- public phony_zero_copy_send_if,
- public udp_zero_copy
-{
+class udp_zero_copy_asio_impl : public udp_zero_copy, public boost::enable_shared_from_this<udp_zero_copy_asio_impl> {
public:
- typedef boost::shared_ptr<udp_zero_copy_impl> sptr;
+ typedef boost::shared_ptr<udp_zero_copy_asio_impl> sptr;
- udp_zero_copy_impl(
- const std::string &addr,
- const std::string &port
+ udp_zero_copy_asio_impl(
+ const std::string &addr, const std::string &port,
+ size_t recv_frame_size, size_t num_recv_frames,
+ size_t send_frame_size, size_t num_send_frames
):
- phony_zero_copy_recv_if(udp_simple::mtu),
- phony_zero_copy_send_if(udp_simple::mtu)
+ _io_service(CONCURRENCY_HINT),
+ _work(new asio::io_service::work(_io_service)),
+ _recv_frame_size(recv_frame_size), _num_recv_frames(num_recv_frames),
+ _send_frame_size(send_frame_size), _num_send_frames(num_send_frames)
{
//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);
+ //resolve the address
+ asio::ip::udp::resolver resolver(_io_service);
+ asio::ip::udp::resolver::query query(asio::ip::udp::v4(), addr, port);
+ 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());
+ //create, open, and connect the socket
+ _socket = new asio::ip::udp::socket(_io_service);
+ _socket->open(asio::ip::udp::v4());
_socket->connect(receiver_endpoint);
- _sock_fd = _socket->native();
}
- ~udp_zero_copy_impl(void){
+ void init(void){
+ //allocate all recv frames and release them to begin xfers
+ _pending_recv_buffs = pending_buffs_type::make(_num_recv_frames);
+ _recv_buffer = boost::shared_array<char>(new char[_num_recv_frames*_recv_frame_size]);
+ for (size_t i = 0; i < _num_recv_frames; i++){
+ release(_recv_buffer.get() + i*_recv_frame_size);
+ }
+
+ //allocate all send frames and push them into the fifo
+ _pending_send_buffs = pending_buffs_type::make(_num_send_frames);
+ _send_buffer = boost::shared_array<char>(new char[_num_send_frames*_send_frame_size]);
+ for (size_t i = 0; i < _num_send_frames; i++){
+ handle_send(_send_buffer.get() + i*_send_frame_size);
+ }
+
+ //spawn the service threads that will run the io service
+ for (size_t i = 0; i < CONCURRENCY_HINT; i++) _thread_group.create_thread(
+ boost::bind(&udp_zero_copy_asio_impl::service, this)
+ );
+ }
+
+ ~udp_zero_copy_asio_impl(void){
+ delete _work; //allow io_service run to complete
+ _thread_group.join_all(); //wait for service threads to exit
delete _socket;
}
@@ -90,60 +123,106 @@ public:
return get_buff_size<Opt>();
}
+ //! pop a filled recv buffer off of the fifo and bind with the release callback
+ managed_recv_buffer::sptr get_recv_buff(double timeout){
+ boost::this_thread::disable_interruption di; //disable because the wait can throw
+ asio::mutable_buffer buff;
+ if (_pending_recv_buffs->pop_with_timed_wait(buff, timeout)){
+ return managed_recv_buffer::make_safe(
+ buff, boost::bind(
+ &udp_zero_copy_asio_impl::release,
+ shared_from_this(),
+ asio::buffer_cast<void*>(buff)
+ )
+ );
+ }
+ return managed_recv_buffer::sptr();
+ }
- //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 _num_recv_frames;}
- size_t get_num_recv_frames(void) const{
- return this->get_buff_size<boost::asio::socket_base::receive_buffer_size>()/udp_simple::mtu;
+ //! pop an empty send buffer off of the fifo and bind with the commit callback
+ managed_send_buffer::sptr get_send_buff(double timeout){
+ boost::this_thread::disable_interruption di; //disable because the wait can throw
+ asio::mutable_buffer buff;
+ if (_pending_send_buffs->pop_with_timed_wait(buff, timeout)){
+ return managed_send_buffer::make_safe(
+ buff, boost::bind(
+ &udp_zero_copy_asio_impl::commit,
+ shared_from_this(),
+ asio::buffer_cast<void*>(buff), _1
+ )
+ );
+ }
+ return managed_send_buffer::sptr();
}
- size_t get_num_send_frames(void) const{
- return this->get_buff_size<boost::asio::socket_base::send_buffer_size>()/udp_simple::mtu;
- }
+ size_t get_num_send_frames(void) const {return _num_send_frames;}
private:
- boost::asio::ip::udp::socket *_socket;
- boost::asio::io_service _io_service;
- int _sock_fd;
-
- ssize_t recv(const boost::asio::mutable_buffer &buff, size_t timeout_ms){
- //setup timeval for timeout
- timeval tv;
- tv.tv_sec = 0;
- tv.tv_usec = timeout_ms*1000;
-
- //setup rset for timeout
- fd_set rset;
- FD_ZERO(&rset);
- FD_SET(_sock_fd, &rset);
-
- //call select to perform timed wait
- if (::select(_sock_fd+1, &rset, NULL, NULL, &tv) <= 0) return 0;
-
- return ::recv(
- _sock_fd,
- boost::asio::buffer_cast<char *>(buff),
- boost::asio::buffer_size(buff), 0
+ void service(void){
+ set_thread_priority_safe();
+ _io_service.run();
+ }
+
+ /*******************************************************************
+ * The async send and receive callbacks
+ ******************************************************************/
+
+ //! handle a recv callback -> push the filled memory into the fifo
+ void handle_recv(void *mem, size_t len){
+ boost::this_thread::disable_interruption di; //disable because the wait can throw
+ _pending_recv_buffs->push_with_wait(boost::asio::buffer(mem, len));
+ }
+
+ //! release a recv buffer -> start an async recv on the buffer
+ void release(void *mem){
+ _socket->async_receive(
+ boost::asio::buffer(mem, _recv_frame_size),
+ boost::bind(
+ &udp_zero_copy_asio_impl::handle_recv,
+ shared_from_this(), mem,
+ asio::placeholders::bytes_transferred
+ )
);
}
- ssize_t send(const boost::asio::const_buffer &buff){
- return ::send(
- _sock_fd,
- boost::asio::buffer_cast<const char *>(buff),
- boost::asio::buffer_size(buff), 0
+ //! handle a send callback -> push the emptied memory into the fifo
+ void handle_send(void *mem){
+ boost::this_thread::disable_interruption di; //disable because the wait can throw
+ _pending_send_buffs->push_with_wait(boost::asio::buffer(mem, _send_frame_size));
+ }
+
+ //! commit a send buffer -> start an async send on the buffer
+ void commit(void *mem, size_t len){
+ _socket->async_send(
+ boost::asio::buffer(mem, len),
+ boost::bind(
+ &udp_zero_copy_asio_impl::handle_send,
+ shared_from_this(), mem
+ )
);
}
+
+ //asio guts -> socket and service
+ asio::ip::udp::socket *_socket;
+ asio::io_service _io_service;
+ asio::io_service::work *_work;
+
+ //memory management -> buffers and fifos
+ boost::thread_group _thread_group;
+ boost::shared_array<char> _send_buffer, _recv_buffer;
+ typedef bounded_buffer<asio::mutable_buffer> pending_buffs_type;
+ pending_buffs_type::sptr _pending_recv_buffs, _pending_send_buffs;
+ const size_t _recv_frame_size, _num_recv_frames;
+ const size_t _send_frame_size, _num_send_frames;
};
/***********************************************************************
* UDP zero copy make function
**********************************************************************/
template<typename Opt> static void resize_buff_helper(
- udp_zero_copy_impl::sptr udp_trans,
+ udp_zero_copy_asio_impl::sptr udp_trans,
size_t target_size,
const std::string &name
){
@@ -183,11 +262,17 @@ udp_zero_copy::sptr udp_zero_copy::make(
size_t recv_buff_size,
size_t send_buff_size
){
- udp_zero_copy_impl::sptr udp_trans(new udp_zero_copy_impl(addr, port));
+ udp_zero_copy_asio_impl::sptr udp_trans(new udp_zero_copy_asio_impl(
+ addr, port,
+ udp_simple::mtu, DEFAULT_NUM_ASYNC_FRAMES, //recv
+ udp_simple::mtu, DEFAULT_NUM_ASYNC_FRAMES //send
+ ));
//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");
+ resize_buff_helper<asio::socket_base::receive_buffer_size>(udp_trans, recv_buff_size, "recv");
+ resize_buff_helper<asio::socket_base::send_buffer_size> (udp_trans, send_buff_size, "send");
+
+ udp_trans->init(); //buffers resized -> call init() to use
return udp_trans;
}
diff --git a/host/lib/transport/vrt_packet_handler.hpp b/host/lib/transport/vrt_packet_handler.hpp
index b603f1371..939517411 100644
--- a/host/lib/transport/vrt_packet_handler.hpp
+++ b/host/lib/transport/vrt_packet_handler.hpp
@@ -303,18 +303,18 @@ template <typename T> UHD_INLINE T get_context_code(
* Pack a vrt header, copy-convert the data, and send it.
* - helper function for vrt_packet_handler::send
******************************************************************/
- static UHD_INLINE void _send1(
+ static UHD_INLINE size_t _send1(
send_state &state,
const std::vector<const void *> &buffs,
- size_t offset_bytes,
- size_t num_samps,
+ const size_t offset_bytes,
+ const size_t num_samps,
uhd::transport::vrt::if_packet_info_t &if_packet_info,
const uhd::io_type_t &io_type,
const uhd::otw_type_t &otw_type,
const vrt_packer_t &vrt_packer,
const get_send_buffs_t &get_send_buffs,
- size_t vrt_header_offset_words32,
- size_t chans_per_otw_buff
+ const size_t vrt_header_offset_words32,
+ const size_t chans_per_otw_buff
){
//load the rest of the if_packet_info in here
if_packet_info.num_payload_words32 = (num_samps*chans_per_otw_buff*otw_type.get_sample_size())/sizeof(boost::uint32_t);
@@ -322,7 +322,7 @@ template <typename T> UHD_INLINE T get_context_code(
//get send buffers for each channel
managed_send_buffs_t send_buffs(buffs.size()/chans_per_otw_buff);
- UHD_ASSERT_THROW(get_send_buffs(send_buffs));
+ if (not get_send_buffs(send_buffs)) return 0;
std::vector<const void *> io_buffs(chans_per_otw_buff);
for (size_t i = 0; i < buffs.size(); i+=chans_per_otw_buff){
@@ -343,10 +343,9 @@ template <typename T> UHD_INLINE T get_context_code(
//commit the samples to the zero-copy interface
size_t num_bytes_total = (vrt_header_offset_words32+if_packet_info.num_packet_words32)*sizeof(boost::uint32_t);
- if (send_buffs[i]->commit(num_bytes_total) < ssize_t(num_bytes_total)){
- std::cerr << "commit to send buffer returned less than commit size" << std::endl;
- }
+ send_buffs[i]->commit(num_bytes_total);
}
+ return num_samps;
}
/*******************************************************************
@@ -381,7 +380,6 @@ template <typename T> UHD_INLINE T get_context_code(
////////////////////////////////////////////////////////////////
case uhd::device::SEND_MODE_ONE_PACKET:{
////////////////////////////////////////////////////////////////
- size_t num_samps = std::min(total_num_samps, max_samples_per_packet);
//fill in parts of the packet info overwrote in full buff mode
if_packet_info.has_tsi = metadata.has_time_spec;
@@ -389,10 +387,10 @@ template <typename T> UHD_INLINE T get_context_code(
if_packet_info.sob = metadata.start_of_burst;
if_packet_info.eob = metadata.end_of_burst;
- _send1(
+ return _send1(
state,
buffs, 0,
- num_samps,
+ std::min(total_num_samps, max_samples_per_packet),
if_packet_info,
io_type, otw_type,
vrt_packer,
@@ -400,31 +398,32 @@ template <typename T> UHD_INLINE T get_context_code(
vrt_header_offset_words32,
chans_per_otw_buff
);
- return num_samps;
}
////////////////////////////////////////////////////////////////
case uhd::device::SEND_MODE_FULL_BUFF:{
////////////////////////////////////////////////////////////////
- //calculate constants for fragmentation
- const size_t num_fragments = (total_num_samps+max_samples_per_packet-1)/max_samples_per_packet;
- static const size_t first_fragment_index = 0;
- const size_t final_fragment_index = num_fragments-1;
+ size_t total_num_samps_sent = 0;
//loop through the following fragment indexes
- for (size_t n = first_fragment_index; n <= final_fragment_index; n++){
+ while(total_num_samps_sent < total_num_samps){
+
+ //calculate per-loop-iteration variables
+ const size_t total_num_samps_unsent = total_num_samps - total_num_samps_sent;
+ const bool first_fragment = (total_num_samps_sent == 0);
+ const bool final_fragment = (total_num_samps_unsent <= max_samples_per_packet);
//calculate new flags for the fragments
- if_packet_info.has_tsi = metadata.has_time_spec and (n == first_fragment_index);
- if_packet_info.has_tsf = metadata.has_time_spec and (n == first_fragment_index);
- if_packet_info.sob = metadata.start_of_burst and (n == first_fragment_index);
- if_packet_info.eob = metadata.end_of_burst and (n == final_fragment_index);
+ if_packet_info.has_tsi = metadata.has_time_spec and first_fragment;
+ if_packet_info.has_tsf = if_packet_info.has_tsi;
+ if_packet_info.sob = metadata.start_of_burst and first_fragment;
+ if_packet_info.eob = metadata.end_of_burst and final_fragment;
//send the fragment with the helper function
- _send1(
+ const size_t num_samps_sent = _send1(
state,
- buffs, n*max_samples_per_packet*io_type.size,
- (n == final_fragment_index)?(total_num_samps%max_samples_per_packet):max_samples_per_packet,
+ buffs, total_num_samps_sent*io_type.size,
+ std::min(total_num_samps_unsent, max_samples_per_packet),
if_packet_info,
io_type, otw_type,
vrt_packer,
@@ -432,8 +431,10 @@ template <typename T> UHD_INLINE T get_context_code(
vrt_header_offset_words32,
chans_per_otw_buff
);
+ total_num_samps_sent += num_samps_sent;
+ if (num_samps_sent == 0) return total_num_samps_sent;
}
- return total_num_samps;
+ return total_num_samps_sent;
}
default: throw std::runtime_error("unknown send mode");
diff --git a/host/lib/transport/zero_copy.cpp b/host/lib/transport/zero_copy.cpp
index 1fcf846a0..a5a864a04 100644
--- a/host/lib/transport/zero_copy.cpp
+++ b/host/lib/transport/zero_copy.cpp
@@ -16,32 +16,35 @@
//
#include <uhd/transport/zero_copy.hpp>
-#include <boost/cstdint.hpp>
-#include <boost/function.hpp>
-#include <boost/bind.hpp>
using namespace uhd::transport;
/***********************************************************************
- * The pure-virtual deconstructor needs an implementation to be happy
+ * Safe managed receive buffer
**********************************************************************/
-managed_recv_buffer::~managed_recv_buffer(void){
+static void release_nop(void){
/* NOP */
}
-/***********************************************************************
- * Phony zero-copy recv interface implementation
- **********************************************************************/
-
-//! phony zero-copy recv buffer implementation
-class managed_recv_buffer_impl : public managed_recv_buffer{
+class safe_managed_receive_buffer : public managed_recv_buffer{
public:
- managed_recv_buffer_impl(const boost::asio::const_buffer &buff) : _buff(buff){
+ safe_managed_receive_buffer(
+ const boost::asio::const_buffer &buff,
+ const release_fcn_t &release_fcn
+ ):
+ _buff(buff), _release_fcn(release_fcn)
+ {
/* NOP */
}
- ~managed_recv_buffer_impl(void){
- delete [] this->cast<const boost::uint8_t *>();
+ ~safe_managed_receive_buffer(void){
+ _release_fcn();
+ }
+
+ void release(void){
+ release_fcn_t release_fcn = _release_fcn;
+ _release_fcn = &release_nop;
+ return release_fcn();
}
private:
@@ -50,64 +53,42 @@ private:
}
const boost::asio::const_buffer _buff;
+ release_fcn_t _release_fcn;
};
-//! phony zero-copy recv interface implementation
-struct phony_zero_copy_recv_if::impl{
- impl(size_t max_buff_size) : max_buff_size(max_buff_size){
- /* NOP */
- }
- size_t max_buff_size;
-};
-
-phony_zero_copy_recv_if::phony_zero_copy_recv_if(size_t max_buff_size){
- _impl = UHD_PIMPL_MAKE(impl, (max_buff_size));
-}
-
-phony_zero_copy_recv_if::~phony_zero_copy_recv_if(void){
- /* NOP */
-}
-
-managed_recv_buffer::sptr phony_zero_copy_recv_if::get_recv_buff(size_t timeout_ms){
- //allocate memory
- boost::uint8_t *recv_mem = new boost::uint8_t[_impl->max_buff_size];
-
- //call recv() with timeout option
- ssize_t num_bytes = this->recv(boost::asio::buffer(recv_mem, _impl->max_buff_size), timeout_ms);
-
- if (num_bytes <= 0) return managed_recv_buffer::sptr(); //NULL sptr
-
- //create a new managed buffer to house the data
- return managed_recv_buffer::sptr(
- new managed_recv_buffer_impl(boost::asio::buffer(recv_mem, num_bytes))
- );
+managed_recv_buffer::sptr managed_recv_buffer::make_safe(
+ const boost::asio::const_buffer &buff,
+ const release_fcn_t &release_fcn
+){
+ return sptr(new safe_managed_receive_buffer(buff, release_fcn));
}
/***********************************************************************
- * Phony zero-copy send interface implementation
+ * Safe managed send buffer
**********************************************************************/
+static void commit_nop(size_t){
+ /* NOP */
+}
-//! phony zero-copy send buffer implementation
-class managed_send_buffer_impl : public managed_send_buffer{
+class safe_managed_send_buffer : public managed_send_buffer{
public:
- typedef boost::function<ssize_t(const boost::asio::const_buffer &)> send_fcn_t;
-
- managed_send_buffer_impl(
+ safe_managed_send_buffer(
const boost::asio::mutable_buffer &buff,
- const send_fcn_t &send_fcn
+ const commit_fcn_t &commit_fcn
):
- _buff(buff),
- _send_fcn(send_fcn)
+ _buff(buff), _commit_fcn(commit_fcn)
{
/* NOP */
}
- ~managed_send_buffer_impl(void){
- /* NOP */
+ ~safe_managed_send_buffer(void){
+ _commit_fcn(0);
}
- ssize_t commit(size_t num_bytes){
- return _send_fcn(boost::asio::buffer(_buff, num_bytes));
+ void commit(size_t num_bytes){
+ commit_fcn_t commit_fcn = _commit_fcn;
+ _commit_fcn = &commit_nop;
+ return commit_fcn(num_bytes);
}
private:
@@ -116,28 +97,12 @@ private:
}
const boost::asio::mutable_buffer _buff;
- const send_fcn_t _send_fcn;
-};
-
-//! phony zero-copy send interface implementation
-struct phony_zero_copy_send_if::impl{
- boost::uint8_t *send_mem;
- managed_send_buffer::sptr send_buff;
+ commit_fcn_t _commit_fcn;
};
-phony_zero_copy_send_if::phony_zero_copy_send_if(size_t max_buff_size){
- _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),
- boost::bind(&phony_zero_copy_send_if::send, this, _1)
- ));
-}
-
-phony_zero_copy_send_if::~phony_zero_copy_send_if(void){
- delete [] _impl->send_mem;
-}
-
-managed_send_buffer::sptr phony_zero_copy_send_if::get_send_buff(void){
- return _impl->send_buff; //FIXME there is only ever one send buff, we assume that the caller doesnt hang onto these
+safe_managed_send_buffer::sptr managed_send_buffer::make_safe(
+ const boost::asio::mutable_buffer &buff,
+ const commit_fcn_t &commit_fcn
+){
+ return sptr(new safe_managed_send_buffer(buff, commit_fcn));
}