LCOV - code coverage report
Current view: top level - waltz/mib - fd_dbl_buf.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 5 27 18.5 %
Date: 2025-03-20 12:08:36 Functions: 0 20 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_waltz_mib_fd_dbl_buf_h
       2             : #define HEADER_fd_src_waltz_mib_fd_dbl_buf_h
       3             : 
       4             : /* fd_dbl_buf.h provides a concurrent lock-free double buffer.  A double
       5             :    buffer contains two buffers that take turns holding a message for
       6             :    consumers and receiving a new message by a producer.
       7             : 
       8             :    Supports a single producer thread and an arbitrary number of consumer
       9             :    threads.  Optimized for rare updates and frequent polling (e.g. config).
      10             :    Use an fd_tango mcache/dcache pair if you need frequent updates.
      11             : 
      12             :    Currently assumes a memory model that preserves store order across
      13             :    threads (e.g. x86-TSO).  Does not use atomics or hardware fences. */
      14             : 
      15             : #include "../../util/bits/fd_bits.h"
      16             : #if FD_HAS_SSE
      17             : #include <emmintrin.h>
      18             : #endif
      19             : 
      20             : /* FIXME COULD ALLOW FOR IN-PLACE READS WITH PODs BY ADDING A MSG ALIGN ARGUMENT */
      21             : 
      22             : /* fd_dbl_buf_t is the header of a dbl_buf object.  May not be locally
      23             :    declared. */
      24             : 
      25             : union __attribute__((aligned(16UL))) fd_dbl_buf {
      26             : 
      27             :   struct {
      28             :     ulong magic; /* ==FD_DBL_BUF_MAGIC */
      29             :     ulong mtu;
      30             :     ulong buf0;  /* offset to first  buffer from beginning of struct */
      31             :     ulong buf1;  /*   — " —   second              — " —              */
      32             :     ulong seq;   /* latest msg seq no */
      33             :     ulong sz;    /* latest msg size */
      34             :     ulong pad[2];
      35             :     /* objects follow here */
      36             :   };
      37             : 
      38             : # if FD_HAS_SSE
      39             :   struct {
      40             :     __m128i magic_mtu;
      41             :     __m128i buf0_buf1;
      42             :     __m128i seq_sz;
      43             :     __m128i pad2;
      44             :   };
      45             : # endif
      46             : 
      47             : };
      48             : 
      49             : typedef union fd_dbl_buf fd_dbl_buf_t;
      50             : 
      51           0 : #define FD_DBL_BUF_MAGIC (0xa6c6f85d431c03ceUL) /* random */
      52             : 
      53           3 : #define FD_DBL_BUF_ALIGN (16UL)
      54             : #define FD_DBL_BUF_FOOTPRINT(mtu)                                         \
      55           3 :   FD_LAYOUT_FINI( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( FD_LAYOUT_INIT,     \
      56           3 :     FD_DBL_BUF_ALIGN, sizeof(fd_dbl_buf_t) ),                             \
      57           3 :     FD_DBL_BUF_ALIGN, FD_ULONG_ALIGN_UP( mtu, FD_DBL_BUF_ALIGN )<<1UL ),                    \
      58           3 :     FD_DBL_BUF_ALIGN )
      59             : 
      60             : FD_PROTOTYPES_BEGIN
      61             : 
      62             : /* fd_dbl_buf_{align,footprint} describe the memory region of a double
      63             :    buffer.  mtu is the largest possible message size. */
      64             : 
      65             : ulong
      66             : fd_dbl_buf_align( void );
      67             : 
      68             : ulong
      69             : fd_dbl_buf_footprint( ulong mtu );
      70             : 
      71             : /* fd_dbl_buf_new formats a memory region for use as a double buffer.
      72             :    shmem points to the memory region matching fd_dbl_buf_{align,footprint}.
      73             :    Initially, the active object of the double buffer will have sequence
      74             :    number seq0 and zero byte size.  */
      75             : 
      76             : void *
      77             : fd_dbl_buf_new( void * shmem,
      78             :                 ulong  mtu,
      79             :                 ulong  seq0 );
      80             : 
      81             : fd_dbl_buf_t *
      82             : fd_dbl_buf_join( void * shbuf );
      83             : 
      84             : void *
      85             : fd_dbl_buf_leave( fd_dbl_buf_t * buf );
      86             : 
      87             : /* fd_dbl_buf_delete unformats the memory region backing a dbl_buf and
      88             :    releases ownership back to the caller.  Returns shbuf. */
      89             : 
      90             : void *
      91             : fd_dbl_buf_delete( void * shbuf );
      92             : 
      93             : /* fd_dbl_buf_obj_mtu returns the max message size a dbl_buf can store. */
      94             : 
      95             : static inline ulong
      96           0 : fd_dbl_buf_obj_mtu( fd_dbl_buf_t * buf ) {
      97           0 :   return buf->mtu;
      98           0 : }
      99             : 
     100             : /* fd_dbl_buf_seq_query peeks the current sequence number. */
     101             : 
     102             : static inline ulong
     103           0 : fd_dbl_buf_seq_query( fd_dbl_buf_t * buf ) {
     104           0 :   FD_COMPILER_MFENCE();
     105           0 :   ulong seq = FD_VOLATILE_CONST( buf->seq );
     106           0 :   FD_COMPILER_MFENCE();
     107           0 :   return seq;
     108           0 : }
     109             : 
     110             : /* fd_dbl_buf_slot returns a pointer to the buffer for the given sequence
     111             :    number. */
     112             : 
     113             : FD_FN_PURE static inline void *
     114             : fd_dbl_buf_slot( fd_dbl_buf_t * buf,
     115           0 :                  ulong          seq ) {
     116           0 :   return (seq&1) ? ((char *)buf)+buf->buf1 : ((char *)buf)+buf->buf0;
     117           0 : }
     118             : 
     119             : /* fd_dbl_buf_insert appends a message to the double buffer.
     120             : 
     121             :    Note: It is NOT safe to call this function from multiple threads. */
     122             : 
     123             : void
     124             : fd_dbl_buf_insert( fd_dbl_buf_t * buf,
     125             :                    void const *   msg,
     126             :                    ulong          sz );
     127             : 
     128             : /* fd_dbl_buf_try_read does a speculative read the most recent message
     129             :    (from the caller's POV).  The read may be overrun by a writer.  out
     130             :    points to a buffer of fd_dbl_buf_obj_mtu(buf) bytes.  opt_seqp points to
     131             :    a ulong or NULL.
     132             : 
     133             :    On success:
     134             :    - returns the size of the message read
     135             :    - a copy of the message is stored at out
     136             :    - *opt_seqp is set to the msg sequence number (if non-NULL)
     137             : 
     138             :    On failure (due to overrun):
     139             :    - returns ULONG_MAX
     140             :    - out buffer is clobbered
     141             :    - *opt_seq is clobbered (if non-NULL) */
     142             : 
     143             : static inline ulong
     144             : fd_dbl_buf_try_read( fd_dbl_buf_t * buf,
     145             :                      void *         out,
     146           0 :                      ulong *        opt_seqp ) {
     147           0 :   ulong  seq = fd_dbl_buf_seq_query( buf );
     148           0 :   void * src = fd_dbl_buf_slot( buf, seq );
     149           0 :   ulong  sz  = FD_VOLATILE_CONST( buf->sz );
     150           0 :   fd_memcpy( out, src, sz );
     151           0 :   if( FD_UNLIKELY( seq!=fd_dbl_buf_seq_query( buf ) ) ) return ULONG_MAX;
     152           0 :   fd_ulong_store_if( !!opt_seqp, opt_seqp, seq );
     153           0 :   return sz;
     154           0 : }
     155             : 
     156             : /* fd_dbl_buf_read does a blocking */
     157             : 
     158             : ulong
     159             : fd_dbl_buf_read( fd_dbl_buf_t * buf,
     160             :                  void *         obj,
     161             :                  ulong *        opt_seqp );
     162             : 
     163             : FD_PROTOTYPES_END
     164             : 
     165             : #endif /* HEADER_fd_src_waltz_mib_fd_dbl_buf_h */

Generated by: LCOV version 1.14