//
// Copyright 2016 Ettus Research
//
// 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
#include
#include
#include
#include
#include
#include
using namespace uhd;
using namespace uhd::transport;
typedef bounded_buffer bounded_buffer_t;
/***********************************************************************
* Zero copy offload transport:
* An intermediate transport that utilizes threading to free
* the main thread from any receive work.
**********************************************************************/
class zero_copy_recv_offload_impl : public zero_copy_recv_offload {
public:
typedef boost::shared_ptr sptr;
zero_copy_recv_offload_impl(zero_copy_if::sptr transport,
const double timeout) :
_transport(transport), _timeout(timeout),
_inbox(transport->get_num_recv_frames()),
_recv_done(false)
{
UHD_LOGGER_DEBUG("XPORT") << "Created threaded transport" ;
// Create the receive and send threads to offload
// the system calls onto other threads
_recv_thread = boost::thread(
boost::bind(&zero_copy_recv_offload_impl::enqueue_recv, this)
);
}
// Receive thread flags
void set_recv_done()
{
boost::lock_guard guard(_recv_mutex);
_recv_done = true;
}
bool is_recv_done()
{
boost::lock_guard guard(_recv_mutex);
return _recv_done;
}
~zero_copy_recv_offload_impl()
{
// Signal the threads we're finished
set_recv_done();
// Wait for them to join
UHD_SAFE_CALL(
_recv_thread.join();
)
}
// The receive thread function is responsible for
// pulling pointers to managed receiver buffers quickly
void enqueue_recv()
{
while (not is_recv_done()) {
managed_recv_buffer::sptr buff = _transport->get_recv_buff(_timeout);
if (not buff) continue;
_inbox.push_with_timed_wait(buff, _timeout);
}
}
/*******************************************************************
* Receive implementation:
* Pop the receive buffer pointer from the underlying transport
******************************************************************/
managed_recv_buffer::sptr get_recv_buff(double timeout)
{
managed_recv_buffer::sptr ptr;
_inbox.pop_with_timed_wait(ptr, timeout);
return ptr;
}
size_t get_num_recv_frames() const
{
return _transport->get_num_recv_frames();
}
size_t get_recv_frame_size() const
{
return _transport->get_recv_frame_size();
}
/*******************************************************************
* Send implementation:
* Pass the send buffer pointer from the underlying transport
******************************************************************/
managed_send_buffer::sptr get_send_buff(double timeout)
{
return _transport->get_send_buff(timeout);
}
size_t get_num_send_frames() const
{
return _transport->get_num_send_frames();
}
size_t get_send_frame_size() const
{
return _transport->get_send_frame_size();
}
private:
// The linked transport
zero_copy_if::sptr _transport;
const double _timeout;
// Shared buffers
bounded_buffer_t _inbox;
// Threading
bool _recv_done;
boost::thread _recv_thread;
boost::mutex _recv_mutex;
};
zero_copy_recv_offload::sptr zero_copy_recv_offload::make(
zero_copy_if::sptr transport,
const double timeout)
{
zero_copy_recv_offload_impl::sptr zero_copy_recv_offload(
new zero_copy_recv_offload_impl(transport, timeout)
);
return zero_copy_recv_offload;
}