LCOV - code coverage report
Current view: top level - util/tile - fd_tile_threads.inc (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 390 506 77.1 %
Date: 2026-03-31 06:22:16 Functions: 27 28 96.4 %

          Line data    Source code
       1             : #ifndef _GNU_SOURCE /* GCC seems to do this this is on the command line somehow when using g++ */
       2             : #define _GNU_SOURCE
       3             : #endif
       4             : 
       5             : #include <ctype.h>
       6             : #include <errno.h>
       7             : #include <pthread.h>
       8             : #include <unistd.h>
       9             : #include <sched.h>
      10             : #include <syscall.h>
      11             : #include <sys/resource.h>
      12             : #include <sys/mman.h>
      13             : #include <sys/prctl.h>
      14             : 
      15             : #include "../sanitize/fd_sanitize.h"
      16             : #include "fd_tile_private.h"
      17             : 
      18             : /* Operating system shims ********************************************/
      19             : 
      20             : struct fd_tile_private_cpu_config {
      21             :   int prio;
      22             : };
      23             : 
      24             : typedef struct fd_tile_private_cpu_config fd_tile_private_cpu_config_t;
      25             : 
      26             : /* Configure the CPU optimally */
      27             : 
      28             : static inline void
      29             : fd_tile_private_cpu_config( fd_tile_private_cpu_config_t * save,
      30        2591 :                             ulong                          cpu_idx ) {
      31             : 
      32             :   /* If a floating tile, leave scheduler priority unchanged from however
      33             :      the thread group launcher configured it. */
      34             : 
      35        2591 :   if( cpu_idx==65535UL ) {
      36        2444 :     save->prio = INT_MIN;
      37        2444 :     return;
      38        2444 :   }
      39             : 
      40             :   /* Otherwise, configure high scheduler priority */
      41             : 
      42        2591 :   errno = 0;
      43         147 :   int prio = getpriority( PRIO_PROCESS, (id_t)0 );
      44         147 :   if( prio==-1 && errno ) {
      45           0 :     FD_LOG_WARNING(( "fd_tile: getpriority failed (%i-%s)\n\t"
      46           0 :                      "Unable to determine initial tile priority so not configuring the tile\n\t"
      47           0 :                      "for high scheduler priority.  Attempting to continue but this thread\n\t"
      48           0 :                      "group's performance and stability might be compromised.  Probably should\n\t"
      49           0 :                      "configure 'ulimit -e 39' (or 40 and this might require adjusting\n\t"
      50           0 :                      "/etc/security/limits.conf as superuser to nice -19 or -20 for this user)\n\t"
      51           0 :                      "to eliminate this warning.  Also consider starting this thread group\n\t"
      52           0 :                      "with 'nice --19'.",
      53           0 :                      errno, fd_io_strerror( errno ) ));
      54           0 :     save->prio = INT_MIN;
      55           0 :   }
      56             : 
      57         147 :   if( FD_UNLIKELY( prio!=-19 ) && FD_UNLIKELY( setpriority( PRIO_PROCESS, (id_t)0, -19 ) ) ) {
      58           0 :     FD_LOG_WARNING(( "fd_tile: setpriority failed (%i-%s)\n\t"
      59           0 :                      "Unable to configure this tile for high scheduler priority.  Attempting\n\t"
      60           0 :                      "to continue but this thread group's performance and stability might be\n\t"
      61           0 :                      "compromised.  Probably should configure 'ulimit -e 39' (or 40 and this\n\t"
      62           0 :                      "might require adjusting /etc/security/limits.conf to nice -19 or -20\n\t"
      63           0 :                      "for this user) to eliminate this warning.  Also consider starting this\n\t"
      64           0 :                      "thread group with 'nice --19'.",
      65           0 :                      errno, fd_io_strerror( errno ) ));
      66           0 :     save->prio = INT_MIN;
      67           0 :     return;
      68           0 :   }
      69             : 
      70         147 :   save->prio = prio;
      71         147 : }
      72             : 
      73             : /* Restore the CPU to the given state */
      74             : 
      75             : static inline void
      76        1412 : fd_tile_private_cpu_restore( fd_tile_private_cpu_config_t * save ) {
      77        1412 :   int prio = save->prio;
      78        1412 :   if( FD_LIKELY( prio!=INT_MIN ) && FD_UNLIKELY( prio!=-19 ) && FD_UNLIKELY( setpriority( PRIO_PROCESS, (id_t)0, prio ) ) )
      79           0 :     FD_LOG_WARNING(( "fd_tile: setpriority failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
      80        1412 : }
      81             : 
      82             : void *
      83             : fd_tile_private_stack_new( int   optimize,
      84          36 :                            ulong cpu_idx ) { /* Ignored if optimize is not requested */
      85             : 
      86          36 :   uchar * stack = NULL;
      87             : 
      88          36 :   if( optimize ) { /* Create a NUMA and TLB optimized stack for a tile running on cpu cpu_idx */
      89             : 
      90          36 :     stack = (uchar *)
      91          36 :       fd_shmem_acquire( FD_SHMEM_HUGE_PAGE_SZ, (FD_TILE_PRIVATE_STACK_SZ/FD_SHMEM_HUGE_PAGE_SZ)+2UL, cpu_idx ); /* logs details */
      92             : 
      93          36 :     if( FD_LIKELY( stack ) ) { /* Make space for guard lo and guard hi */
      94             : 
      95          36 :       fd_shmem_release( stack, FD_SHMEM_HUGE_PAGE_SZ, 1UL );
      96             : 
      97          36 :       stack += FD_SHMEM_HUGE_PAGE_SZ;
      98             : 
      99          36 :       fd_shmem_release( stack + FD_TILE_PRIVATE_STACK_SZ, FD_SHMEM_HUGE_PAGE_SZ, 1UL );
     100             : 
     101          36 :     } else {
     102             : 
     103           0 :       ulong numa_idx = fd_shmem_numa_idx( cpu_idx );
     104           0 :       static ulong warn = 0UL;
     105           0 :       if( FD_LIKELY( !(warn & (1UL<<numa_idx) ) ) ) {
     106           0 :         FD_LOG_WARNING(( "fd_tile: fd_shmem_acquire failed\n\t"
     107           0 :                          "There are probably not enough huge pages allocated by the OS on numa\n\t"
     108           0 :                          "node %lu.  Falling back on normal page backed stack for tile on cpu %lu\n\t"
     109           0 :                          "and attempting to continue.  Run:\n\t"
     110           0 :                          "\techo [CNT] > /sys/devices/system/node/node%lu/hugepages/hugepages-2048kB/nr_hugepages\n\t"
     111           0 :                          "(probably as superuser) or equivalent where [CNT] is a sufficient number\n\t"
     112           0 :                          "huge pages to reserve on this numa node system wide and/or adjust\n\t"
     113           0 :                          "/etc/security/limits.conf to permit this user to lock a sufficient\n\t"
     114           0 :                          "amount of memory to eliminate this warning.",
     115           0 :                          numa_idx, cpu_idx, numa_idx ));
     116           0 :         warn |= 1UL<<numa_idx;
     117           0 :       }
     118             : 
     119           0 :     }
     120             : 
     121          36 :   }
     122             : 
     123          36 :   if( !stack ) { /* Request for a non-optimized stack (or optimized stack creation failed above and we are falling back) */
     124             : 
     125           0 :     ulong mmap_sz = FD_TILE_PRIVATE_STACK_SZ + 2UL*FD_SHMEM_NORMAL_PAGE_SZ;
     126           0 :     stack = (uchar *)mmap( NULL, mmap_sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, (off_t)0 );
     127             : 
     128           0 :     if( FD_LIKELY( ((void *)stack)!=MAP_FAILED ) ) { /* Make space for guard lo and guard hi */
     129             : 
     130           0 :       if( FD_UNLIKELY( munmap( stack, FD_SHMEM_NORMAL_PAGE_SZ ) ) )
     131           0 :         FD_LOG_WARNING(( "fd_tile: munmap failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
     132             : 
     133           0 :       stack += FD_SHMEM_NORMAL_PAGE_SZ;
     134             : 
     135           0 :       if( FD_UNLIKELY( munmap( stack + FD_TILE_PRIVATE_STACK_SZ, FD_SHMEM_NORMAL_PAGE_SZ ) ) )
     136           0 :         FD_LOG_WARNING(( "fd_tile: munmap failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
     137             : 
     138           0 :     } else {
     139             : 
     140           0 :       FD_LOG_WARNING(( "fd_tile: mmap(NULL,%lu KiB,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) failed (%i-%s)\n\t"
     141           0 :                        "Falling back on pthreads created stack and attempting to continue.",
     142           0 :                        mmap_sz >> 10, errno, fd_io_strerror( errno ) ));
     143           0 :       return NULL;
     144             : 
     145           0 :     }
     146             : 
     147           0 :   }
     148             : 
     149             :   /* Create the guard regions in the extra space */
     150             : 
     151          36 :   void * guard_lo = (void *)(stack - FD_SHMEM_NORMAL_PAGE_SZ );
     152          36 :   if( FD_UNLIKELY( mmap( guard_lo, FD_SHMEM_NORMAL_PAGE_SZ, PROT_NONE,
     153          36 :                          MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, (off_t)0 )!=guard_lo ) )
     154           0 :     FD_LOG_WARNING(( "fd_tile: mmap failed (%i-%s)\n\tAttempting to continue without stack guard lo.",
     155          36 :                      errno, fd_io_strerror( errno ) ));
     156             : 
     157          36 :   void * guard_hi = (void *)(stack + FD_TILE_PRIVATE_STACK_SZ);
     158          36 :   if( FD_UNLIKELY( mmap( guard_hi, FD_SHMEM_NORMAL_PAGE_SZ, PROT_NONE,
     159          36 :                          MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, (off_t)0 )!=guard_hi ) )
     160           0 :     FD_LOG_WARNING(( "fd_tile: mmap failed (%i-%s)\n\tAttempting to continue without stack guard hi.",
     161          36 :                      errno, fd_io_strerror( errno ) ));
     162             : 
     163          36 :   return stack;
     164          36 : }
     165             : 
     166             : static void
     167          36 : fd_tile_private_stack_delete( void * _stack ) {
     168          36 :   if( FD_UNLIKELY( !_stack ) ) return;
     169             : 
     170          36 :   uchar * stack    = (uchar *)_stack;
     171          36 :   uchar * guard_lo = stack - FD_SHMEM_NORMAL_PAGE_SZ;
     172          36 :   uchar * guard_hi = stack + FD_TILE_PRIVATE_STACK_SZ;
     173             : 
     174          36 :   if( FD_UNLIKELY( munmap( guard_hi, FD_SHMEM_NORMAL_PAGE_SZ ) ) )
     175           0 :     FD_LOG_WARNING(( "fd_tile: munmap failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
     176             : 
     177          36 :   if( FD_UNLIKELY( munmap( guard_lo, FD_SHMEM_NORMAL_PAGE_SZ ) ) )
     178           0 :     FD_LOG_WARNING(( "fd_tile: munmap failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
     179             : 
     180             :   /* Note that fd_shmem_release is just a wrapper around munmap such
     181             :      that this covers both the optimized and non-optimized cases */
     182             : 
     183          36 :   if( FD_UNLIKELY( munmap( stack, FD_TILE_PRIVATE_STACK_SZ ) ) )
     184           0 :     FD_LOG_WARNING(( "fd_tile: munmap failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
     185          36 : }
     186             : 
     187             : /* Tile side APIs ****************************************************/
     188             : 
     189             : static ulong fd_tile_private_id0; /* Zeroed at app start, initialized by the boot manager */
     190             : static ulong fd_tile_private_id1; /* " */
     191             : static ulong fd_tile_private_cnt; /* " */
     192             : 
     193          78 : ulong fd_tile_id0( void ) { return fd_tile_private_id0; }
     194          36 : ulong fd_tile_id1( void ) { return fd_tile_private_id1; }
     195         234 : ulong fd_tile_cnt( void ) { return fd_tile_private_cnt; }
     196             : 
     197             : static FD_TL ulong fd_tile_private_id;     /* Zeroed at app/thread start, initialized by the boot / tile manager */
     198             : static FD_TL ulong fd_tile_private_idx;    /* " */
     199             : /**/   FD_TL ulong fd_tile_private_stack0; /* " */
     200             : /**/   FD_TL ulong fd_tile_private_stack1; /* " */
     201             : 
     202          54 : ulong fd_tile_id ( void ) { return fd_tile_private_id;  }
     203         993 : ulong fd_tile_idx( void ) { return fd_tile_private_idx; }
     204             : 
     205             : static ushort fd_tile_private_cpu_id[ FD_TILE_MAX ]; /* Zeroed at app start, initialized by boot */
     206             : 
     207             : ulong
     208          87 : fd_tile_cpu_id( ulong tile_idx ) {
     209          87 :   if( FD_UNLIKELY( tile_idx>=fd_tile_private_cnt ) ) return ULONG_MAX;
     210          87 :   ulong cpu_idx = (ulong)fd_tile_private_cpu_id[ tile_idx ];
     211          87 :   return fd_ulong_if( cpu_idx<65535UL, cpu_idx, ULONG_MAX-1UL );
     212          87 : }
     213             : 
     214             : /* This is used for the OS services to communicate information with the
     215             :    tile managers */
     216             : 
     217    13919116 : #define FD_TILE_PRIVATE_STATE_BOOT (0) /* Tile is booting */
     218         189 : #define FD_TILE_PRIVATE_STATE_IDLE (1) /* Tile is idle */
     219         105 : #define FD_TILE_PRIVATE_STATE_EXEC (2) /* Tile is executing a task */
     220          36 : #define FD_TILE_PRIVATE_STATE_HALT (3) /* Tile is halting */
     221             : 
     222             : struct __attribute__((aligned(128))) fd_tile_private { /* Double cache line aligned to avoid aclpf false sharing */
     223             :   ulong          id;
     224             :   ulong          idx;
     225             :   int            state;  /* FD_TILE_PRIVATE_STATE_* */
     226             :   int            argc;
     227             :   char **        argv;
     228             :   fd_tile_task_t task;
     229             :   char const *   fail;
     230             :   int            ret;
     231             : };
     232             : 
     233             : typedef struct fd_tile_private fd_tile_private_t;
     234             : 
     235             : struct fd_tile_private_manager_args {
     236             :   ulong               id;
     237             :   ulong               idx;
     238             :   ulong               cpu_idx;
     239             :   void *              stack;    /* NULL if pthread created, non-NULL if user created */
     240             :   ulong               stack_sz;
     241             :   fd_tile_private_t * tile;
     242             : };
     243             : 
     244             : typedef struct fd_tile_private_manager_args fd_tile_private_manager_args_t;
     245             : 
     246             : static void *
     247          36 : fd_tile_private_manager( void * _args ) {
     248          36 :   fd_tile_private_manager_args_t * args = (fd_tile_private_manager_args_t *)_args;
     249             : 
     250             : # if !__GLIBC__
     251             :   if( args->cpu_idx<65535UL ) {
     252             :     FD_CPUSET_DECL( cpu_set );
     253             :     fd_cpuset_insert( cpu_set, args->cpu_idx );
     254             :     int err = fd_cpuset_setaffinity( (pid_t)0, cpu_set );
     255             :     if( FD_UNLIKELY( err ) )
     256             :       FD_LOG_WARNING(( "fd_tile: fd_cpuset_setaffinity_failed (%i-%s)\n\t"
     257             :                        "Unable to set the thread affinity for tile %lu to cpu %lu.  Attempting to\n\t"
     258             :                        "continue without explicitly specifying this tile's cpu affinity but it\n\t"
     259             :                        "is likely this thread group's performance and stability are compromised\n\t"
     260             :                        "(possibly catastrophically so).  Update --tile-cpus to specify a set of\n\t"
     261             :                        "allowed cpus that have been reserved for this thread group on this host\n\t"
     262             :                        "to eliminate this warning.", err, fd_io_strerror( err ), args->idx, args->cpu_idx ));
     263             :   }
     264             : # endif /* !__GLIBC__ */
     265             : 
     266          36 :   ulong  id       = args->id;
     267          36 :   ulong  idx      = args->idx;
     268          36 :   void * stack    = args->stack;
     269          36 :   ulong  stack_sz = args->stack_sz;
     270             : 
     271          36 :   char thread_name[ 20 ];
     272          36 :   FD_TEST( fd_cstr_printf_check( thread_name, sizeof( thread_name ), NULL, "tile:%lu", idx ) );
     273          36 :   if( FD_UNLIKELY( prctl( PR_SET_NAME, thread_name, 0, 0, 0 ) ) ) FD_LOG_ERR(( "prctl(PR_SET_NAME) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     274             : 
     275          36 :   if( FD_UNLIKELY( !( (id ==fd_log_thread_id()                                       ) &
     276          36 :                       (idx==(id-fd_tile_private_id0)                                 ) &
     277          36 :                       ((fd_tile_private_id0<id) & (id<fd_tile_private_id1)           ) &
     278          36 :                       (fd_tile_private_cnt==(fd_tile_private_id1-fd_tile_private_id0)) ) ) )
     279           0 :     FD_LOG_ERR(( "fd_tile: internal error (unexpected thread identifiers)" ));
     280             : 
     281          36 :   fd_tile_private_t tile[1];
     282          36 :   FD_VOLATILE( tile->id    ) = id;
     283          36 :   FD_VOLATILE( tile->idx   ) = idx;
     284          36 :   FD_VOLATILE( tile->state ) = FD_TILE_PRIVATE_STATE_BOOT;
     285          36 :   FD_VOLATILE( tile->argc  ) = 0;
     286          36 :   FD_VOLATILE( tile->argv  ) = NULL;
     287          36 :   FD_VOLATILE( tile->task  ) = NULL;
     288          36 :   FD_VOLATILE( tile->fail  ) = NULL;
     289          36 :   FD_VOLATILE( tile->ret   ) = 0;
     290             : 
     291             :   /* state is BOOT ... configure the tile, transition to IDLE and then
     292             :      start polling for tasks */
     293             : 
     294          36 :   fd_tile_private_id  = id;
     295          36 :   fd_tile_private_idx = idx;
     296             : 
     297          36 :   if( FD_LIKELY( stack ) ) { /* User provided stack */
     298          36 :     fd_tile_private_stack0 = (ulong)stack;
     299          36 :     fd_tile_private_stack1 = (ulong)stack + stack_sz;
     300             : 
     301             :     /* Prevent another fork() from smashing the stack */
     302          36 :     if( FD_UNLIKELY( madvise( stack, FD_TILE_PRIVATE_STACK_SZ, MADV_DONTFORK ) ) ) {
     303           0 :       FD_LOG_ERR(( "madvise(stack,MADV_DONTFORK) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     304           0 :     }
     305             : 
     306          36 :   } else { /* Pthread provided stack */
     307           0 :     fd_log_private_stack_discover( stack_sz, &fd_tile_private_stack0, &fd_tile_private_stack1 ); /* logs details */
     308           0 :     if( FD_UNLIKELY( !fd_tile_private_stack0 ) )
     309           0 :       FD_LOG_WARNING(( "stack diagnostics not available on this tile; attempting to continue" ));
     310           0 :   }
     311             : 
     312          36 :   fd_tile_private_cpu_config_t dummy[1];
     313          36 :   fd_tile_private_cpu_config( dummy, args->cpu_idx );
     314             : 
     315          36 :   ulong app_id = fd_log_app_id();
     316          36 :   FD_LOG_INFO(( "fd_tile: boot tile %lu success (thread %lu:%lu in thread group %lu:%lu/%lu)",
     317          36 :                 idx, app_id, id, app_id, fd_tile_private_id0, fd_tile_private_cnt ));
     318             : 
     319          36 :   FD_COMPILER_MFENCE();
     320          36 :   FD_VOLATILE( tile->state ) = FD_TILE_PRIVATE_STATE_IDLE;
     321          36 :   FD_VOLATILE( args->tile  ) = tile;
     322             : 
     323   130712059 :   for(;;) {
     324             : 
     325             :     /* We are awake ... see what we should do next */
     326             : 
     327   130712059 :     int state = FD_VOLATILE_CONST( tile->state );
     328   144630998 :     if( FD_UNLIKELY( state!=FD_TILE_PRIVATE_STATE_EXEC ) ) {
     329   144630998 :       if( FD_UNLIKELY( state!=FD_TILE_PRIVATE_STATE_IDLE ) ) break;
     330             :       /* state is IDLE ... try again */
     331   144630969 :       FD_SPIN_PAUSE();
     332   144630969 :       continue;
     333   144630998 :     }
     334             : 
     335             :     /* state is EXEC ... the run assigned task and then
     336             :        transition to IDLE when done */
     337             :     /* FIXME: MORE SOPHISTICATED HANDLING OF EXCEPTIONS */
     338             : 
     339           5 :     int            argc = FD_VOLATILE_CONST( tile->argc );
     340           5 :     char **        argv = FD_VOLATILE_CONST( tile->argv );
     341           5 :     fd_tile_task_t task = FD_VOLATILE_CONST( tile->task );
     342           5 : #   ifdef __cplusplus
     343           5 :     try {
     344           5 : #   endif
     345           5 :       FD_VOLATILE( tile->ret  ) = task( argc, argv );
     346           5 :       FD_VOLATILE( tile->fail ) = NULL;
     347           5 : #   ifdef __cplusplus
     348           5 :     } catch( ... ) {
     349           0 :       FD_VOLATILE( tile->fail ) = "uncaught exception";
     350           0 :     }
     351           5 : #   endif
     352             : 
     353           5 :     FD_COMPILER_MFENCE();
     354         105 :     FD_VOLATILE( tile->state ) = FD_TILE_PRIVATE_STATE_IDLE;
     355         105 :   }
     356             : 
     357             :   /* state is HALT, clean up and then reset back to BOOT */
     358             : 
     359    13919080 :   FD_LOG_INFO(( "fd_tile: halting tile %lu", idx ));
     360             : 
     361    13919080 :   FD_COMPILER_MFENCE();
     362    13919080 :   FD_VOLATILE( tile->state ) = FD_TILE_PRIVATE_STATE_BOOT;
     363    13919080 :   return stack;
     364          36 : }
     365             : 
     366             : /* Dispatch side APIs ************************************************/
     367             : 
     368             : static struct __attribute__((aligned(128))) { /* Each on its own cache line pair to limit false sharing in parallel dispatch */
     369             :   fd_tile_private_t * lock; /* Non-NULL if tile idx is available for dispatch, ==tile otherwise */
     370             :   fd_tile_private_t * tile;
     371             :   pthread_t           pthread;
     372             : } fd_tile_private[ FD_TILE_MAX ];
     373             : 
     374             : /* FIXME: ATOMIC_XCHG BASED INSTEAD? */
     375             : static inline fd_tile_private_t *
     376         108 : fd_tile_private_trylock( ulong tile_idx ) {
     377         108 :   fd_tile_private_t * volatile * vtile = (fd_tile_private_t * volatile *)&fd_tile_private[ tile_idx ].lock;
     378         108 :   fd_tile_private_t * tile = *vtile;
     379         108 :   if( FD_LIKELY( tile ) && FD_LIKELY( FD_ATOMIC_CAS( vtile, tile, NULL )==tile ) ) return tile;
     380           3 :   return NULL;
     381         108 : }
     382             : 
     383             : static inline fd_tile_private_t *
     384          36 : fd_tile_private_lock( ulong tile_idx ) {
     385          36 :   fd_tile_private_t * volatile * vtile = (fd_tile_private_t * volatile *)&fd_tile_private[ tile_idx ].lock;
     386          36 :   fd_tile_private_t * tile;
     387          36 :   for(;;) {
     388          36 :     tile = *vtile;
     389          36 :     if( FD_LIKELY( tile ) && FD_LIKELY( FD_ATOMIC_CAS( vtile, tile, NULL )==tile ) ) break;
     390           0 :     FD_SPIN_PAUSE();
     391           0 :   }
     392          36 :   return tile;
     393          36 : }
     394             : 
     395             : static inline void
     396             : fd_tile_private_unlock( ulong               tile_idx,
     397         141 :                         fd_tile_private_t * tile ) {
     398         141 :   FD_VOLATILE( fd_tile_private[ tile_idx ].lock ) = tile;
     399         141 : }
     400             : 
     401             : fd_tile_exec_t *
     402             : fd_tile_exec_new( ulong          idx,
     403             :                   fd_tile_task_t task,
     404             :                   int            argc,
     405         141 :                   char **        argv ) {
     406         141 :   if( FD_UNLIKELY( (idx==fd_tile_private_idx) | (!idx) ) ) return NULL; /* Can't dispatch to self or to tile 0 */
     407             : 
     408         108 :   fd_tile_private_t * tile = fd_tile_private_trylock( idx );
     409         108 :   if( FD_UNLIKELY( !tile ) ) return NULL;
     410             : 
     411             :   /* Exec holds the lock and tile state is idle here */
     412         105 :   FD_VOLATILE( tile->argc ) = argc;
     413         105 :   FD_VOLATILE( tile->argv ) = argv;
     414         105 :   FD_VOLATILE( tile->task ) = task;
     415         105 :   FD_COMPILER_MFENCE();
     416         105 :   FD_VOLATILE( tile->state ) = FD_TILE_PRIVATE_STATE_EXEC;
     417         105 :   return (fd_tile_exec_t *)tile;
     418         108 : }
     419             : 
     420             : char const *
     421             : fd_tile_exec_delete( fd_tile_exec_t * exec,
     422         105 :                      int *            opt_ret ) {
     423         105 :   fd_tile_private_t * tile     = (fd_tile_private_t *)exec;
     424         105 :   ulong               tile_idx = tile->idx;
     425             : 
     426         105 :   int state;
     427     1729802 :   for(;;) {
     428     1729802 :     state = FD_VOLATILE_CONST( tile->state );
     429     1729802 :     if( FD_LIKELY( state==FD_TILE_PRIVATE_STATE_IDLE ) ) break;
     430     1729697 :     FD_SPIN_PAUSE();
     431     1729697 :   }
     432             :   /* state is IDLE at this point */
     433         105 :   char const * fail = FD_VOLATILE_CONST( tile->fail );
     434         105 :   if( FD_LIKELY( (!fail) & (!!opt_ret) ) ) *opt_ret = FD_VOLATILE_CONST( tile->ret );
     435         105 :   fd_tile_private_unlock( tile_idx, tile );
     436         105 :   return fail;
     437         105 : }
     438             : 
     439         108 : fd_tile_exec_t * fd_tile_exec( ulong tile_idx ) { return (fd_tile_exec_t *)fd_tile_private[ tile_idx ].tile; }
     440             : 
     441           0 : ulong          fd_tile_exec_id  ( fd_tile_exec_t const * exec ) { return ((fd_tile_private_t const *)exec)->id;   }
     442          12 : ulong          fd_tile_exec_idx ( fd_tile_exec_t const * exec ) { return ((fd_tile_private_t const *)exec)->idx;  }
     443          12 : fd_tile_task_t fd_tile_exec_task( fd_tile_exec_t const * exec ) { return ((fd_tile_private_t const *)exec)->task; }
     444          12 : int            fd_tile_exec_argc( fd_tile_exec_t const * exec ) { return ((fd_tile_private_t const *)exec)->argc; }
     445          12 : char **        fd_tile_exec_argv( fd_tile_exec_t const * exec ) { return ((fd_tile_private_t const *)exec)->argv; }
     446             : 
     447             : int
     448          12 : fd_tile_exec_done( fd_tile_exec_t const * exec ) {
     449          12 :   fd_tile_private_t const * tile = (fd_tile_private_t const *)exec;
     450          12 :   return FD_VOLATILE_CONST( tile->state )==FD_TILE_PRIVATE_STATE_IDLE;
     451          12 : }
     452             : 
     453             : /* Boot/halt APIs ****************************************************/
     454             : 
     455             : /* Parse a list of cpu tiles */
     456             : 
     457             : FD_STATIC_ASSERT( FD_TILE_MAX<65535, update_tile_to_cpu_type );
     458             : 
     459             : ulong
     460             : fd_tile_private_cpus_parse( char const * cstr,
     461        2555 :                             ushort *     tile_to_cpu ) {
     462        2555 :   if( !cstr ) return 0UL;
     463         111 :   ulong cnt = 0UL;
     464             : 
     465         111 :   FD_CPUSET_DECL( assigned_set );
     466             : 
     467         111 :   char const * p = cstr;
     468         225 :   for(;;) {
     469             : 
     470         225 :     while( fd_isspace( (int)p[0] ) ) p++; /* Munch whitespace */
     471             : 
     472         225 :     if( p[0]=='f' ) { /* These tiles have been requested to float on the original core set */
     473           0 :       p++;
     474             : 
     475           0 :       ulong float_cnt;
     476             : 
     477           0 :       while( fd_isspace( (int)p[0] ) ) p++; /* Munch whitespace */
     478           0 :       if     ( p[0]==','             ) float_cnt = 1UL, p++;
     479           0 :       else if( p[0]=='\0'            ) float_cnt = 1UL;
     480           0 :       else if( !fd_isdigit( (int)p[0] ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (malformed count)" ));
     481           0 :       else {
     482           0 :         float_cnt = fd_cstr_to_ulong( p );
     483           0 :         if( FD_UNLIKELY( !float_cnt ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (bad count)" ));
     484           0 :         p++; while( fd_isdigit( (int)p[0] ) ) p++; /* FIXME: USE STRTOUL ENDPTR FOR CORRECT HANDLING OF NON-BASE-10 */
     485           0 :         while( fd_isspace( (int)p[0] ) ) p++; /* Munch whitespace */
     486           0 :         if( FD_UNLIKELY( !( p[0]==',' || p[0]=='\0' ) ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (bad count delimiter)" ));
     487           0 :         if( p[0]==',' ) p++;
     488           0 :       }
     489             : 
     490             :       /* float_cnt is at least 1 at this point */
     491           0 :       do {
     492           0 :         if( FD_UNLIKELY( cnt>=FD_TILE_MAX ) ) FD_LOG_ERR(( "fd_tile: too many --tile-cpus" ));
     493           0 :         tile_to_cpu[ cnt++ ] = (ushort)65535;
     494           0 :       } while( --float_cnt );
     495             : 
     496           0 :       continue;
     497           0 :     }
     498             : 
     499         225 :     if( !fd_isdigit( (int)p[0] ) ) {
     500         111 :       if( FD_UNLIKELY( p[0]!='\0' ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (range lo not a cpu)" ));
     501         111 :       break;
     502         111 :     }
     503         114 :     ulong cpu0   = fd_cstr_to_ulong( p );
     504         114 :     ulong cpu1   = cpu0;
     505         114 :     ulong stride = 1UL;
     506         156 :     p++; while( fd_isdigit( (int)p[0] ) ) p++; /* FIXME: USE STRTOUL ENDPTR FOR CORRECT HANDLING OF NON-BASE-10 */
     507         114 :     while( fd_isspace( (int)p[0] ) ) p++;
     508         114 :     if( p[0]=='-' ) {
     509           6 :       p++;
     510           6 :       while( fd_isspace( (int)p[0] ) ) p++;
     511           6 :       if( FD_UNLIKELY( !fd_isdigit( (int)p[0] ) ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (range hi not a cpu)" ));
     512           6 :       cpu1 = fd_cstr_to_ulong( p );
     513           6 :       p++; while( fd_isdigit( (int)p[0] ) ) p++; /* FIXME: USE STRTOUL ENDPTR FOR CORRECT HANDLING OF NON-BASE-10 */
     514           6 :       while( fd_isspace( (int)p[0] ) ) p++;
     515           6 :       if( p[0]=='/' || p[0]==':' ) {
     516           3 :         p++;
     517           3 :         while( fd_isspace( (int)p[0] ) ) p++;
     518           3 :         if( FD_UNLIKELY( !fd_isdigit( (int)p[0] ) ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (stride not an int)" ));
     519           3 :         stride = fd_cstr_to_ulong( p );
     520           3 :         p++; while( fd_isdigit( (int)p[0] ) ) p++; /* FIXME: USE STRTOUL ENDPTR FOR CORRECT HANDLING OF NON-BASE-10 */
     521           3 :       }
     522           6 :     }
     523         108 :     else if( p[0]=='h' ) {
     524             :       /* `Nh` is a shorthand for "core N and its hyperthread sibling".
     525             :          If M is the sibling id and S = N-M, it translates as "N,M" or
     526             :          equivalently "N-M/S", i.e. a range from N to M with stride S.
     527             :          If core N doesn't have a sibling, the `h` suffix is ignored.
     528             :          This way an expression like "0h" also works on systems where
     529             :          hyperthread is not available. */
     530           0 :       p++;
     531           0 :       ulong sibling = fd_tile_private_sibling_idx( cpu0 );
     532           0 :       cpu1 =   fd_ulong_if( sibling==ULONG_MAX, cpu0, sibling );
     533           0 :       stride = fd_ulong_if( sibling==ULONG_MAX, 1,    sibling-cpu0 );
     534           0 :     }
     535         114 :     while( fd_isspace( (int)p[0] ) ) p++;
     536         114 :     if( FD_UNLIKELY( !( p[0]==',' || p[0]=='\0' ) ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (bad range delimiter)" ));
     537         114 :     if( p[0]==',' ) p++;
     538         114 :     cpu1++;
     539         114 :     if( FD_UNLIKELY( cpu1<=cpu0 ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (invalid range)"  ));
     540         114 :     if( FD_UNLIKELY( !stride    ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (invalid stride)" ));
     541             : 
     542         261 :     for( ulong cpu=cpu0; cpu<cpu1; cpu+=stride ) {
     543         147 :       if( FD_UNLIKELY( cnt>=FD_TILE_MAX                    ) ) FD_LOG_ERR(( "fd_tile: too many --tile-cpus" ));
     544         147 :       if( FD_UNLIKELY( fd_cpuset_test( assigned_set, cpu ) ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (repeated cpu)" ));
     545         147 :       tile_to_cpu[ cnt++ ] = (ushort)cpu;
     546         147 :       fd_cpuset_insert( assigned_set, cpu );
     547         147 :     }
     548         114 :   }
     549             : 
     550         111 :   return cnt;
     551         111 : }
     552             : 
     553             : static fd_tile_private_cpu_config_t fd_tile_private_cpu_config_save[1];
     554             : 
     555             : void
     556             : fd_tile_private_map_boot( ushort * tile_to_cpu,
     557        2555 :                           ulong    tile_cnt ) {
     558        2555 :   fd_tile_private_id0 = fd_log_thread_id();
     559        2555 :   fd_tile_private_id1 = fd_tile_private_id0 + tile_cnt;
     560        2555 :   fd_tile_private_cnt = tile_cnt;
     561             : 
     562        2555 :   ulong app_id  = fd_log_app_id();
     563        2555 :   ulong host_id = fd_log_host_id();
     564        2555 :   FD_LOG_INFO(( "fd_tile: booting thread group %lu:%lu/%lu", app_id, fd_tile_private_id0, fd_tile_private_cnt ));
     565             : 
     566             :   /* We create the tiles [1,tile_cnt) first so that any floating tiles
     567             :      in this inherit the appropriate scheduler priorities and affinities
     568             :      from the thread group launcher. */
     569             : 
     570        2591 :   for( ulong tile_idx=1UL; tile_idx<tile_cnt; tile_idx++ ) {
     571             : 
     572          36 :     ulong cpu_idx = (ulong)tile_to_cpu[ tile_idx ];
     573          36 :     int   fixed   = (cpu_idx<65535UL);
     574             : 
     575          36 :     if( fixed ) FD_LOG_INFO(( "fd tile: booting tile %lu on cpu %lu:%lu",   tile_idx, host_id, cpu_idx ));
     576           0 :     else        FD_LOG_INFO(( "fd tile: booting tile %lu on cpu %lu:float", tile_idx, host_id ));
     577             : 
     578          36 :     pthread_attr_t attr[1];
     579          36 :     int err = pthread_attr_init( attr );
     580          36 :     if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_tile: pthread_attr_init failed (%i-%s) for tile %lu.\n\t",
     581          36 :                                           err, fd_io_strerror( err ), tile_idx ));
     582             : 
     583             :     /* Set affinity ahead of time.  This is a GNU-specific extension
     584             :        that is not available on musl.  On musl, we just skip this
     585             :        step as we call sched_setaffinity(2) later on regardless. */
     586             : 
     587          36 : #   if __GLIBC__
     588          36 :     if( fixed ) {
     589          36 :       FD_CPUSET_DECL( cpu_set );
     590          36 :       fd_cpuset_insert( cpu_set, cpu_idx );
     591          36 :       err = pthread_attr_setaffinity_np( attr, fd_cpuset_footprint(), (cpu_set_t const *)fd_type_pun_const( cpu_set ) );
     592          36 :       if( FD_UNLIKELY( err ) ) FD_LOG_WARNING(( "fd_tile: pthread_attr_setaffinity_failed (%i-%s)\n\t"
     593          36 :                                                 "Unable to set the thread affinity for tile %lu on cpu %lu.  Attempting to\n\t"
     594          36 :                                                 "continue without explicitly specifying this cpu's thread affinity but it\n\t"
     595          36 :                                                 "is likely this thread group's performance and stability are compromised\n\t"
     596          36 :                                                 "(possibly catastrophically so).  Update --tile-cpus to specify a set of\n\t"
     597          36 :                                                 "allowed cpus that have been reserved for this thread group on this host\n\t"
     598          36 :                                                 "to eliminate this warning.",
     599          36 :                                                 err, fd_io_strerror( err ), tile_idx, cpu_idx ));
     600          36 :     }
     601          36 : #   endif /* __GLIBC__ */
     602             : 
     603             :     /* Create an optimized stack with guard regions if the build target
     604             :        is x86 (e.g. supports huge pages necessary to optimize TLB usage)
     605             :        and the tile is assigned to a particular CPU (e.g. bind the stack
     606             :        memory to the NUMA node closest to the cpu).
     607             : 
     608             :        Otherwise (or if an optimized stack could not be created), create
     609             :        vanilla pthread-style stack with guard regions.  We DIY here
     610             :        because pthreads seems to be missing an API to determine the
     611             :        extents of the stacks it creates and we need to know the stack
     612             :        extents for run-time stack diagnostics.  Though we can use
     613             :        fd_log_private_stack_discover to determine stack extents after
     614             :        the thread is started, it is faster, more flexible, more reliable
     615             :        and more portable to use a user specified stack when possible.
     616             : 
     617             :        If neither can be done, we will let pthreads create the tile's
     618             :        stack and try to discover the stack extents after the thread is
     619             :        started. */
     620             : 
     621          36 :     int optimize = FD_HAS_X86 & fixed;
     622             : 
     623          36 :     void * stack = fd_tile_private_stack_new( optimize, cpu_idx );
     624          36 :     if( FD_LIKELY( stack ) ) {
     625          36 :       err = pthread_attr_setstack( attr, stack, FD_TILE_PRIVATE_STACK_SZ );
     626          36 :       if( FD_UNLIKELY( err ) ) {
     627           0 :         FD_LOG_WARNING(( "fd_tile: pthread_attr_setstack failed (%i-%s)\n\t", err, fd_io_strerror( err ) ));
     628           0 :         fd_tile_private_stack_delete( stack );
     629           0 :         stack = NULL;
     630           0 :       }
     631          36 :     }
     632             : 
     633          36 :     if( FD_UNLIKELY( !stack ) ) FD_LOG_WARNING(( "fd_tile: Unable to create a stack for tile %lu.\n\t"
     634          36 :                                                  "Attempting to continue with the default stack but it is likely this\n\t"
     635          36 :                                                  "thread group's performance and stability is compromised (possibly\n\t"
     636          36 :                                                  "catastrophically so).",
     637          36 :                                                  tile_idx ));
     638             : 
     639          36 :     ulong stack_sz;
     640          36 :     err = pthread_attr_getstacksize( attr, &stack_sz );
     641          36 :     if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_tile: pthread_attr_getstacksize failed (%i-%s) for tile %lu.\n\t",
     642          36 :                                           err, fd_io_strerror( err ), tile_idx ));
     643             : 
     644          36 :     FD_VOLATILE( fd_tile_private[ tile_idx ].lock ) = NULL;
     645             : 
     646          36 :     fd_tile_private_manager_args_t args[1];
     647             : 
     648          36 :     FD_VOLATILE( args->id       ) = fd_tile_private_id0 + tile_idx;
     649          36 :     FD_VOLATILE( args->idx      ) = tile_idx;
     650          36 :     FD_VOLATILE( args->cpu_idx  ) = cpu_idx;
     651          36 :     FD_VOLATILE( args->stack    ) = stack;
     652          36 :     FD_VOLATILE( args->stack_sz ) = stack_sz;
     653          36 :     FD_VOLATILE( args->tile     ) = NULL;
     654             : 
     655          36 :     FD_COMPILER_MFENCE();
     656             : 
     657          36 :     err = pthread_create( &fd_tile_private[tile_idx].pthread, attr, fd_tile_private_manager, args );
     658          36 :     if( FD_UNLIKELY( err ) ) {
     659           0 :       if( fixed ) FD_LOG_ERR(( "fd_tile: pthread_create failed (%i-%s)\n\t"
     660           0 :                                "Unable to start up the tile %lu on cpu %lu.  Likely causes for this include\n\t"
     661           0 :                                "this cpu is restricted from the user or does not exist on this host.\n\t"
     662           0 :                                "Update --tile-cpus to specify a set of allowed cpus that have been reserved\n\t"
     663           0 :                                "for this thread group on this host.",
     664           0 :                                err, fd_io_strerror( err ), tile_idx, cpu_idx ));
     665           0 :       FD_LOG_ERR(( "fd_tile: pthread_create failed (%i-%s)\n\tUnable to start up the tile %lu (floating).",
     666           0 :                    err, fd_io_strerror( err ), tile_idx ));
     667           0 :     }
     668             : 
     669             :     /* Wait for the tile to be ready to exec */
     670             : 
     671          36 :     fd_tile_private_t * tile;
     672        7820 :     for(;;) {
     673        7820 :       tile = FD_VOLATILE_CONST( args->tile );
     674        7820 :       if( FD_LIKELY( tile ) ) break;
     675        7784 :       FD_YIELD();
     676        7784 :     }
     677          36 :     FD_VOLATILE( fd_tile_private[ tile_idx ].tile ) = tile;
     678          36 :     FD_VOLATILE( fd_tile_private[ tile_idx ].lock ) = tile;
     679             : 
     680             :     /* Tile is running, args is safe to reuse */
     681             : 
     682          36 :     err = pthread_attr_destroy( attr );
     683          36 :     if( FD_UNLIKELY( err ) )
     684           0 :       FD_LOG_WARNING(( "fd_tile: pthread_attr_destroy failed (%i-%s) for tile %lu; attempting to continue",
     685          36 :                        err, fd_io_strerror( err ), tile_idx ));
     686          36 :   }
     687             : 
     688             :   /* And now we "boot" tile 0 */
     689             : 
     690        2555 :   ulong cpu_idx = (ulong)tile_to_cpu[ 0UL ];
     691        2555 :   int   fixed   = (cpu_idx<65535UL);
     692        2555 :   if( fixed ) FD_LOG_INFO(( "fd tile: booting tile %lu on cpu %lu:%lu",   0UL, host_id, cpu_idx ));
     693        2444 :   else        FD_LOG_INFO(( "fd tile: booting tile %lu on cpu %lu:float", 0UL, host_id ));
     694             : 
     695        2555 :   if( fixed ) {
     696             : 
     697         111 :     int good_taskset;
     698         111 :     FD_CPUSET_DECL( cpu_set );
     699         111 :     if( FD_UNLIKELY( fd_cpuset_getaffinity( (pid_t)0, cpu_set ) ) ) {
     700           0 :       FD_LOG_WARNING(( "fd_tile: fd_cpuset_getaffinity failed (%i-%s) for tile 0 on cpu %lu",
     701           0 :                        errno, fd_io_strerror( errno ), cpu_idx ));
     702           0 :       good_taskset = 0;
     703         111 :     } else {
     704         111 :       ulong cnt = fd_cpuset_cnt( cpu_set );
     705         111 :       ulong idx = fd_cpuset_first( cpu_set );
     706         111 :       good_taskset = (cnt==1UL) & (idx==cpu_idx);
     707         111 :     }
     708             : 
     709         111 :     if( FD_UNLIKELY( !good_taskset ) ) {
     710           9 :       FD_LOG_WARNING(( "fd_tile: --tile-cpus for tile 0 may not match initial kernel affinity\n\t"
     711           9 :                        "Tile 0 might not be fully optimized because of kernel first touch.\n\t"
     712           9 :                        "Overriding fd_log_cpu_id(), fd_log_cpu(), fd_log_thread() on tile 0 to\n\t"
     713           9 :                        "match --tile-cpus and attempting to continue.  Launch this thread\n\t"
     714           9 :                        "group via 'taskset -c %lu' or equivalent to eliminate this warning.", cpu_idx ));
     715           9 :       fd_cpuset_null( cpu_set );
     716           9 :       fd_cpuset_insert( cpu_set, cpu_idx );
     717           9 :       if( FD_UNLIKELY( fd_cpuset_setaffinity( (pid_t)0, cpu_set ) ) )
     718           0 :         FD_LOG_WARNING(( "fd_tile: fd_cpuset_setaffinity_failed (%i-%s)\n\t"
     719           9 :                          "Unable to set the thread affinity for tile 0 on cpu %lu.  Attempting to\n\t"
     720           9 :                          "continue without explicitly specifying this cpu's thread affinity but it\n\t"
     721           9 :                          "is likely this thread group's performance and stability are compromised\n\t"
     722           9 :                          "(possibly catastrophically so).  Update --tile-cpus to specify a set of\n\t"
     723           9 :                          "allowed cpus that have been reserved for this thread group on this host\n\t"
     724           9 :                          "to eliminate this warning.",
     725           9 :                          errno, fd_io_strerror( errno ), cpu_idx ));
     726           9 :       fd_log_private_cpu_id_set( cpu_idx );
     727           9 :       fd_log_cpu_set   ( NULL );
     728           9 :       fd_log_thread_set( NULL );
     729           9 :     }
     730         111 :   }
     731             : 
     732             :   /* Tile 0 "pthread_create" */
     733        2555 :   fd_tile_private[0].pthread = pthread_self();
     734             :   /* FIXME: ON X86, DETECT IF TILE 0 STACK ISN'T HUGE PAGE AND WARN AS NECESSARY? */
     735             : 
     736             :   /* Tile 0 "thread manager init" */
     737        2555 :   fd_tile_private_id  = fd_tile_private_id0;
     738        2555 :   fd_tile_private_idx = 0UL;
     739             : 
     740        2555 : # if !FD_HAS_ASAN
     741        2555 :   fd_log_private_stack_discover( fd_log_private_main_stack_sz(),
     742        2555 :                                  &fd_tile_private_stack0, &fd_tile_private_stack1 ); /* logs details */
     743        2555 :   if( FD_UNLIKELY( !fd_tile_private_stack0 ) )
     744           0 :     FD_LOG_WARNING(( "stack diagnostics not available on this tile; attempting to continue" ));
     745        2555 : # endif /* FD_HAS_ASAN */
     746             : 
     747        2555 :   fd_tile_private_cpu_config( fd_tile_private_cpu_config_save, cpu_idx );
     748        2555 :   fd_tile_private[0].lock = NULL; /* Can't dispatch to tile 0 */
     749        2555 :   fd_tile_private[0].tile = NULL; /* " */
     750             : 
     751        2555 :   FD_LOG_INFO(( "fd_tile: boot tile %lu success (thread %lu:%lu in thread group %lu:%lu/%lu)",
     752        2555 :                 fd_tile_private_idx, app_id, fd_tile_private_id, app_id, fd_tile_private_id0, fd_tile_private_cnt ));
     753             : 
     754        2555 :   fd_memcpy( fd_tile_private_cpu_id, tile_to_cpu, fd_tile_private_cnt*sizeof(ushort) );
     755             : 
     756        2555 :   FD_LOG_INFO(( "fd_tile: boot success" ));
     757        2555 : }
     758             : 
     759             : void
     760        2555 : fd_tile_private_boot_str( char const * cpus ) {
     761        2555 :   ushort tile_to_cpu[ FD_TILE_MAX ];
     762        2555 :   ulong  tile_cnt = fd_tile_private_cpus_parse( cpus, tile_to_cpu );
     763             : 
     764        2555 :   if( FD_UNLIKELY( !tile_cnt ) ) {
     765        2444 :     FD_LOG_INFO(( "fd_tile: no cpus specified; treating thread group as single tile running on O/S assigned cpu(s)" ));
     766        2444 :     tile_to_cpu[0] = (ushort)65535;
     767        2444 :     tile_cnt       = 1UL;
     768        2444 :   }
     769             : 
     770        2555 :   fd_tile_private_map_boot( tile_to_cpu, tile_cnt );
     771        2555 : }
     772             : 
     773             : void
     774             : fd_tile_private_boot( int *    pargc,
     775        2555 :                       char *** pargv ) {
     776             :   /* Extract the tile configuration from the command line */
     777             : 
     778        2555 :   char const * cpus = fd_env_strip_cmdline_cstr( pargc, pargv, "--tile-cpus", "FD_TILE_CPUS", NULL );
     779             : 
     780        2555 :   if( !cpus ) FD_LOG_INFO(( "fd_tile: --tile-cpus not specified" ));
     781         111 :   else        FD_LOG_INFO(( "fd_tile: --tile-cpus \"%s\"", cpus ));
     782             : 
     783        2555 :   fd_tile_private_boot_str( cpus );
     784        2555 : }
     785             : 
     786             : void
     787        1412 : fd_tile_private_halt( void ) {
     788        1412 :   FD_LOG_INFO(( "fd_tile: halt" ));
     789             : 
     790        1412 :   fd_memset( fd_tile_private_cpu_id, 0, fd_tile_private_cnt*sizeof(ushort) );
     791             : 
     792        1412 :   ulong tile_cnt = fd_tile_private_cnt;
     793             : 
     794        1412 :   fd_tile_private_t * tile[ FD_TILE_MAX ]; /* FIXME: ALLOCA TO TILE_CNT? */
     795             : 
     796        1412 :   FD_LOG_INFO(( "fd_tile: disabling dispatch" ));
     797        1448 :   for( ulong tile_idx=1UL; tile_idx<tile_cnt; tile_idx++ ) tile[ tile_idx ] = fd_tile_private_lock( tile_idx );
     798             :   /* All tile to tile dispatches will fail at this point */
     799             : 
     800        1412 :   FD_LOG_INFO(( "fd_tile: waiting for all tasks to complete" ));
     801        1448 :   for( ulong tile_idx=1UL; tile_idx<tile_cnt; tile_idx++ )
     802          36 :     while( FD_VOLATILE_CONST( tile[ tile_idx ]->state )!=FD_TILE_PRIVATE_STATE_IDLE ) FD_YIELD();
     803             :   /* All halt transitions will be valid at this point */
     804             : 
     805        1412 :   FD_LOG_INFO(( "fd_tile: signaling all tiles to halt" ));
     806        1448 :   for( ulong tile_idx=1UL; tile_idx<tile_cnt; tile_idx++ ) FD_VOLATILE( tile[ tile_idx ]->state ) = FD_TILE_PRIVATE_STATE_HALT;
     807             :   /* All tiles are halting at this point.  tile[*] is no longer safe */
     808             : 
     809        1412 :   FD_LOG_INFO(( "fd_tile: waiting for all tiles to halt" ));
     810        1448 :   for( ulong tile_idx=1UL; tile_idx<tile_cnt; tile_idx++ ) {
     811          36 :     void * stack;
     812          36 :     int err = pthread_join( fd_tile_private[ tile_idx ].pthread, &stack );
     813          36 :     if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_tile: pthread_join failed (%i-%s)", err, fd_io_strerror( err ) ));
     814          36 :     fd_tile_private_stack_delete( stack );
     815          36 :     FD_LOG_INFO(( "fd_tile: halt tile %lu success", tile_idx ));
     816          36 :   }
     817             : 
     818             :   /* All tiles but this one are halted at this point */
     819             : 
     820        1412 :   fd_tile_private_cpu_restore( fd_tile_private_cpu_config_save );
     821             : 
     822        1412 :   FD_LOG_INFO(( "fd_tile: halt tile 0 success" ));
     823             : 
     824        1412 :   FD_LOG_INFO(( "fd_tile: cleaning up" ));
     825             : 
     826        1448 :   for( ulong tile_idx=1UL; tile_idx<tile_cnt; tile_idx++ ) fd_tile_private_unlock( tile_idx, NULL );
     827             : 
     828        1412 :   fd_memset( fd_tile_private_cpu_config_save, 0, sizeof(fd_tile_private_cpu_config_t) );
     829             : 
     830        1412 :   fd_tile_private_stack1 = 0UL;
     831        1412 :   fd_tile_private_stack0 = 0UL;
     832        1412 :   fd_tile_private_idx    = 0UL;
     833        1412 :   fd_tile_private_id     = 0UL;
     834             : 
     835        1412 :   fd_tile_private_cnt = 0UL;
     836        1412 :   fd_tile_private_id1 = 0UL;
     837        1412 :   fd_tile_private_id0 = 0UL;
     838             : 
     839        1412 :   FD_LOG_INFO(( "fd_tile: halt success" ));
     840        1412 : }

Generated by: LCOV version 1.14