diff options
Diffstat (limited to 'host/lib/include')
| -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 */ | 
