LCOV - code coverage report
Current view: top level - waltz/aio - fd_aio.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 10 10 100.0 %
Date: 2024-11-13 11:58:15 Functions: 6 129 4.7 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_waltz_aio_fd_aio_h
       2             : #define HEADER_fd_src_waltz_aio_fd_aio_h
       3             : 
       4             : #include "../fd_waltz_base.h"
       5             : 
       6             : /* fd_aio defines a simple abstraction for asynchronous sending and
       7             :    receiving packets.  It abstracts away many details so the same code
       8             :    can work performant and transparent with different low level I/O
       9             :    libraries and hardware.
      10             : 
      11             :    FIXME: update the below documentation.  Also, given fd_aio_t almost
      12             :    certainly is behaves like an abstract base class, doing a declaration
      13             :    like the below for a fd_aio_t is probably wrong because different
      14             :    implementations likely will need different footprints and the like
      15             :    long term.
      16             : 
      17             :    ### Example: Setup
      18             : 
      19             :      fd_quic_t * quic = get_quic(); // get an initialized quic instance
      20             :      fd_xdp_t *  xdp  = get_xdp();  // get an initialized xdp instance
      21             : 
      22             :      fd_aio_t aio_xdp_to_quic;
      23             :      fd_aio_t aio_quic_to_xdp;
      24             : 
      25             :      fd_quic_set_aio( quic, aio_xdp_to_quic, aio_quic_to_xdp );
      26             :      fd_xdp_set_aio ( xdp, aio_quic_to_xdp,  aio_xdp_to_quic );
      27             : 
      28             :      // the two *set_aio calls have the following effect:
      29             :      //   aio_xdp_to_quic.recv = fd_aio_cb_receive;
      30             :      //   aio_xdp_to_quic.ctx  = quic;
      31             : 
      32             :      //   aio_quic_to_xdp.recv = fd_xdp_aio_cb_receive;
      33             :      //   aio_quic_to_xdp.ctx  = xdp;
      34             : 
      35             :      // now whenever fd_quic_process is called on quic, quic will
      36             :      // be able to send data to xdp via fd_aio_send(...)
      37             :      // and vice versa
      38             : 
      39             :    ### Example: Sending
      40             : 
      41             :      fd_aio_t aio;
      42             : 
      43             :      aio.recv = my_cb_receive;
      44             :      aio.ctx   quic;
      45             : 
      46             :      fd_aio_pkt_info_t batch[10] = {{ .data = data, .data_sz = data_sz }};
      47             : 
      48             :      fd_aio_pkt_info_t cur_batch    = batch;
      49             :      ulong         cur_batch_sz = 10;
      50             :      while( cur_batch_sz ) {
      51             :        int send_rc = fd_aio_send( aio, cur_batch, cur_batch_sz ); // send a batch of buffers to the peer
      52             :        if( send_rc < 0 ) {
      53             :          fprintf( stderr, "error occurred during send\n" );
      54             :          break;
      55             :        }
      56             :        cur_batch    += send_rc;
      57             :        cur_batch_sz -= send_rc;
      58             : 
      59             :        // possibly do some other process that might free up resources
      60             :        // to avoid deadlock
      61             :      } */
      62             : 
      63             : /* FD_AIO_SUCCESS, FD_AIO_ERR_* give a number of integer error codes
      64             :    used by AIO operations.  FD_AIO_ERR_* will be negative integers.
      65             : 
      66             :    FIXME: these current values are largely common placeholders.  These
      67             :    should be revamped for specific AIO instance needs and harmonized
      68             :    with other fd error codes long term. */
      69             : 
      70    69079687 : #define FD_AIO_SUCCESS   ( 0) /* Success */
      71           9 : #define FD_AIO_ERR_INVAL (-1) /* Bad input args */
      72    19406603 : #define FD_AIO_ERR_AGAIN (-2) /* Try again later */
      73             : 
      74             : /* An fd_aio_pkt_info_t is used to describe a memory region in the
      75             :    caller's local address space for sending and receiving packets.  Note
      76             :    that, as this is only used by AIO APIs to box up info the caller
      77             :    communications with AIO instances, this is not an object (i.e. it has
      78             :    no need for object creation/destruction/accessor semantics). */
      79             : 
      80             : /* FD_AIO_PKT_INFO_{ALIGN,FOOTPRINT} specify the alignment and footprint
      81             :    needed for an fd_aio_pkt_info_t.  ALIGN will be positive integer
      82             :    power of 2.  FOOTPRINT will be an integer multiple of align. */
      83             : 
      84             : #define FD_AIO_PKT_INFO_ALIGN     (16UL)
      85             : #define FD_AIO_PKT_INFO_FOOTPRINT (16UL)
      86             : 
      87             : /* FD_AIO_PKT_INFO_BUF_MAX specifies the largest buffer supported by an
      88             :    fd_aio_pkt_info_t.  FIXME: fine tune this to be the smallest multiple
      89             :    of something cache line pair-ish to supporting needed functionality
      90             :    of AIO instances and the application needs. */
      91             : 
      92             : #define FD_AIO_PKT_INFO_BUF_MAX (4096UL)
      93             : 
      94             : struct __attribute__((aligned(FD_AIO_PKT_INFO_ALIGN))) fd_aio_pkt_info {
      95             : 
      96             :   /* buf is a pointer in the thread group's local address space to the
      97             :      first byte of a memory region:
      98             : 
      99             :      - Holding a packet received in the background by an AIO instance.
     100             :      - Holding a packet to be sent in the background by an AIO instance.
     101             :      - Holding a packet sent in the background by an AIO instance.
     102             :      - To be used by an AIO instance for receiving future packets.
     103             :      - ...
     104             : 
     105             :      The readability, writability, lifetime, footprint, alignment, ...
     106             :      requirements of memory region here depending on the specific APIs
     107             :      and/or AIO instance. */
     108             : 
     109             :   void * buf;
     110             : 
     111             :   /* buf_sz is the number of bytes in the memory region.  The exact
     112             :      limitations on buf_sz can also depend on specific AIO instance.  In
     113             :      general, this will be in [0,FD_AIO_PKT_INFO_DATA_MAX] and a zero
     114             :      buf_sz (and buf==NULL for such cases) can be a possibility for some
     115             :      APIs. */
     116             : 
     117             :   ushort buf_sz;
     118             : 
     119             :   /* Padding to FD_AIO_PKT_INFO_ALIGN here (reserved for potential
     120             :      future use and/or use by specific AIO instances). */
     121             : 
     122             : };
     123             : 
     124             : typedef struct fd_aio_pkt_info fd_aio_pkt_info_t;
     125             : 
     126             : /* A fd_aio_send_func_t is used to tell an AIO instance to do a best
     127             :    effort packet batch send.  Unless otherwise noted by a specific API
     128             :    or implementation:
     129             : 
     130             :    - The AIO instance will queue up, in order, the given packet batch
     131             :      for transmission.  Packets in the batch are indexed in
     132             :      [0,batch_cnt) and info about the packet to transmit is in
     133             :      batch[idx].
     134             : 
     135             :    - batch[idx].buf_sz==0 (and, if so, possibly batch[idx].buf==NULL) is
     136             :      valid.  It will be treated as successfully transmitted and
     137             :      otherwise ignored.
     138             : 
     139             :    - batch_cnt==0 is a valid and returns success immediately.
     140             : 
     141             :    - There is no restriction on the packet buffers used by the send
     142             :      function.  For example, specifying the same buffer multiple times
     143             :      in the batch and/or using overlapping buffers in the batch are
     144             :      valid.
     145             : 
     146             :    - On success, this will return zero (FD_AIO_SUCCESS) and
     147             :      opt_batch_idx will be ignored.
     148             : 
     149             :    - If an error occurs, this will return a negative error code
     150             :      (FD_AIO_ERR_*).
     151             : 
     152             :    - If an error occurs and opt_batch_idx is non-NULL, *opt_batch_idx
     153             :      will contain the index of the first packet in the batch that was
     154             :      not "sent" from the caller's POV.
     155             : 
     156             :    - As such, in this case, all packets indexed [0,*opt_batch_idx) will
     157             :      have been "sent" from the caller's POV and those in
     158             :      [*opt_batch_idx,batch_cnt) were not.
     159             : 
     160             :    - Further, in this case and the error reason is specific to a packet,
     161             :      packets indexed [0,*opt_batch_idx) were seemingly transmissible,
     162             :      the packet indexed *opt_batch_idx was untransmissible and packets
     163             :      indexed (*opt_batch_idx,batch_cnt) had unexamined transmissibility.
     164             : 
     165             :    - The batch array and memory regions covered by the batch array and
     166             :      any *opt_batch_idx must not be modified while this is running.
     167             :      The batch array itself and the memory regions referred to by the
     168             :      batch array are not modified by this function.  The AIO retains no
     169             :      interest in the batch array, the packet buffers referred to in the
     170             :      array on return.
     171             : 
     172             :    - flush requests an asynchronous best-effort transmit of packets
     173             :      buffered from this and prior send operations.  Actual flush
     174             :      semantics are implementation-defined.
     175             : 
     176             :    Note the reception of any packets "sent" by this call is not
     177             :    guaranteed.  Reasons for failed reception could include local AIO
     178             :    instance failures not diagnosable at time of call and/or failures,
     179             :    for whatever reason, in the connectivity between the sender and
     180             :    receiver.  Likewise, though the AIO will send packets in the order
     181             :    specified, it there is no general guarantee they will be received in
     182             :    any particular order (within this batch or between batches). */
     183             : 
     184             : /* FIXME: consider passing an fd_aio_t instead of the aio_t ctx so
     185             :    that send function naturally has access to all other AIO
     186             :    functionality? */
     187             : 
     188             : typedef int
     189             : (*fd_aio_send_func_t)( void *                    ctx,
     190             :                        fd_aio_pkt_info_t const * batch,
     191             :                        ulong                     batch_cnt,
     192             :                        ulong *                   opt_batch_idx,
     193             :                        int                       flush );
     194             : 
     195             : /* An fd_aio_t * is an opaque handle of an AIO instance.  (It
     196             :    technically isn't here to facilitate inlining of fd_aio operations.) */
     197             : 
     198             : struct fd_aio_private {
     199             :   void *             ctx;       /* AIO specific context */
     200             :   fd_aio_send_func_t send_func; /* Send_func for specific AIO */
     201             : 
     202             :   /* FIXME: probably AIO specific functionality state follows here as
     203             :      per FIXME above (this might also clean up some of the ctx messiness
     204             :      below too, give the callbacks more power and slightly reduce
     205             :      overhead for the actual callback invocation because it doesn't need
     206             :      to do an aio->ctx load as part of the user API). */
     207             : 
     208             : };
     209             : 
     210             : typedef struct fd_aio_private fd_aio_t;
     211             : 
     212           6 : #define FD_AIO_ALIGN (alignof(fd_aio_t))
     213           6 : #define FD_AIO_FOOTPRINT (sizeof(fd_aio_t))
     214             : 
     215             : FD_PROTOTYPES_BEGIN
     216             : 
     217             : /* FIXME: document these.  Also fd_aio_{align,footprint,new} are
     218             :    probably not things that should be exposed as per FIXME above.  That
     219             :    is, probably should be more like
     220             :    fd_aio_{xdp,quic}_{align,footprint,new} and similar for other
     221             :    specific AIO implementations (e.g. io_uring, etc).  Probably implies
     222             :    that fd_aio_private like have their own delete_func too.  Likewise,
     223             :    if the ctx gets included in the actual aio, the fd_aio_ctx function
     224             :    probably does away. */
     225             : 
     226             : FD_FN_CONST ulong fd_aio_align    ( void );
     227             : FD_FN_CONST ulong fd_aio_footprint( void );
     228             : 
     229             : void *
     230             : fd_aio_new( void *             shmem,
     231             :             void *             ctx,         /* FIXME: AIO currently has a R/W interest in ctx for lifetime of AIO */
     232             :             fd_aio_send_func_t send_func );
     233             : 
     234             : fd_aio_t * fd_aio_join  ( void *     shaio );
     235             : void *     fd_aio_leave ( fd_aio_t * aio   );
     236             : void *     fd_aio_delete( void *     shaio ); /* FIXME: No interest in the ctx on delete */
     237             : 
     238           6 : FD_FN_PURE static inline void *             fd_aio_ctx      ( fd_aio_t * aio ) { return FD_LIKELY( aio ) ? aio->ctx       : NULL; }
     239           6 : FD_FN_PURE static inline fd_aio_send_func_t fd_aio_send_func( fd_aio_t * aio ) { return FD_LIKELY( aio ) ? aio->send_func : NULL; }
     240             : 
     241             : /* fd_aio_send sends a batch of packets.  Assumes aio is a current local
     242             :    join to an AIO instance.  The batch, batch_cnt, opt_batch_idx and
     243             :    return value are as described in fd_aio_send_func_t with any
     244             :    additional restrictions that might be imposed by the specific AIO
     245             :    instance. */
     246             : 
     247             : /* TODO: This would ideally be extern inline but that causes issues with
     248             :    the build system. */
     249             : 
     250             : static inline int
     251             : fd_aio_send( fd_aio_t const *          aio,
     252             :              fd_aio_pkt_info_t const * batch,
     253             :              ulong                     batch_cnt,
     254             :              ulong *                   opt_batch_idx,
     255    30250683 :              int                       flush ) {
     256    30250683 :   return aio->send_func( aio->ctx, batch, batch_cnt, opt_batch_idx, flush );
     257    30250683 : }
     258             : 
     259             : /* fd_aio_strerror converts an FD_AIO_SUCCESS / FD_AIO_ERR_* code into
     260             :    a human readable cstr.  The lifetime of the returned pointer is
     261             :    infinite.  The returned pointer is always to a non-NULL cstr. */
     262             : 
     263             : FD_FN_CONST char const *
     264             : fd_aio_strerror( int err );
     265             : 
     266             : FD_PROTOTYPES_END
     267             : 
     268             : #endif /* HEADER_fd_src_waltz_aio_fd_aio_h */

Generated by: LCOV version 1.14