LCOV - code coverage report
Current view: top level - util/wksp - fd_wksp_admin.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 379 416 91.1 %
Date: 2026-09-17 04:28:31 Functions: 18 19 94.7 %

          Line data    Source code
       1             : #include "fd_wksp_private.h"
       2             : 
       3             : int
       4    60387780 : fd_wksp_private_lock( fd_wksp_t * wksp ) {
       5             : # if FD_WKSP_LOCK_RECLAIM
       6             :   int   warning = 0;
       7             : #endif
       8    60387780 :   ulong me      = fd_log_group_id();
       9             : 
      10    60387780 :   ulong * _owner = &wksp->owner;
      11    60387780 :   for(;;) {
      12             : 
      13             :     /* Note that we emulate CAS on platforms without FD_HAS_ATOMIC
      14             :        to minimize the amount of code differences we have to test.  On
      15             :        platforms without FD_HAS_ATOMIC, a workspace should not be used
      16             :        concurrently though. */
      17             : 
      18    60387780 :     FD_COMPILER_MFENCE();
      19    60387780 : #   if FD_HAS_ATOMIC
      20    60387780 :     ulong pid = FD_ATOMIC_CAS( _owner, ULONG_MAX, me );
      21             : #   else
      22             :     ulong pid = FD_VOLATILE_CONST( *_owner );
      23             :     if( pid==ULONG_MAX ) FD_VOLATILE( *_owner ) = me;
      24             : #   endif
      25    60387780 :     FD_COMPILER_MFENCE();
      26             : 
      27    60387780 :     if( FD_LIKELY( pid==ULONG_MAX ) ) return FD_WKSP_SUCCESS;
      28             : 
      29             : # if FD_WKSP_LOCK_RECLAIM
      30             :     int status = fd_log_group_id_query( pid );
      31             :     if( FD_UNLIKELY( status==FD_LOG_GROUP_ID_QUERY_DEAD ) ) { /* A process died while holding the lock, try to recover the lock */
      32             : 
      33             :       FD_COMPILER_MFENCE();
      34             : #     if FD_HAS_ATOMIC
      35             :       ulong cur = FD_ATOMIC_CAS( _owner, pid, me );
      36             : #     else
      37             :       ulong cur = FD_VOLATILE_CONST( *_owner );
      38             :       if( cur==pid ) FD_VOLATILE( *_owner ) = me;
      39             : #     endif
      40             :       FD_COMPILER_MFENCE();
      41             : 
      42             :       if( FD_LIKELY( cur==pid ) ) { /* We recovered the lock from the dead pid, try to fix up incomplete ops */
      43             : 
      44             :         FD_LOG_WARNING(( "Process %lu died in an operation on wksp %s; verifying", pid, wksp->name ));
      45             :         if( FD_LIKELY( !fd_wksp_verify( wksp ) ) ) { /* logs details of issues detected */
      46             :           FD_LOG_NOTICE(( "wksp verified" ));
      47             :           return FD_WKSP_SUCCESS;
      48             :         }
      49             : 
      50             :         FD_LOG_WARNING(( "Issues detected; rebuilding" ));
      51             :         if( FD_UNLIKELY( fd_wksp_rebuild( wksp, wksp->seed ) ) ) { /* Rebuild failed (logs details of issues detected) */
      52             :           /* Return control of the lock to the previous owner */
      53             :           FD_COMPILER_MFENCE();
      54             :           FD_VOLATILE( *_owner ) = pid;
      55             :           FD_COMPILER_MFENCE();
      56             :           FD_LOG_WARNING(( "corrupt wksp detected" ));
      57             :           return FD_WKSP_ERR_CORRUPT;
      58             :         }
      59             : 
      60             :         FD_LOG_NOTICE(( "wksp rebuilt" ));
      61             :         return FD_WKSP_SUCCESS;
      62             : 
      63             :       }
      64             : 
      65             :       /* Somebody beat us to recovering the lock ... try again */
      66             : 
      67             :     } else if( FD_UNLIKELY( status!=FD_LOG_GROUP_ID_QUERY_LIVE ) ) { /* Unclear pid status ... issue a warning and try again */
      68             : 
      69             :       if( FD_UNLIKELY( !warning ) ) {
      70             :         FD_LOG_WARNING(( "wksp %s is owned by unknown pid %li; attempting to recover", wksp->name, pid ));
      71             :         warning = 1;
      72             :       }
      73             : 
      74             :     }
      75             : 
      76             :     /* At this point, either another thread in this process has the
      77             :        lock, another active thread in another process has the lock,
      78             :        another unknown status thread in other process has the lock or
      79             :        another thread beat us to reclaim the lock from a dead process.
      80             :        In any case, we don't have the lock.  Wait a while to limit O/S
      81             :        contention and try again. */
      82             : 
      83             :     FD_YIELD();
      84             : # else
      85             : 
      86             :     /* If we are running without FD_WKSP_LOCK_RECLAIM then it is assumed
      87             :        that the contention is caused by a tile pinned to another core,
      88             :        and that this core is itself pinned so spin locking is best. */
      89           0 :     FD_SPIN_PAUSE();
      90             : 
      91           0 : #endif
      92           0 :   }
      93             : 
      94             :   /* never get here */
      95    60387780 : }
      96             : 
      97             : /* Public APIs ********************************************************/
      98             : 
      99             : ulong
     100             : fd_wksp_part_max_est( ulong footprint,
     101         336 :                       ulong sz_typical ) {
     102         336 :   footprint       = fd_ulong_align_dn( footprint, FD_WKSP_ALIGN );
     103         336 :   ulong data_end  = footprint - 1UL;
     104         336 :   ulong pinfo_off = fd_wksp_private_pinfo_off();
     105         336 :   ulong consumed  = sizeof(fd_wksp_private_pinfo_t) + sz_typical;
     106         336 :   ulong part_max  = (data_end - pinfo_off) / (consumed + (ulong)!consumed); /* avoid div-by-zero */
     107         336 :   if( FD_UNLIKELY( (!footprint) | (!sz_typical) | (sz_typical>consumed) | (pinfo_off>data_end) ) ) return 0UL;
     108         327 :   return fd_ulong_min( part_max, FD_WKSP_PRIVATE_PINFO_IDX_NULL );
     109         336 : }
     110             : 
     111             : ulong
     112             : fd_wksp_data_max_est( ulong footprint,
     113         303 :                       ulong part_max ) {
     114         303 :   footprint = fd_ulong_align_dn( footprint, FD_WKSP_ALIGN );
     115         303 :   ulong data_end  = footprint - 1UL;
     116         303 :   ulong data_off  = fd_wksp_private_data_off( part_max );
     117         303 :   if( FD_UNLIKELY( (!part_max) | (part_max>FD_WKSP_PRIVATE_PINFO_IDX_NULL) |
     118         303 :                    (part_max > ((ULONG_MAX - fd_wksp_private_pinfo_off())/sizeof(fd_wksp_private_pinfo_t))) | /* covered above */
     119         303 :                    (!footprint) | (data_off>=data_end) ) ) return 0UL;
     120         285 :   return data_end - data_off;
     121         303 : }
     122             : 
     123             : ulong
     124           3 : fd_wksp_align( void ) {
     125           3 :   return FD_WKSP_ALIGN;
     126           3 : }
     127             : 
     128             : ulong
     129             : fd_wksp_footprint( ulong part_max,
     130        1560 :                    ulong data_max ) {
     131        1560 :   ulong data_off = fd_wksp_private_data_off( part_max );
     132        1560 :   if( FD_UNLIKELY( (!part_max) | (part_max>FD_WKSP_PRIVATE_PINFO_IDX_NULL) | (!data_max) |
     133        1560 :                    (part_max > ((ULONG_MAX - fd_wksp_private_pinfo_off())/sizeof(fd_wksp_private_pinfo_t))) | /* Covered above */
     134        1560 :                    (data_max > (ULONG_MAX - FD_WKSP_ALIGN + 1UL - data_off - 1UL)                         ) ) ) return 0UL;
     135        1491 :   return fd_ulong_align_up( data_off + data_max + 1UL, FD_WKSP_ALIGN );
     136        1560 : }
     137             : 
     138             : void *
     139             : fd_wksp_new( void *       shmem,
     140             :              char const * name,
     141             :              uint         seed,
     142             :              ulong        part_max,
     143         288 :              ulong        data_max ) {
     144         288 :   fd_wksp_t * wksp = (fd_wksp_t *)shmem;
     145             : 
     146         288 :   if( FD_UNLIKELY( !wksp ) ) {
     147           3 :     FD_LOG_WARNING(( "NULL shmem" ));
     148           3 :     return NULL;
     149           3 :   }
     150             : 
     151         285 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)wksp, FD_WKSP_ALIGN ) ) ) {
     152           3 :     FD_LOG_WARNING(( "bad align" ));
     153           3 :     return NULL;
     154           3 :   }
     155             : 
     156         282 :   ulong name_len = fd_shmem_name_len( name );
     157         282 :   if( FD_UNLIKELY( !name_len ) ) {
     158           3 :     FD_LOG_WARNING(( "bad name" ));
     159           3 :     return NULL;
     160           3 :   }
     161             : 
     162         279 :   ulong footprint = fd_wksp_footprint( part_max, data_max );
     163         279 :   if( FD_UNLIKELY( !footprint ) ) {
     164          12 :     FD_LOG_WARNING(( "bad part_max and/or data_max" ));
     165          12 :     return NULL;
     166          12 :   }
     167             : 
     168         267 :   fd_memset( wksp, 0, fd_wksp_footprint( part_max, 1UL ) );
     169             : 
     170         267 :   wksp->part_max       = part_max;
     171         267 :   wksp->data_max       = data_max;
     172         267 :   wksp->gaddr_lo       = fd_wksp_private_data_off( part_max );
     173         267 :   wksp->gaddr_hi       = wksp->gaddr_lo + data_max;
     174         267 :   fd_memcpy( wksp->name, name, name_len+1UL );
     175         267 :   wksp->seed           = seed;
     176         267 :   wksp->idle_top_cidx  = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
     177         267 :   wksp->part_head_cidx = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
     178         267 :   wksp->part_tail_cidx = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
     179         267 :   wksp->part_used_cidx = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
     180         267 :   wksp->part_free_cidx = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
     181         267 :   wksp->cycle_tag      = 4UL;  /* Verify uses tags 0-3 */
     182         267 :   wksp->owner          = 0UL;  /* Mark as locked and in construction */
     183             : 
     184             :   /* Note that wksp->owner was set to zero above, "locking" the wksp by
     185             :      group_id 0.  And the memset above set all the partition tags to
     186             :      zero such that there are no allocated partitions.  So once we set
     187             :      magic below, we can finish the initialization by rebuilding and
     188             :      unlocking.  Since fd_log_group_id is non-zero, the zero owner
     189             :      indicates to any remote observer of the shared memory region that
     190             :      the wksp is being built for the first time. */
     191             : 
     192         267 :   FD_COMPILER_MFENCE();
     193         267 :   FD_VOLATILE( wksp->magic ) = FD_WKSP_MAGIC;
     194         267 :   FD_COMPILER_MFENCE();
     195             : 
     196         267 :   int err = fd_wksp_rebuild( wksp, seed );
     197         267 :   if( FD_UNLIKELY( err ) ) { /* Should be impossible at this point */
     198             : 
     199           0 :     FD_COMPILER_MFENCE();
     200           0 :     FD_VOLATILE( wksp->magic ) = 0UL;
     201           0 :     FD_COMPILER_MFENCE();
     202             : 
     203           0 :     FD_LOG_WARNING(( "fd_wksp_rebuild failed (%i-%s)", err, fd_wksp_strerror( err ) ));
     204           0 :     return NULL;
     205           0 :   }
     206             : 
     207             :   #if FD_HAS_DEEPASAN
     208             :   fd_wksp_private_asan_poison_data( wksp );
     209             :   #endif
     210             : 
     211         267 :   fd_wksp_private_unlock( wksp );
     212             : 
     213         267 :   return wksp;
     214         267 : }
     215             : 
     216             : fd_wksp_t *
     217        1839 : fd_wksp_join( void * shwksp ) {
     218        1839 :   fd_wksp_t * wksp = (fd_wksp_t *)shwksp;
     219             : 
     220        1839 :   if( FD_UNLIKELY( !wksp ) ) {
     221           3 :     FD_LOG_WARNING(( "NULL shwksp" ));
     222           3 :     return NULL;
     223           3 :   }
     224             : 
     225        1836 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)wksp, FD_WKSP_ALIGN ) ) ) {
     226           3 :     FD_LOG_WARNING(( "bad align" ));
     227           3 :     return NULL;
     228           3 :   }
     229             : 
     230        1833 :   if( FD_UNLIKELY( wksp->magic!=FD_WKSP_MAGIC ) ) {
     231           3 :     FD_LOG_WARNING(( "bad magic" ));
     232           3 :     return NULL;
     233           3 :   }
     234             : 
     235        1830 :   return wksp;
     236        1833 : }
     237             : 
     238             : void *
     239        1605 : fd_wksp_leave( fd_wksp_t * wksp ) {
     240        1605 :   if( FD_UNLIKELY( !wksp ) ) {
     241           3 :     FD_LOG_WARNING(( "NULL wksp" ));
     242           3 :     return NULL;
     243           3 :   }
     244             : 
     245        1602 :   return (void *)wksp;
     246        1605 : }
     247             : 
     248             : void *
     249         126 : fd_wksp_delete( void * shwksp ) {
     250         126 :   fd_wksp_t * wksp = (fd_wksp_t *)shwksp;
     251             : 
     252         126 :   if( FD_UNLIKELY( !wksp ) ) {
     253           3 :     FD_LOG_WARNING(( "NULL shwksp" ));
     254           3 :     return NULL;
     255           3 :   }
     256             : 
     257         123 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)wksp, FD_WKSP_ALIGN ) ) ) {
     258           3 :     FD_LOG_WARNING(( "bad align" ));
     259           3 :     return NULL;
     260           3 :   }
     261             : 
     262         120 :   if( FD_UNLIKELY( wksp->magic!=FD_WKSP_MAGIC ) ) {
     263           3 :     FD_LOG_WARNING(( "bad magic" ));
     264           3 :     return NULL;
     265           3 :   }
     266             : 
     267             :   /* TODO: consider testing owner */
     268             : 
     269         117 :   FD_COMPILER_MFENCE();
     270         117 :   FD_VOLATILE( wksp->magic ) = 0UL;
     271         117 :   FD_COMPILER_MFENCE();
     272             : 
     273             : # if FD_HAS_DEEPASAN
     274             :   /* Unpoison entire wksp region. */
     275             :   ulong footprint = fd_wksp_footprint( wksp->part_max, wksp->data_max );
     276             :   void * wksp_data = (void*)((ulong)wksp + fd_wksp_private_pinfo_off());
     277             :   fd_asan_unpoison( wksp_data, footprint - fd_wksp_private_pinfo_off());
     278             : # endif
     279             : 
     280         117 :   return wksp;
     281         120 : }
     282             : 
     283           3 : char const * fd_wksp_name    ( fd_wksp_t const * wksp ) { return wksp->name;     }
     284          84 : uint         fd_wksp_seed    ( fd_wksp_t const * wksp ) { return wksp->seed;     }
     285           3 : ulong        fd_wksp_part_max( fd_wksp_t const * wksp ) { return wksp->part_max; }
     286           3 : ulong        fd_wksp_data_max( fd_wksp_t const * wksp ) { return wksp->data_max; }
     287           6 : ulong        fd_wksp_gaddr_lo( fd_wksp_t const * wksp ) { return wksp->gaddr_lo; }
     288          18 : ulong        fd_wksp_gaddr_hi( fd_wksp_t const * wksp ) { return wksp->gaddr_hi; }
     289             : 
     290             : ulong
     291           0 : fd_wksp_owner( fd_wksp_t const * wksp ) {
     292           0 :   FD_COMPILER_MFENCE();
     293           0 :   ulong owner = FD_VOLATILE_CONST( wksp->owner );
     294           0 :   FD_COMPILER_MFENCE();
     295           0 :   return owner;
     296           0 : }
     297             : 
     298             : char const *
     299          48 : fd_wksp_strerror( int err ) {
     300          48 :   switch( err ) {
     301           3 :   case FD_WKSP_SUCCESS:     return "success";
     302          12 :   case FD_WKSP_ERR_INVAL:   return "inval";
     303          30 :   case FD_WKSP_ERR_FAIL:    return "fail";
     304           3 :   case FD_WKSP_ERR_CORRUPT: return "corrupt";
     305           0 :   default: break;
     306          48 :   }
     307           0 :   return "unknown";
     308          48 : }
     309             : 
     310             : int
     311          21 : fd_wksp_verify( fd_wksp_t * wksp ) {
     312             : 
     313      304122 : # define TEST(c) do {                                                                             \
     314      304122 :     if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_WKSP_ERR_CORRUPT; } \
     315      304122 :   } while(0)
     316             : 
     317             :   /* Validate metadata */
     318             : 
     319          21 :   TEST( wksp );
     320          21 :   TEST( wksp->magic==FD_WKSP_MAGIC );
     321             : 
     322          21 :   ulong part_max = wksp->part_max;
     323          21 :   ulong data_max = wksp->data_max;
     324          21 :   TEST( fd_wksp_footprint( part_max, data_max ) );
     325             : 
     326          21 :   ulong gaddr_lo = wksp->gaddr_lo; TEST( gaddr_lo==fd_wksp_private_data_off( part_max ) );
     327          21 :   ulong gaddr_hi = wksp->gaddr_hi; TEST( gaddr_hi==gaddr_lo+data_max                    );
     328             : 
     329          21 :   TEST( fd_shmem_name_len( wksp->name ) );
     330             : 
     331             :   /* seed is arbitrary */
     332             : 
     333          21 :   TEST( wksp->cycle_tag >= 4UL );
     334             : 
     335             :   /* TODO: consider verifying owner */
     336             : 
     337          21 :   fd_wksp_private_pinfo_t * pinfo = fd_wksp_private_pinfo( wksp );
     338             : 
     339             :   /* Clear out cycle tags */
     340             : 
     341      148845 :   for( ulong i=0UL; i<part_max; i++ ) pinfo[ i ].cycle_tag = 0UL;
     342             : 
     343             :   /* Verify the idle stack */
     344             : 
     345          21 :   ulong idle_cnt = 0UL;
     346             : 
     347          21 :   do {
     348          21 :     ulong i = fd_wksp_private_pinfo_idx( wksp->idle_top_cidx );
     349      148317 :     while( !fd_wksp_private_pinfo_idx_is_null( i ) ) {
     350             : 
     351             :       /* Visit i.  Note that i has not been validated yet. */
     352             : 
     353      148296 :       TEST( i<part_max            ); /* Validate i */
     354      148296 :       TEST( !pinfo[ i ].cycle_tag ); /* Make sure not visited before */
     355      148296 :       pinfo[ i ].cycle_tag = 1UL;    /* Mark as visited in idle stack */
     356      148296 :       idle_cnt++;                    /* Update the idle cnt */
     357             : 
     358             :       /* Advance to the next idle */
     359             : 
     360      148296 :       i = fd_wksp_private_pinfo_idx( pinfo[ i ].parent_cidx );
     361      148296 :     }
     362          21 :   } while(0);
     363             : 
     364             :   /* Idle stack looks intact, verify partitioning */
     365             : 
     366          21 :   ulong free_cnt = 0UL;
     367          21 :   ulong used_cnt = 0UL;
     368             : 
     369          21 :   do {
     370          21 :     ulong j = FD_WKSP_PRIVATE_PINFO_IDX_NULL;
     371          21 :     ulong i = fd_wksp_private_pinfo_idx( wksp->part_head_cidx );
     372          21 :     ulong g = gaddr_lo;
     373             : 
     374          21 :     int last_free = 0;
     375             : 
     376         549 :     while( !fd_wksp_private_pinfo_idx_is_null( i ) ) {
     377             : 
     378             :       /* At this point, we last visited j.  Visit i.  Note that j has
     379             :          been validated but i has not. */
     380             : 
     381         528 :       TEST( i<part_max                                           ); /* Validate i */
     382         528 :       TEST( pinfo[ i ].gaddr_lo==g                               ); /* Make sure partition is tightly adjacent to previous */
     383         528 :       TEST( pinfo[ i ].gaddr_hi> g                               ); /* Make sure partition size is non-zero */
     384         528 :       TEST( fd_wksp_private_pinfo_idx( pinfo[ i ].prev_cidx )==j ); /* Make sure correct prev partition */
     385         528 :       TEST( !pinfo[ i ].cycle_tag                                ); /* Make sure not visited before */
     386         528 :       pinfo[ i ].cycle_tag = 2UL;                                   /* Mark as visited in partitioning */
     387             : 
     388         528 :       g = pinfo[ i ].gaddr_hi;                                      /* Extract where the next partition should start */
     389         528 :       int is_free = !pinfo[ i ].tag;                                /* Determine if this partition is free or used */
     390         528 :       TEST( !(last_free & is_free) );                               /* Make sure no adjacent free partitions */
     391         528 :       free_cnt += (ulong) is_free;                                  /* Update the free cnt */
     392         528 :       used_cnt += (ulong)!is_free;                                  /* Update the used cnt */
     393             : 
     394             :       /* Advance to the next partition */
     395             : 
     396         528 :       last_free = is_free;
     397             : 
     398         528 :       j = i;
     399         528 :       i = fd_wksp_private_pinfo_idx( pinfo[ i ].next_cidx );
     400         528 :     }
     401             : 
     402          21 :     TEST( fd_wksp_private_pinfo_idx( wksp->part_tail_cidx )==j ); /* Make sure correct partition tail */
     403          21 :     TEST( g==gaddr_hi );                                          /* Make sure complete partitioning */
     404          21 :     TEST( (idle_cnt + free_cnt + used_cnt)==part_max );           /* Make sure no lost idle partitions */
     405          21 :   } while(0);
     406             : 
     407             :   /* Idle stack and partitioning look intact, validate used treap */
     408             : 
     409          21 :   do {
     410          21 :     ulong visit_cnt = 0UL;
     411             : 
     412          21 :     ulong i = fd_wksp_private_pinfo_idx( wksp->part_used_cidx );
     413          21 :     ulong s = FD_WKSP_PRIVATE_PINFO_IDX_NULL;
     414          21 :     ulong g = gaddr_lo;
     415             : 
     416          21 :     if( !fd_wksp_private_pinfo_idx_is_null( i ) ) {
     417          12 :       TEST( i<part_max );                                             /* Validate i */
     418          12 :       TEST( fd_wksp_private_pinfo_idx( pinfo[ i ].parent_cidx )==s ); /* Validate parent */
     419          12 :     }
     420             : 
     421         627 :     for(;;) {
     422             : 
     423             :       /* At this point i is and everything on stack is validated */
     424             : 
     425         627 :       if( fd_wksp_private_pinfo_idx_is_null( i ) ) {
     426         324 :         if( fd_wksp_private_pinfo_idx_is_null( s ) ) break; /* Done */
     427             : 
     428             :         /* Pop stack */
     429             : 
     430         303 :         i = s;
     431         303 :         s = fd_wksp_private_pinfo_idx( pinfo[ i ].stack_cidx );
     432             : 
     433             :         /* Visit i */
     434             : 
     435         303 :         ulong p = fd_wksp_private_pinfo_idx( pinfo[ i ].parent_cidx ); /* Extract the parent */
     436             : 
     437         303 :         TEST( pinfo[ i ].gaddr_lo>=g    );            /* Make sure this starts after last visited */
     438         303 :         TEST( pinfo[ i ].tag            );            /* Make sure tagged as a used partition */
     439         303 :         TEST( pinfo[ i ].cycle_tag==2UL );            /* Make sure in partitioning and not visited yet this traversal */
     440         303 :         if( !fd_wksp_private_pinfo_idx_is_null( p ) ) /* Make sure heap property satisfied */
     441         291 :           TEST( pinfo[ p ].heap_prio >= pinfo[ i ].heap_prio );
     442             : 
     443         303 :         TEST( !pinfo[ i ].in_same );                  /* Make sure unique */
     444         303 :         TEST( fd_wksp_private_pinfo_idx_is_null( fd_wksp_private_pinfo_idx( pinfo[ i ].same_cidx ) ) ); /* " */
     445             : 
     446         303 :         pinfo[ i ].cycle_tag = 3UL;                   /* Mark as visited this traversal */
     447         303 :         visit_cnt++;                                  /* Update the visit cnt */
     448         303 :         g = pinfo[ i ].gaddr_hi;                      /* Get minimum start for next partition */
     449             : 
     450             :         /* Traverse the right subtree */
     451             : 
     452         303 :         p = i;
     453         303 :         i = fd_wksp_private_pinfo_idx( pinfo[ i ].right_cidx );
     454         303 :         if( !fd_wksp_private_pinfo_idx_is_null( i ) ) {
     455         150 :           TEST( i<part_max );                                             /* Validate i */
     456         150 :           TEST( fd_wksp_private_pinfo_idx( pinfo[ i ].parent_cidx )==p ); /* Validate parent */
     457         150 :         }
     458             : 
     459         303 :       } else {
     460             : 
     461             :         /* At this point i and everything on the stack is validated.
     462             :            Push i to the stack and recurse on the left subtree. */
     463             : 
     464         303 :         pinfo[ i ].stack_cidx = fd_wksp_private_pinfo_cidx( s );
     465         303 :         s = i;
     466         303 :         i = fd_wksp_private_pinfo_idx( pinfo[ i ].left_cidx );
     467         303 :         if( !fd_wksp_private_pinfo_idx_is_null( i ) ) {
     468         141 :           TEST( i<part_max );                                             /* Validate i */
     469         141 :           TEST( fd_wksp_private_pinfo_idx( pinfo[ i ].parent_cidx )==s ); /* Validate parent */
     470         141 :         }
     471             : 
     472         303 :       }
     473         627 :     }
     474             : 
     475          21 :     TEST( visit_cnt==used_cnt ); /* Make sure all used partitions in used treap */
     476          21 :   } while(0);
     477             : 
     478             :   /* Idle stack, partitioning and used treap look intact, validate the
     479             :      free treap. */
     480             : 
     481          21 :   do {
     482          21 :     ulong visit_cnt = 0UL;
     483             : 
     484          21 :     ulong i  = fd_wksp_private_pinfo_idx( wksp->part_free_cidx );
     485          21 :     ulong s  = FD_WKSP_PRIVATE_PINFO_IDX_NULL;
     486          21 :     ulong sz = 0UL;
     487             : 
     488          21 :     if( !fd_wksp_private_pinfo_idx_is_null( i ) ) {
     489          21 :       TEST( i<part_max );                                             /* Validate i */
     490          21 :       TEST( fd_wksp_private_pinfo_idx( pinfo[ i ].parent_cidx )==s ); /* Validate parent */
     491          21 :     }
     492             : 
     493         309 :     for(;;) {
     494             : 
     495             :       /* At this point i and everything on the stack is validated */
     496             : 
     497         309 :       if( fd_wksp_private_pinfo_idx_is_null( i ) ) {
     498         165 :         if( fd_wksp_private_pinfo_idx_is_null( s ) ) break; /* Done */
     499             : 
     500             :         /* Pop stack */
     501             : 
     502         144 :         i = s;
     503         144 :         s = fd_wksp_private_pinfo_idx( pinfo[ i ].stack_cidx );
     504             : 
     505             :         /* Visit i */
     506             : 
     507         144 :         ulong p   = fd_wksp_private_pinfo_idx( pinfo[ i ].parent_cidx ); /* Extract the parent */
     508         144 :         ulong isz = fd_wksp_private_pinfo_sz( pinfo + i );               /* Extract the size */
     509             : 
     510         144 :         TEST( isz>sz              ); /* Make sure this partition i larger than previous */
     511         144 :         TEST( !pinfo[ i ].tag     ); /* Make sure tagged as a free partition */
     512         144 :         TEST( !pinfo[ i ].in_same ); /* Make sure marked as not in same */
     513             : 
     514         144 :         if( !fd_wksp_private_pinfo_idx_is_null( p ) ) { /* Make sure heap property satisfied */
     515         123 :           TEST( pinfo[ p ].heap_prio >= pinfo[ i ].heap_prio );
     516         123 :         }
     517             : 
     518         144 :         sz = isz; /* Update largest size partition seen so far */
     519             : 
     520             :         /* Traverse all same sized partitions */
     521             : 
     522         144 :         ulong j = i;
     523         225 :         for(;;) {
     524             : 
     525             :           /* At this point, j is validated */
     526             : 
     527         225 :           TEST( pinfo[ j ].cycle_tag==2UL ); /* Make sure in partitioning and not visited yet this traversal */
     528         225 :           pinfo[ j ].cycle_tag = 3UL;        /* Mark as visited this traversal */
     529         225 :           visit_cnt++;
     530             : 
     531         225 :           ulong k = fd_wksp_private_pinfo_idx( pinfo[ j ].same_cidx );    /* Get the next same sized */
     532         225 :           if( fd_wksp_private_pinfo_idx_is_null( k ) ) break;             /* If no more, we are done with this node */
     533          81 :           TEST( k<part_max );                                             /* Make sure valid index */
     534          81 :           TEST( fd_wksp_private_pinfo_sz( pinfo + k )==sz );              /* Make sure same size */
     535          81 :           TEST( pinfo[ k ].in_same );                                     /* Make sure marked as in same */
     536          81 :           TEST( fd_wksp_private_pinfo_idx_is_null( fd_wksp_private_pinfo_idx( pinfo[ k ].left_cidx  ) ) );
     537          81 :           TEST( fd_wksp_private_pinfo_idx_is_null( fd_wksp_private_pinfo_idx( pinfo[ k ].right_cidx ) ) );
     538          81 :           TEST( fd_wksp_private_pinfo_idx( pinfo[ k ].parent_cidx )==j ); /* Make sure correct parent */
     539          81 :           j = k;
     540          81 :         }
     541             : 
     542             :         /* Recurse on the right subtree */
     543             : 
     544         144 :         p = i;
     545         144 :         i = fd_wksp_private_pinfo_idx( pinfo[ i ].right_cidx );
     546         144 :         if( !fd_wksp_private_pinfo_idx_is_null( i ) ) {
     547          60 :           TEST( i<part_max );                                             /* Validate i */
     548          60 :           TEST( fd_wksp_private_pinfo_idx( pinfo[ i ].parent_cidx )==p ); /* Validate parent */
     549          60 :         }
     550             : 
     551         144 :       } else {
     552             : 
     553         144 :         TEST( i<part_max ); /* Validate i */
     554             : 
     555             :         /* At this point i and everything on the stack is validated.
     556             :            Push i to the stack and recurse on the left subtree. */
     557             : 
     558         144 :         pinfo[ i ].stack_cidx = fd_wksp_private_pinfo_cidx( s );
     559         144 :         s = i;
     560         144 :         i = fd_wksp_private_pinfo_idx( pinfo[ i ].left_cidx );
     561         144 :         if( !fd_wksp_private_pinfo_idx_is_null( i ) ) {
     562          63 :           TEST( i<part_max );                                             /* Validate i */
     563          63 :           TEST( fd_wksp_private_pinfo_idx( pinfo[ i ].parent_cidx )==s ); /* Validate parent */
     564          63 :         }
     565         144 :       }
     566         309 :     }
     567             : 
     568          21 :     TEST( visit_cnt==free_cnt ); /* Make sure all free partitions in free treap */
     569             : 
     570          21 :   } while(0);
     571             : 
     572          21 : # undef TEST
     573             : 
     574          21 :   return FD_WKSP_SUCCESS;
     575          21 : }
     576             : 
     577             : int
     578             : fd_wksp_rebuild( fd_wksp_t * wksp,
     579         564 :                  uint        seed ) {
     580             : 
     581             :   /* Load the wksp metadata, don't rebuild if any of it looks even
     582             :      slightly off. */
     583             : 
     584         564 :   if( FD_UNLIKELY( !wksp ) ) {
     585           0 :     FD_LOG_WARNING(( "NULL wksp" ));
     586           0 :     return FD_WKSP_ERR_CORRUPT;
     587           0 :   }
     588             : 
     589         564 :   ulong magic     = wksp->magic;
     590         564 :   ulong part_max  = wksp->part_max;
     591         564 :   ulong data_max  = wksp->data_max;
     592         564 :   ulong gaddr_lo  = wksp->gaddr_lo;
     593         564 :   ulong gaddr_hi  = wksp->gaddr_hi;
     594         564 :   ulong cycle_tag = wksp->cycle_tag;
     595             : 
     596             :   /* TODO: consider verifying owner */
     597             : 
     598         564 :   ulong footprint    = fd_wksp_footprint( part_max, data_max );
     599         564 :   ulong gaddr_lo_exp = fd_wksp_private_data_off( part_max );
     600         564 :   ulong gaddr_hi_exp = gaddr_lo_exp + data_max;
     601         564 :   if( FD_UNLIKELY( (magic!=FD_WKSP_MAGIC) | (!footprint) | (!fd_shmem_name_len( wksp->name )) |
     602         564 :                    (gaddr_lo!=gaddr_lo_exp) | (gaddr_hi!=gaddr_hi_exp) | (cycle_tag<4UL) ) ) {
     603           0 :     FD_LOG_WARNING(( "bad metadata\n\t"
     604           0 :                      "magic     %016lx (exp %016lx)\n\t"
     605           0 :                      "part_max  %lu data_max %lu (footprint %lu)\n\t"
     606           0 :                      "gaddr_lo  %lu (exp %lu)\n\t"
     607           0 :                      "gaddr_hi  %lu (exp %lu)\n\t"
     608           0 :                      "cycle_tag %lu (exp>=4)",
     609           0 :                      magic, FD_WKSP_MAGIC, part_max, data_max, footprint,
     610           0 :                      gaddr_lo, gaddr_lo_exp, gaddr_hi, gaddr_hi_exp, cycle_tag ));
     611           0 :     return FD_WKSP_ERR_CORRUPT;
     612           0 :   }
     613             : 
     614             :   /* Scan the wksp pinfo and insert any used partitions into the used
     615             :      treap and put the rest on the idle stack.  If there is any sign of
     616             :      corruption (empty, bad range or overlap between used partitions),
     617             :      we abort the rebuild (this is almost certainly data corruption of
     618             :      some form and we don't have enough info to resolve a conflict
     619             :      without potentially making the situation worse).  We do the scan in
     620             :      reverse order to rebuild the idle stack in forward order.
     621             : 
     622             :      Note that we don't ever change the gaddr_lo,gaddr_hi of any tagged
     623             :      partitions such that operation is guaranteed to never change the
     624             :      single source of truth.  As such, this operation can be interrupted
     625             :      and restarted arbitrarily safely.*/
     626             : 
     627         564 :   fd_wksp_private_pinfo_t * pinfo = fd_wksp_private_pinfo( wksp );
     628             : 
     629         564 :   do {
     630         564 :     wksp->seed           = seed;
     631         564 :     wksp->idle_top_cidx  = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL ); /* Flush idle stack */
     632         564 :     wksp->part_used_cidx = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL ); /* Flush used treap */
     633         564 :     wksp->part_free_cidx = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL ); /* Flush free treap */
     634             : 
     635         564 :     ulong i = part_max;
     636    15099015 :     while( i ) {
     637    15098451 :       i--;
     638             : 
     639             :       /* Ideally, heap priorities should just be a shuffling of the
     640             :          integers [0,part_max).  fd_uint_hash will generate such a
     641             :          shuffling for part_max = 2^32.  Using the lower 30 bits
     642             :          (reserving bit 31 for bulk operations) will yield something
     643             :          very close.  We use seed to mix it up some more. */
     644             : 
     645    15098451 :       pinfo[ i ].in_same    = 0U;
     646    15098451 :       pinfo[ i ].heap_prio  = fd_uint_hash( seed ^ (uint)i ) & ((1U<<30)-1U);
     647    15098451 :       pinfo[ i ].stack_cidx = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
     648    15098451 :       pinfo[ i ].cycle_tag  = 0U;
     649             : 
     650    15098451 :       ulong tag = pinfo[ i ].tag;
     651    15098451 :       if( !tag ) { /* Not used ... make it available for reuse below */
     652    15096261 :         fd_wksp_private_idle_stack_push( i, wksp, pinfo );
     653    15096261 :         continue;
     654    15096261 :       }
     655             : 
     656        2190 :       pinfo[ i ].prev_cidx = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
     657        2190 :       pinfo[ i ].next_cidx = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
     658             : 
     659        2190 :       if( FD_UNLIKELY( fd_wksp_private_used_treap_insert( i, wksp, pinfo ) ) ) return FD_WKSP_ERR_CORRUPT; /* Logs details */
     660        2190 :     }
     661         564 :   } while(0);
     662             : 
     663             :   /* At this point, a partition is either in the idle stack or used
     664             :      treap.  Further, we have:
     665             : 
     666             :                  | used                       | idle
     667             :        ----------+----------------------------+--------
     668             :        gaddr_*   | non-empty range            | 0
     669             :                  | no overlap with other used | 0
     670             :        tag       | non-zero                   | 0
     671             :        in_same   | 0                          | 0
     672             :        heap_prio | randomized                 | randomized
     673             :        prev      | NULL                       | NULL
     674             :        next      | NULL                       | NULL
     675             :        left      | used treap managed         | NULL
     676             :        right     | used treap managed         | NULL
     677             :        same      | used treap managed (NULL)  | NULL
     678             :        parent    | used treap managed         | idle stack managed
     679             :        stack     | wksp managed               | wksp managed
     680             :        cycle_tag | wksp managed               | wksp managed
     681             : 
     682             :      In-order traverse the used treap to rebuild the partitioning and
     683             :      the free treap. */
     684             : 
     685         564 :   do {
     686         564 :     uint * j_next_cidx_ptr = &wksp->part_head_cidx;  /* Location of most recently added partition next link */
     687             : 
     688         564 :     ulong  j  = FD_WKSP_PRIVATE_PINFO_IDX_NULL; /* Most recently added partition */
     689         564 :     ulong  g0 = gaddr_lo;                       /* Most recently added partition end */
     690             : 
     691         564 :     ulong i = fd_wksp_private_pinfo_idx( wksp->part_used_cidx );
     692         564 :     ulong s = FD_WKSP_PRIVATE_PINFO_IDX_NULL;
     693        4944 :     for(;;) {
     694        4944 :       if( fd_wksp_private_pinfo_idx_is_null( i ) ) {
     695        2754 :         if( fd_wksp_private_pinfo_idx_is_null( s ) ) break; /* Done */
     696             : 
     697             :         /* Pop traversal stack */
     698             : 
     699        2190 :         i = s;
     700        2190 :         s = fd_wksp_private_pinfo_idx( pinfo[ i ].stack_cidx );
     701             : 
     702             :         /* Visit i */
     703             : 
     704        2190 :         ulong g1 = pinfo[ i ].gaddr_lo;
     705        2190 :         if( g1 > g0 ) { /* There's a gap between i and the most recently added partition */
     706             : 
     707             :           /* Acquire an idle partition to hold the gap */
     708             : 
     709        1503 :           if( FD_UNLIKELY( fd_wksp_private_idle_stack_is_empty( wksp ) ) ) {
     710           0 :             FD_LOG_WARNING(( "part_max (%lu) too small to fill gap before partition %lu (tag %lu gaddr_lo %lu gaddr_hi %lu)",
     711           0 :                              part_max, i, pinfo[i].tag, pinfo[i].gaddr_lo, pinfo[i].gaddr_hi ));
     712           0 :             return FD_WKSP_ERR_CORRUPT;
     713           0 :           }
     714        1503 :           ulong k = fd_wksp_private_idle_stack_pop( wksp, pinfo );
     715             : 
     716             :           /* Populate the acquired partition with the gap details,
     717             :              append it to the wksp partitioning and insert it into the
     718             :              free treap.  Note that stack_push/pop reset gaddr_lo,
     719             :              gaddr_hi, tag, in_same, {prev, next, left, right, same,
     720             :              parent}_cidx.  It preserved heap_prio from its original
     721             :              assignment and didn't touch stack_cidx or cycle_tag. */
     722             : 
     723        1503 :           pinfo[ k ].gaddr_lo  = g0;
     724        1503 :           pinfo[ k ].gaddr_hi  = g1;
     725        1503 :           pinfo[ k ].prev_cidx = fd_wksp_private_pinfo_cidx( j );
     726        1503 :           *j_next_cidx_ptr = fd_wksp_private_pinfo_cidx( k );
     727        1503 :           j_next_cidx_ptr  = &pinfo[ k ].next_cidx;
     728        1503 :           j  = k;
     729        1503 :           g0 = g1;
     730             : 
     731        1503 :           fd_wksp_private_free_treap_insert( j, wksp, pinfo );
     732        1503 :         }
     733             : 
     734             :         /* Add i to the partitioning. */
     735             : 
     736        2190 :         pinfo[ i ].prev_cidx = fd_wksp_private_pinfo_cidx( j );
     737        2190 :         *j_next_cidx_ptr = fd_wksp_private_pinfo_cidx( i );
     738        2190 :         j_next_cidx_ptr  = &pinfo[ i ].next_cidx;
     739        2190 :         j  = i;
     740        2190 :         g0 = pinfo[ i ].gaddr_hi;
     741             : 
     742             :         /* Traverse the right subtree */
     743             : 
     744        2190 :         i = fd_wksp_private_pinfo_idx( pinfo[ i ].right_cidx );
     745             : 
     746        2190 :       } else {
     747             : 
     748             :         /* Push i to the stack and recurse on the left subtree. */
     749             : 
     750        2190 :         pinfo[ i ].stack_cidx = fd_wksp_private_pinfo_cidx( s );
     751        2190 :         s = i;
     752        2190 :         i = fd_wksp_private_pinfo_idx( pinfo[ i ].left_cidx );
     753             : 
     754        2190 :       }
     755        4944 :     }
     756             : 
     757         564 :     if( g0 < gaddr_hi ) { /* Have final gap to fill */
     758             : 
     759             :       /* This works the same as the above */
     760             : 
     761         564 :       if( FD_UNLIKELY( fd_wksp_private_idle_stack_is_empty( wksp ) ) ) {
     762           0 :         FD_LOG_WARNING(( "part_max (%lu) too small to complete partitioning", part_max ));
     763           0 :         return FD_WKSP_ERR_CORRUPT;
     764           0 :       }
     765         564 :       ulong k = fd_wksp_private_idle_stack_pop( wksp, pinfo );
     766             : 
     767         564 :       pinfo[ k ].gaddr_lo  = g0;
     768         564 :       pinfo[ k ].gaddr_hi  = gaddr_hi;
     769         564 :       pinfo[ k ].prev_cidx = fd_wksp_private_pinfo_cidx( j );
     770         564 :       *j_next_cidx_ptr = fd_wksp_private_pinfo_cidx( k );
     771         564 :       j_next_cidx_ptr  = &pinfo[ k ].next_cidx;
     772         564 :       j  = k;
     773             :     //g0 = gaddr_hi;
     774             : 
     775         564 :       fd_wksp_private_free_treap_insert( j, wksp, pinfo );
     776         564 :     }
     777             : 
     778         564 :     wksp->part_tail_cidx = fd_wksp_private_pinfo_cidx( j );
     779             : 
     780         564 :   } while(0);
     781             : 
     782         564 :   return FD_WKSP_SUCCESS;
     783         564 : }

Generated by: LCOV version 1.14