diff options
Diffstat (limited to 'host')
-rw-r--r-- | host/lib/include/uhdlib/transport/io_service.hpp | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/host/lib/include/uhdlib/transport/io_service.hpp b/host/lib/include/uhdlib/transport/io_service.hpp new file mode 100644 index 000000000..69a3a523e --- /dev/null +++ b/host/lib/include/uhdlib/transport/io_service.hpp @@ -0,0 +1,337 @@ +// +// Copyright 2019 Ettus Research, a National Instruments brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// +/*! + * \file + * This file declares the I/O service interfaces. There are two layers to the + * implementation of a transport: + * + * - The link layer is the lower of the two layers. Link layer interfaces + * (uhd::transport::recv_link_if and uhd::transport::send_link_if) provide + * an abstraction for transmission of frames over a physical layer. + * - Transports are a layer above the link layer and provide flow control, + * multiplexing, and the ability to offload I/O to worker threads. + * - Transports are implemented using I/O services + * (uhd::transport::io_service). Transports implement callbacks defined by + * the I/O service, and the I/O service implements the scheduling of the work + * defined by the callbacks. Different I/O service implementations exist to + * implement different work scheduling policies. + * + * Callback parameters include pointers to the send and recv links. In a + * multithreaded I/O service, the methods defined by the transport run in the + * caller thread, while the callbacks run in a worker thread. Transport + * implementations must be careful to ensure any shared data between the + * transport methods and the callbacks it implements is thread-safe. Callbacks + * should never sleep since doing so would stall worker threads that may be + * servicing multiple transports. + * + * Only the callbacks should interact directly with the links. The public + * transport methods request and release buffers from the I/O service using the + * uhd::transport::recv_io_if and uhd::transport::send_io_if interfaces. + */ + + +#ifndef INCLUDED_UHDLIB_TRANSPORT_IO_SERVICE_HPP +#define INCLUDED_UHDLIB_TRANSPORT_IO_SERVICE_HPP + +#include <uhdlib/transport/link_if.hpp> +#include <functional> +#include <memory> + +namespace uhd { namespace transport { + +/*! + * Callback that a transport must implement to process received packets. + * Function should make a determination of whether the packet belongs to it + * and return the bool. + * + * Function may consume and release the buffer internally (if packet was + * destined for it). The recv_link_if may be used to release it, and the + * provided frame_buff::uptr must be made to relinquish ownership before the + * callback returns. If the buffer was not destined for the callback's + * transport, the buffer must NOT be released, and the uptr must remain intact. + * + * For uhd::transport::recv_io_if clients: + * - A buffer that is not released (but destined for this client) will be + * queued up to be returned on a uhd::transport::recv_io_if::get_recv_buff() + * call. + * - The send_link_if may be used to send automatic responses, such as a + * simple response to a query of flow control state. + * + * For uhd::transport::send_io_if clients: + * - The buffer must either be destined for this client and released, or it + * must not be for this client and left unreleased. + * - Currently, the send_link_if is always null and should not be used. + * + * Callbacks execute on the I/O thread! Callback implementations must take care + * with what state is touched. In addition, this callback should NOT sleep. + * + * \param frame_buff the buffer that was received + * \param recv_link_if the link used to retrieve the buffer. Can be used to + * release the buffer back to the link, if buffer is consumed internally. + * \param send_link_if a link for sending a response. Can be null. + * \return true if buffer matched this transport, false otherwise + */ +using recv_callback_t = + std::function<bool(frame_buff::uptr&, recv_link_if*, send_link_if*)>; + +/*! + * Interface for a recv transport to request/release buffers from a link. A + * recv transport is a transport with a primary purpose of receiving data, and + * the recv_io_if class interacts with the io_service to schedule the data + * movement, including any queuing when there are worker threads. + * + * recv_io_if::get_recv_buff and recv_io_if::release_recv_buff are the pathways + * for data reception, and the uhd::transport::recv_callback_t and + * recv_io_if::fc_callback_t are available for handling flow control and + * automatic responses. The uhd::transport::recv_callback_t also indicates + * whether the buffer is either + * - to go up to the transport above, + * - destined to this transport but consumed by it, + * - or not destined for this transport and left alone. + */ +class recv_io_if +{ +public: + using sptr = std::shared_ptr<recv_io_if>; + + /*! + * Callback for producing a flow control response (or any other response + * needed when a received frame_buff is released via + * recv_io_if::get_recv_buff()). + * + * The callback must release the buffer, but it can update internal state + * as well. It can also send a response with the send_link_if, should it + * desire to do so. + * + * Callbacks execute on the I/O thread: Be careful about what state is + * touched. In addition, this callback should NOT sleep. + */ + using fc_callback_t = + std::function<void(frame_buff::uptr, recv_link_if*, send_link_if*)>; + + /* Transport client methods */ + /*! + * Gets a receive buffer from the I/O service. + * + * Multi-thread version: + * - Check queue, and wait on queue. + * + * Single-thread version: + * - Do receive on the link, and wait on link. + * + * \param timeout_ms The timeout in milliseconds + * \return if timeout, an empty frame_buff::uptr, else a buffer with data + */ + virtual frame_buff::uptr get_recv_buff(int32_t timeout_ms) = 0; + + /*! + * Release buffer back to the I/O service. + * + * \param buff the buffer to release + */ + virtual void release_recv_buff(frame_buff::uptr buff) = 0; + + /*! + * Get number of send frames reserved by this I/O interface. + * + * \return Number of frames reserved + */ + size_t get_num_send_frames(void) const + { + return _num_send_frames; + } + + /*! + * Get number of recv frames reserved by this I/O interface. + * + * \return Number of frames reserved + */ + size_t get_num_recv_frames(void) const + { + return _num_recv_frames; + } + +protected: + /*! Number of frames reserved on the send_link_if associated with this */ + size_t _num_send_frames; + + /*! Number of frames reserved on the recv_link_if associated with this */ + size_t _num_recv_frames; +}; + +/*! + * Interface for a send transport to request/release buffers from a link. A + * send transport is a transport with a primary purpose of sending data, and + * the send_io_if class interacts with the io_service to schedule the data + * movement, including any queuing when there are worker threads. + * + * send_io_if::get_send_buff and send_io_if::release_send_buff are the pathways + * for data transmission, and the uhd::transport::recv_callback_t and + * send_io_if::send_callback_t are available for handling flow control and + * receiving side-band messages. Currently, the uhd::transport::recv_callback_t + * would be provided only a NULL send_link_if. Thus, when a buffer is destined + * for this transport, it must consume the buffer and release it back to the + * recv_link_if. + */ +class send_io_if +{ +public: + using sptr = std::shared_ptr<send_io_if>; + + /*! + * Callback for sending the packet. Callback is responsible for calling + * release_send_buff() if it wants to send the packet. This will require + * moving the uptr's reference. If the packet will NOT be sent, the + * callback must NOT release the uptr. + * + * Function should update any internal state needed. For example, flow + * control state could be updated here, and the header could be filled out + * as well, like the packet's sequence number and/or addresses. + * + * Callbacks execute on the I/O thread! Be careful about what state is + * touched. In addition, this callback should NOT sleep. + */ + using send_callback_t = std::function<void(frame_buff::uptr&, send_link_if*)>; + + /* Transport client methods */ + /*! + * Get an empty send buffer from the link. + * + * \param timeout_ms timeout in milliseconds to wait for a buffer + * \return If no buffer available, return an empty frame_buff:uptr. Else + * return an empty frame_buff with its packet_size set to the + * maximum capacity of the buffer. + */ + virtual frame_buff::uptr get_send_buff(int32_t timeout_ms) = 0; + + /*! + * Release the send buffer to the send queue. + * If the frame_buff's packet_size is zero, the link will free the buffer + * without sending it. + * + * \param buff the buffer to be freed/sent + */ + virtual void release_send_buff(frame_buff::uptr buff) = 0; + + /*! + * Get number of send frames reserved by this I/O interface. + * + * \return Number of frames reserved + */ + size_t get_num_send_frames(void) const + { + return _num_send_frames; + } + + /*! + * Get number of recv frames reserved by this I/O interface. + * + * \return Number of frames reserved + */ + size_t get_num_recv_frames(void) const + { + return _num_recv_frames; + } + +protected: + /*! Number of frames reserved on the send_link_if associated with this */ + size_t _num_send_frames; + + /*! Number of frames reserved on the recv_link_if associated with this */ + size_t _num_recv_frames; +}; + +/*! + * Together with the recv_io_if and send_io_if, connects transports to links + * and schedules I/O. + * + * Transports gain access to the links via io_service::make_send_client() and + * io_service::make_recv_client(). These functions produce the conduits to + * send and recv data through the links (via send_io_if and recv_io_if, + * respectively). Note that muxing of send_link_if and recv_link_if is allowed, + * but there may be a balance needed for performance / ease-of-use / resource + * utilization. + * + * In all implementations, only one thread may do the work of a single + * io_service object. If the I/O service does not provide worker threads, its + * client interfaces and send_io_if / recv_io_if implementations cannot be + * shared by multiple threads. Thus, without worker threads, a muxed link must + * have all clients on the same thread (or a synchronization function + * protecting access). + */ +class io_service +{ +public: + using sptr = std::shared_ptr<io_service>; + + /*! + * Attach a recv_link_if to be serviced by this I/O service. + * + * \param link the recv_link_if to attach + */ + virtual void attach_recv_link(recv_link_if::sptr link) = 0; + + /*! + * Attach a send_link_if to be serviced by this I/O service. + * + * \param link the send_link_if to attach + */ + virtual void attach_send_link(send_link_if::sptr link) = 0; + + /* TODO: Cleanup functions + virtual void detach_recv_link(recv_link_if::sptr link) = 0; + virtual void detach_send_link(send_link_if::sptr link) = 0; + */ + + /*! + * Create a send_io_if so a transport may send packets through the link. + * + * Throws exception if cannot reserve requested frames. + * + * \param send_link the link to use for sending data + * \param num_send_frames Number of buffers to reserve in data_link + * \param send_cb callback function for buffer processing just before sending + * \param recv_link the link used to observe flow control (can be empty) + * \param num_recv_frames Number of buffers to reserve in recv_link + * \param recv_cb callback function for receiving packets from recv_link + * \return a send_io_if for interfacing with the link + */ + virtual send_io_if::sptr make_send_client(send_link_if::sptr send_link, + size_t num_send_frames, + send_io_if::send_callback_t cb, + recv_link_if::sptr recv_link, + size_t num_recv_frames, + recv_callback_t recv_cb) = 0; + + /*! + * Create a recv_io_if and registers the transport's callbacks. + * + * Throws exception if cannot reserve requested frames. + * + * \param data_link the link to receive packets from + * \param cb callback function for processing received packets + * \param num_recv_frames Number of buffers to reserve in data_link + * \param fc_link the link used to send flow control responses + * \param fc_cb callback function for handling flow control + * \param num_send_frames Number of buffers to reserve in fc_link + * \return a recv_io_if for interfacing with the link + */ + virtual recv_io_if::sptr make_recv_client(recv_link_if::sptr data_link, + size_t num_recv_frames, + recv_callback_t cb, + send_link_if::sptr fc_link, + size_t num_send_frames, + recv_io_if::fc_callback_t fc_cb) = 0; + + io_service() = default; + io_service(const io_service&) = delete; + io_service& operator=(const io_service&) = delete; +}; + +}} // namespace uhd::transport + +#endif /* INCLUDED_UHDLIB_TRANSPORT_IO_SERVICE_HPP */ |