LCOV - code coverage report
Current view: top level - waltz/mib - fd_dbl_buf.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 28 0.0 %
Date: 2025-07-10 04:52:38 Functions: 0 24 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             : #include "../../util/log/fd_log.h"
      17             : #if FD_HAS_SSE
      18             : #include <emmintrin.h>
      19             : #endif
      20             : 
      21             : /* FIXME COULD ALLOW FOR IN-PLACE READS WITH PODs BY ADDING A MSG ALIGN ARGUMENT */
      22             : 
      23             : /* fd_dbl_buf_t is the header of a dbl_buf object.  May not be locally
      24             :    declared. */
      25             : 
      26             : union __attribute__((aligned(16UL))) fd_dbl_buf {
      27             : 
      28             :   struct {
      29             :     ulong magic; /* ==FD_DBL_BUF_MAGIC */
      30             :     ulong mtu;
      31             :     ulong buf0;  /* offset to first  buffer from beginning of struct */
      32             :     ulong buf1;  /*   — " —   second              — " —              */
      33             :     ulong seq;   /* latest msg seq no */
      34             :     ulong sz;    /* latest msg size */
      35             :     ulong pad[2];
      36             :     /* objects follow here */
      37             :   };
      38             : 
      39             : # if FD_HAS_SSE
      40             :   struct {
      41             :     __m128i magic_mtu;
      42             :     __m128i buf0_buf1;
      43             :     __m128i seq_sz;
      44             :     __m128i pad2;
      45             :   };
      46             : # endif
      47             : 
      48             : };
      49             : 
      50             : typedef union fd_dbl_buf fd_dbl_buf_t;
      51             : 
      52           0 : #define FD_DBL_BUF_MAGIC (0xa6c6f85d431c03ceUL) /* random */
      53             : 
      54           0 : #define FD_DBL_BUF_ALIGN (16UL)
      55             : #define FD_DBL_BUF_FOOTPRINT(mtu)                                         \
      56           0 :   FD_LAYOUT_FINI( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( FD_LAYOUT_INIT,     \
      57           0 :     FD_DBL_BUF_ALIGN, sizeof(fd_dbl_buf_t) ),                             \
      58           0 :     FD_DBL_BUF_ALIGN, FD_ULONG_ALIGN_UP( mtu, FD_DBL_BUF_ALIGN )<<1UL ),                    \
      59           0 :     FD_DBL_BUF_ALIGN )
      60             : 
      61             : FD_PROTOTYPES_BEGIN
      62             : 
      63             : /* fd_dbl_buf_{align,footprint} describe the memory region of a double
      64             :    buffer.  mtu is the largest possible message size. */
      65             : 
      66             : ulong
      67             : fd_dbl_buf_align( void );
      68             : 
      69             : ulong
      70             : fd_dbl_buf_footprint( ulong mtu );
      71             : 
      72             : /* fd_dbl_buf_new formats a memory region for use as a double buffer.
      73             :    shmem points to the memory region matching fd_dbl_buf_{align,footprint}.
      74             :    Initially, the active object of the double buffer will have sequence
      75             :    number seq0 and zero byte size.  */
      76             : 
      77             : void *
      78             : fd_dbl_buf_new( void * shmem,
      79             :                 ulong  mtu,
      80             :                 ulong  seq0 );
      81             : 
      82             : fd_dbl_buf_t *
      83             : fd_dbl_buf_join( void * shbuf );
      84             : 
      85             : void *
      86             : fd_dbl_buf_leave( fd_dbl_buf_t * buf );
      87             : 
      88             : /* fd_dbl_buf_delete unformats the memory region backing a dbl_buf and
      89             :    releases ownership back to the caller.  Returns shbuf. */
      90             : 
      91             : void *
      92             : fd_dbl_buf_delete( void * shbuf );
      93             : 
      94             : /* fd_dbl_buf_obj_mtu returns the max message size a dbl_buf can store. */
      95             : 
      96             : static inline ulong
      97           0 : fd_dbl_buf_obj_mtu( fd_dbl_buf_t * buf ) {
      98           0 :   return buf->mtu;
      99           0 : }
     100             : 
     101             : /* fd_dbl_buf_seq_query peeks the current sequence number. */
     102             : 
     103             : static inline ulong
     104           0 : fd_dbl_buf_seq_query( fd_dbl_buf_t * buf ) {
     105           0 :   FD_COMPILER_MFENCE();
     106           0 :   ulong seq = FD_VOLATILE_CONST( buf->seq );
     107           0 :   FD_COMPILER_MFENCE();
     108           0 :   return seq;
     109           0 : }
     110             : 
     111             : /* fd_dbl_buf_slot returns a pointer to the buffer for the given sequence
     112             :    number. */
     113             : 
     114             : FD_FN_PURE static inline void *
     115             : fd_dbl_buf_slot( fd_dbl_buf_t * buf,
     116           0 :                  ulong          seq ) {
     117           0 :   return (seq&1) ? ((char *)buf)+buf->buf1 : ((char *)buf)+buf->buf0;
     118           0 : }
     119             : 
     120             : /* fd_dbl_buf_insert appends a message to the double buffer.
     121             : 
     122             :    Note: It is NOT safe to call this function from multiple threads. */
     123             : 
     124             : void
     125             : fd_dbl_buf_insert( fd_dbl_buf_t * buf,
     126             :                    void const *   msg,
     127             :                    ulong          sz );
     128             : 
     129             : /* fd_dbl_buf_try_read does a speculative read the most recent message
     130             :    (from the caller's POV).  The read may be overrun by a writer.  out
     131             :    points to a buffer of fd_dbl_buf_obj_mtu(buf) bytes.  opt_seqp points to
     132             :    a ulong or NULL.
     133             : 
     134             :    On success:
     135             :    - returns the size of the message read
     136             :    - a copy of the message is stored at out
     137             :    - *opt_seqp is set to the msg sequence number (if non-NULL)
     138             : 
     139             :    On failure (due to overrun):
     140             :    - returns ULONG_MAX
     141             :    - out buffer is clobbered
     142             :    - *opt_seq is clobbered (if non-NULL) */
     143             : 
     144             : static inline ulong
     145             : fd_dbl_buf_try_read( fd_dbl_buf_t * buf,
     146             :                      void *         out,
     147             :                      ulong          out_sz,
     148           0 :                      ulong *        opt_seqp ) {
     149           0 :   ulong  seq = fd_dbl_buf_seq_query( buf );
     150           0 :   void * src = fd_dbl_buf_slot( buf, seq );
     151           0 :   ulong  sz  = FD_VOLATILE_CONST( buf->sz );
     152           0 :   if( out_sz<sz ) FD_LOG_ERR(( "fd_dbl_buf_try_read failed: output buffer too small: out_sz: %lu, sz: %lu", out_sz, sz ));
     153           0 :   fd_memcpy( out, src, sz );
     154           0 :   if( FD_UNLIKELY( seq!=fd_dbl_buf_seq_query( buf ) ) ) return ULONG_MAX;
     155           0 :   fd_ulong_store_if( !!opt_seqp, opt_seqp, seq );
     156           0 :   return sz;
     157           0 : }
     158             : 
     159             : /* fd_dbl_buf_read does a blocking */
     160             : 
     161             : ulong
     162             : fd_dbl_buf_read( fd_dbl_buf_t * buf,
     163             :                  ulong          buf_sz,
     164             :                  void *         obj,
     165             :                  ulong *        opt_seqp );
     166             : 
     167             : FD_PROTOTYPES_END
     168             : 
     169             : #endif /* HEADER_fd_src_waltz_mib_fd_dbl_buf_h */

Generated by: LCOV version 1.14