LCOV - code coverage report
Current view: top level - funkier - fd_funkier_rec.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 10 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 52 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_funk_fd_funkier_rec_h
       2             : #define HEADER_fd_src_funk_fd_funkier_rec_h
       3             : 
       4             : /* This provides APIs for managing funk records.  It is generally not
       5             :    meant to be included directly.  Use fd_funkier.h instead.
       6             : 
       7             :    The following APIs are thread safe and can be interleaved arbirarily
       8             :    across threads:
       9             :      fd_funkier_rec_query_try
      10             :      fd_funkier_rec_query_test
      11             :      fd_funkier_rec_query_try_global
      12             :      fd_funkier_rec_prepare
      13             :      fd_funkier_rec_publish
      14             :      fd_funkier_rec_cancel
      15             :      fd_funkier_rec_remove
      16             : */
      17             : 
      18             : #include "fd_funkier_txn.h" /* Includes fd_funkier_base.h */
      19             : 
      20             : /* FD_FUNKIER_REC_{ALIGN,FOOTPRINT} describe the alignment and footprint of
      21             :    a fd_funkier_rec_t.  ALIGN will be a power of 2, footprint will be a
      22             :    multiple of align.  These are provided to facilitate compile time
      23             :    declarations. */
      24             : 
      25             : #define FD_FUNKIER_REC_ALIGN     (64UL)
      26             : 
      27             : /* FD_FUNKIER_REC_FLAG_* are flags that can be bit-ored together to specify
      28             :    how records are to be interpreted.  The 5 most significant bytes of a
      29             :    rec's flag are reserved to be used in conjunction with the ERASE flag.
      30             : 
      31             :    - ERASE indicates a record in an in-preparation transaction should be
      32             :    erased if and when the in-preparation transaction is published. If
      33             :    set on a published record, it serves as a tombstone.
      34             :    If set, there will be no value resources used by this record. */
      35             : 
      36           0 : #define FD_FUNKIER_REC_FLAG_ERASE (1UL<<0)
      37             : 
      38             : /* FD_FUNKIER_REC_IDX_NULL gives the map record idx value used to represent
      39             :    NULL.  This value also set a limit on how large rec_max can be. */
      40             : 
      41           0 : #define FD_FUNKIER_REC_IDX_NULL (ULONG_MAX)
      42             : 
      43             : /* A fd_funkier_rec_t describes a funk record. */
      44             : 
      45             : struct __attribute__((aligned(FD_FUNKIER_REC_ALIGN))) fd_funkier_rec {
      46             : 
      47             :   /* These fields are managed by the funk's rec_map */
      48             : 
      49             :   fd_funkier_xid_key_pair_t pair;     /* Transaction id and record key pair */
      50             :   ulong                  map_next; /* Internal use by map */
      51             :   ulong                  map_hash; /* Internal use by map */
      52             : 
      53             :   /* These fields are managed by funk.  TODO: Consider using record
      54             :      index compression here (much more debatable than in txn itself). */
      55             : 
      56             :   ulong prev_idx;  /* Record map index of previous record in its transaction */
      57             :   ulong next_idx;  /* Record map index of next record in its transaction */
      58             :   uint  txn_cidx;  /* Compressed transaction map index (or compressed FD_FUNKIER_TXN_IDX if this is in the last published) */
      59             :   uint  tag;       /* Internal use only */
      60             :   ulong flags;     /* Flags that indicate how to interpret a record */
      61             : 
      62             :   /* Note: use of uint here requires FD_FUNKIER_REC_VAL_MAX to be at most
      63             :      UINT_MAX. */
      64             : 
      65             :   uint  val_sz;    /* Num bytes in record value, in [0,val_max] */
      66             :   uint  val_max;   /* Max byte  in record value, in [0,FD_FUNKIER_REC_VAL_MAX], 0 if erase flag set or val_gaddr is 0 */
      67             :   ulong val_gaddr; /* Wksp gaddr on record value if any, 0 if erase flag set or val_max is 0
      68             :                       If non-zero, the region [val_gaddr,val_gaddr+val_max) will be a current fd_alloc allocation (such that it is
      69             :                       has tag wksp_tag) and the owner of the region will be the record. The allocator is
      70             :                       fd_funkier_alloc(). IMPORTANT! HAS NO GUARANTEED ALIGNMENT! */
      71             : 
      72             : 
      73             :   /* Padding to FD_FUNKIER_REC_ALIGN here */
      74             : };
      75             : 
      76             : typedef struct fd_funkier_rec fd_funkier_rec_t;
      77             : 
      78             : FD_STATIC_ASSERT( sizeof(fd_funkier_rec_t) == 2U*FD_FUNKIER_REC_ALIGN, record size is wrong );
      79             : 
      80             : /* fd_funkier_rec_map allows for indexing records by their (xid,key) pair.
      81             :    It is used to store all records of the last published transaction and
      82             :    the records being updated for a transaction that is in-preparation.
      83             :    Published records are stored under the pair (root,key).  (This is
      84             :    done so that publishing a transaction doesn't require updating all
      85             :    transaction id of all the records that were not updated by the
      86             :    publish.) */
      87             : 
      88             : #define POOL_NAME          fd_funkier_rec_pool
      89             : #define POOL_ELE_T         fd_funkier_rec_t
      90             : #define POOL_IDX_T         uint
      91             : #define POOL_NEXT          map_next
      92             : #define POOL_IMPL_STYLE    1
      93             : #include "../util/tmpl/fd_pool_para.c"
      94             : 
      95             : #define MAP_NAME              fd_funkier_rec_map
      96           0 : #define MAP_ELE_T             fd_funkier_rec_t
      97             : #define MAP_KEY_T             fd_funkier_xid_key_pair_t
      98             : #define MAP_KEY               pair
      99           0 : #define MAP_KEY_EQ(k0,k1)     fd_funkier_xid_key_pair_eq((k0),(k1))
     100           0 : #define MAP_KEY_HASH(k0,seed) fd_funkier_xid_key_pair_hash((k0),(seed))
     101           0 : #define MAP_NEXT              map_next
     102             : #define MAP_MEMO              map_hash
     103             : #define MAP_MAGIC             (0xf173da2ce77ecdb0UL) /* Firedancer rec db version 0 */
     104             : #define MAP_MEMOIZE           1
     105             : #define MAP_IMPL_STYLE        1
     106             : #include "../util/tmpl/fd_map_para.c"
     107             : #undef  MAP_MEMOIZE
     108             : #undef  MAP_HASH
     109             : 
     110             : typedef fd_funkier_rec_map_query_t fd_funkier_rec_query_t;
     111             : 
     112             : /* fd_funkier_rec_prepare_t represents a new record that has been
     113             :    prepared but not inserted into the map yet. See documentation for
     114             :    fd_funkier_rec_prepare. */
     115             : 
     116             : struct _fd_funkier_rec_prepare {
     117             :   fd_funkier_t * funk;
     118             :   fd_wksp_t * wksp;
     119             :   fd_funkier_rec_t * rec;
     120             :   ulong * rec_head_idx;
     121             :   ulong * rec_tail_idx;
     122             : };
     123             : 
     124             : typedef struct _fd_funkier_rec_prepare fd_funkier_rec_prepare_t;
     125             : 
     126             : FD_PROTOTYPES_BEGIN
     127             : 
     128             : /* fd_funkier_rec_idx_is_null returns 1 if idx is FD_FUNKIER_REC_IDX_NULL and
     129             :    0 otherwise. */
     130             : 
     131           0 : FD_FN_CONST static inline int fd_funkier_rec_idx_is_null( ulong idx ) { return idx==FD_FUNKIER_REC_IDX_NULL; }
     132             : 
     133             : /* Accessors */
     134             : 
     135             : /* fd_funkier_rec_query_try queries the in-preparation transaction pointed to
     136             :    by txn for the record whose key matches the key pointed to by key.
     137             :    If txn is NULL, the query will be done for the funk's last published
     138             :    transaction.  Returns a pointer to current record on success and NULL
     139             :    on failure.  Reasons for failure include txn is neither NULL nor a
     140             :    pointer to a in-preparation transaction, key is NULL or not a record
     141             :    in the given transaction.
     142             : 
     143             :    The returned pointer is in the caller's address space if the
     144             :    return value is non-NULL.
     145             : 
     146             :    Assumes funk is a current local join (NULL returns NULL), txn is NULL
     147             :    or points to an in-preparation transaction in the caller's address
     148             :    space, key points to a record key in the caller's address space (NULL
     149             :    returns NULL), and no concurrent operations on funk, txn or key.
     150             :    funk retains no interest in key.  The funk retains ownership of any
     151             :    returned record.
     152             : 
     153             :    The query argument remembers the query for later validity testing.
     154             : 
     155             :    This is reasonably fast O(1).
     156             : 
     157             :    Important safety tip!  This function can encounter records
     158             :    that have the ERASE flag set (i.e. are tombstones of erased
     159             :    records). fd_funkier_rec_query_try will still return the record in this
     160             :    case, and the application should check for the flag. */
     161             : 
     162             : fd_funkier_rec_t const *
     163             : fd_funkier_rec_query_try( fd_funkier_t *               funk,
     164             :                           fd_funkier_txn_t const *     txn,
     165             :                           fd_funkier_rec_key_t const * key,
     166             :                           fd_funkier_rec_query_t *     query );
     167             : 
     168             : /* fd_funkier_rec_query_test returns SUCCESS if a prior query still has a
     169             :    valid result. The coding pattern is:
     170             : 
     171             :      for(;;) {
     172             :        fd_funkier_rec_query_t query[1];
     173             :        fd_funkier_rec_t * rec = fd_funkier_rec_query_try( funk, txn, key, query );
     174             :        ... Optimistically read record value ...
     175             :        if( fd_funkier_rec_query_test( query ) == FD_FUNKIER_SUCCESS ) break;
     176             :        ... Clean up and try again ...
     177             :      }
     178             : */
     179             : 
     180             : int fd_funkier_rec_query_test( fd_funkier_rec_query_t * query );
     181             : 
     182             : /* fd_funkier_rec_query_try_global is the same as fd_funkier_rec_query_try but will
     183             :    query txn's ancestors for key from youngest to oldest if key is not
     184             :    part of txn.  As such, the txn of the returned record may not match
     185             :    txn but will be the txn of most recent ancestor with the key
     186             :    otherwise. *txn_out is set to the transaction where the record was
     187             :    found.
     188             : 
     189             :    This is reasonably fast O(in_prep_ancestor_cnt).
     190             : 
     191             :    Important safety tip!  This function can encounter records
     192             :    that have the ERASE flag set (i.e. are tombstones of erased
     193             :    records). fd_funkier_rec_query_try_global will return a NULL in this case
     194             :    but still set *txn_out to the relevant transaction. This behavior
     195             :    differs from fd_funkier_rec_query_try. */
     196             : fd_funkier_rec_t const *
     197             : fd_funkier_rec_query_try_global( fd_funkier_t *               funk,
     198             :                                  fd_funkier_txn_t const *     txn,
     199             :                                  fd_funkier_rec_key_t const * key,
     200             :                                  fd_funkier_txn_t const **    txn_out,
     201             :                                  fd_funkier_rec_query_t *     query );
     202             : 
     203             : /* fd_funkier_rec_{pair,xid,key} returns a pointer in the local address
     204             :    space of the {(transaction id,record key) pair,transaction id,record
     205             :    key} of a live record.  Assumes rec points to a live record in the
     206             :    caller's address space.  The lifetime of the returned pointer is the
     207             :    same as rec.  The value at the pointer will be constant for its
     208             :    lifetime. */
     209             : 
     210           0 : FD_FN_CONST static inline fd_funkier_xid_key_pair_t const * fd_funkier_rec_pair( fd_funkier_rec_t const * rec ) { return &rec->pair;    }
     211           0 : FD_FN_CONST static inline fd_funkier_txn_xid_t const *      fd_funkier_rec_xid ( fd_funkier_rec_t const * rec ) { return rec->pair.xid; }
     212           0 : FD_FN_CONST static inline fd_funkier_rec_key_t const *      fd_funkier_rec_key ( fd_funkier_rec_t const * rec ) { return rec->pair.key; }
     213             : 
     214             : /* fd_funkier_rec_prepare prepares to insert a new record. This call just
     215             :    allocates a record from the pool and initializes it.
     216             :    The application should then fill in the new
     217             :    value. fd_funkier_rec_publish actually does the map insert and
     218             :    should be called once the value is correct. */
     219             : 
     220             : fd_funkier_rec_t *
     221             : fd_funkier_rec_prepare( fd_funkier_t *               funk,
     222             :                         fd_funkier_txn_t *           txn,
     223             :                         fd_funkier_rec_key_t const * key,
     224             :                         fd_funkier_rec_prepare_t *   prepare,
     225             :                         int *                        opt_err );
     226             : 
     227             : /* fd_funkier_rec_publish inserts a prepared record into the record map. */
     228             : 
     229             : void
     230             : fd_funkier_rec_publish( fd_funkier_rec_prepare_t * prepare );
     231             : 
     232             : /* fd_funkier_rec_cancel returns a prepared record to the pool without
     233             :    inserting it. */
     234             : 
     235             : void
     236             : fd_funkier_rec_cancel( fd_funkier_rec_prepare_t * prepare );
     237             : 
     238             : /* fd_funkier_rec_clone copies a record from an ancestor transaction
     239             :    to create a new record in the given transaction. The record can be
     240             :    modified afterward and must then be published. */
     241             : 
     242             : fd_funkier_rec_t *
     243             : fd_funkier_rec_clone( fd_funkier_t *               funk,
     244             :                       fd_funkier_txn_t *           txn,
     245             :                       fd_funkier_rec_key_t const * key,
     246             :                       fd_funkier_rec_prepare_t *   prepare,
     247             :                       int *                        opt_err );
     248             : 
     249             : /* fd_funkier_rec_is_full returns true if no more records can be
     250             :    allocated. */
     251             : 
     252             : int
     253             : fd_funkier_rec_is_full( fd_funkier_t * funk );
     254             : 
     255             : /* fd_funkier_rec_remove removes the live record with the
     256             :    given (xid,key) from funk. Returns FD_FUNKIER_SUCCESS (0) on
     257             :    success and a FD_FUNKIER_ERR_* (negative) on failure.  Reasons for
     258             :    failure include:
     259             : 
     260             :      FD_FUNKIER_ERR_INVAL - bad inputs (NULL funk, NULL xid)
     261             : 
     262             :      FD_FUNKIER_ERR_KEY - the record did not appear to be a live record.
     263             :        Specifically, a record query of funk for rec's (xid,key) pair did
     264             :        not return rec.
     265             : 
     266             :    The record will cease to exist in that transaction and any of
     267             :    transaction's subsequently created descendants (again, assuming no
     268             :    subsequent insert of key).  This type of remove can be done on a
     269             :    published record (assuming the last published transaction is
     270             :    unfrozen). A tombstone is left in funk to track removals as they
     271             :    are published or cancelled.
     272             : 
     273             :    Any information in an erased record is lost.
     274             : 
     275             :    This is a reasonably fast O(1) and fortified against memory
     276             :    corruption. */
     277             : 
     278             : int
     279             : fd_funkier_rec_remove( fd_funkier_t *               funk,
     280             :                        fd_funkier_txn_t *           txn,
     281             :                        fd_funkier_rec_key_t const * key,
     282             :                        fd_funkier_rec_t **          rec_out,
     283             :                        ulong                        erase_data );
     284             : 
     285             : 
     286             : /* When a record is erased there is metadata stored in the five most
     287             :    significant bytes of record flags.  These are helpers to make setting
     288             :    and getting these values simple. The caller is responsible for doing
     289             :    a check on the flag of the record before using the value of the erase
     290             :    data. The 5 least significant bytes of the erase data parameter will
     291             :    be used and set into the erase flag. */
     292             : 
     293             : void
     294             : fd_funkier_rec_set_erase_data( fd_funkier_rec_t * rec, ulong erase_data );
     295             : 
     296             : ulong
     297             : fd_funkier_rec_get_erase_data( fd_funkier_rec_t const * rec );
     298             : 
     299             : /* Iterator which walks all records in all transactions. Usage is:
     300             : 
     301             :   fd_funkier_all_iter_t iter[1];
     302             :   for( fd_funkier_all_iter_new( funk, iter ); !fd_funkier_all_iter_done( iter ); fd_funkier_all_iter_next( iter ) ) {
     303             :     fd_funkier_rec_t const * rec = fd_funkier_all_iter_ele_const( iter );
     304             :     ...
     305             :   }
     306             : */
     307             : 
     308             : struct fd_funkier_all_iter {
     309             :   fd_funkier_rec_map_t rec_map;
     310             :   ulong chain_cnt;
     311             :   ulong chain_idx;
     312             :   fd_funkier_rec_map_iter_t rec_map_iter;
     313             : };
     314             : 
     315             : typedef struct fd_funkier_all_iter fd_funkier_all_iter_t;
     316             : 
     317             : void fd_funkier_all_iter_new( fd_funkier_t * funk, fd_funkier_all_iter_t * iter );
     318             : 
     319             : int fd_funkier_all_iter_done( fd_funkier_all_iter_t * iter );
     320             : 
     321             : void fd_funkier_all_iter_next( fd_funkier_all_iter_t * iter );
     322             : 
     323             : fd_funkier_rec_t const * fd_funkier_all_iter_ele_const( fd_funkier_all_iter_t * iter );
     324             : 
     325             : /* Misc */
     326             : 
     327             : #ifdef FD_FUNKIER_HANDHOLDING
     328             : /* fd_funkier_rec_verify verifies the record map.  Returns FD_FUNKIER_SUCCESS
     329             :    if the record map appears intact and FD_FUNKIER_ERR_INVAL if not (logs
     330             :    details).  Meant to be called as part of fd_funkier_verify.  As such, it
     331             :    assumes funk is non-NULL, fd_funkier_{wksp,txn_map,rec_map} have been
     332             :    verified to work and the txn_map has been verified. */
     333             : 
     334             : int
     335             : fd_funkier_rec_verify( fd_funkier_t * funk );
     336             : #endif
     337             : 
     338             : FD_PROTOTYPES_END
     339             : 
     340             : #endif /* HEADER_fd_src_funk_fd_funkier_rec_h */

Generated by: LCOV version 1.14