LCOV - code coverage report
Current view: top level - disco/sleep - fd_sleep.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 132 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 11 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "fd_sleep.h"
       3             : #include "../topo/fd_topo.h"
       4             : 
       5             : #include <errno.h>
       6             : #include <float.h>
       7             : #include <time.h>
       8             : #include <linux/futex.h>
       9             : #include <sys/epoll.h>
      10             : #include <sys/eventfd.h>
      11             : #include <sys/syscall.h>
      12             : #include <unistd.h>
      13             : 
      14           0 : FD_FN_CONST ulong fd_sleep_align    ( void ) { return FD_SLEEP_ALIGN;    }
      15           0 : FD_FN_CONST ulong fd_sleep_footprint( void ) { return sizeof(fd_sleep_t); }
      16             : 
      17             : void *
      18             : fd_sleep_new( void * shmem,
      19           0 :               double tick_per_ns ) {
      20           0 :   if( FD_UNLIKELY( !shmem ) ) {
      21           0 :     FD_LOG_WARNING(( "NULL shmem" ));
      22           0 :     return NULL;
      23           0 :   }
      24             : 
      25           0 :   if( FD_UNLIKELY( !(tick_per_ns>0.0 && tick_per_ns<=DBL_MAX) ) ) {
      26           0 :     FD_LOG_WARNING(( "bad tick_per_ns" ));
      27           0 :     return NULL;
      28           0 :   }
      29             : 
      30           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_sleep_align() ) ) ) {
      31           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
      32           0 :     return NULL;
      33           0 :   }
      34             : 
      35           0 :   fd_sleep_t * sleep = (fd_sleep_t *)shmem;
      36           0 :   fd_memset( sleep, 0, fd_sleep_footprint() );
      37           0 :   for( ulong i=0UL; i<FD_SLEEP_TILE_MAX; i++ ) sleep->tile[ i ].word = 1UL; /* running */
      38           0 :   sleep->tick_per_ns = tick_per_ns;
      39             : 
      40           0 :   FD_COMPILER_MFENCE();
      41           0 :   FD_VOLATILE( sleep->magic ) = FD_SLEEP_MAGIC;
      42           0 :   FD_COMPILER_MFENCE();
      43             : 
      44           0 :   return shmem;
      45           0 : }
      46             : 
      47             : ulong
      48             : fd_sleep_wake_table( fd_sleep_wake_t *      wake,
      49             :                      struct fd_topo const * topo,
      50           0 :                      ulong                  link_id ) {
      51           0 :   if( FD_UNLIKELY( topo->sleep_obj_id==ULONG_MAX ) ) return 0UL;
      52           0 :   ulong mask[ FD_SLEEP_BITS_CNT ] = {0};
      53           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
      54           0 :     fd_topo_tile_t const * consumer = &topo->tiles[ i ];
      55           0 :     for( ulong j=0UL; j<consumer->in_cnt; j++ ) {
      56           0 :       if( FD_UNLIKELY( consumer->in_link_id[ j ]==link_id && consumer->in_link_poll[ j ] ) ) {
      57           0 :         mask[ consumer->id>>6 ] |= 1UL<<(consumer->id&63UL);
      58           0 :       }
      59           0 :     }
      60           0 :   }
      61             : 
      62           0 :   ulong cnt = 0UL;
      63           0 :   for( ulong w=0UL; w<FD_SLEEP_BITS_CNT; w++ ) {
      64           0 :     if( FD_UNLIKELY( mask[ w ] ) ) wake[ cnt++ ] = (fd_sleep_wake_t){ .w=w, .mask=mask[ w ] };
      65           0 :   }
      66           0 :   return cnt;
      67           0 : }
      68             : 
      69             : fd_sleep_t *
      70           0 : fd_sleep_join( void * shsleep ) {
      71           0 :   if( FD_UNLIKELY( !shsleep ) ) {
      72           0 :     FD_LOG_WARNING(( "NULL shsleep" ));
      73           0 :     return NULL;
      74           0 :   }
      75             : 
      76           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shsleep, fd_sleep_align() ) ) ) {
      77           0 :     FD_LOG_WARNING(( "misaligned shsleep" ));
      78           0 :     return NULL;
      79           0 :   }
      80             : 
      81           0 :   fd_sleep_t * sleep = (fd_sleep_t *)shsleep;
      82             : 
      83           0 :   if( FD_UNLIKELY( sleep->magic!=FD_SLEEP_MAGIC ) ) {
      84           0 :     FD_LOG_WARNING(( "bad magic" ));
      85           0 :     return NULL;
      86           0 :   }
      87             : 
      88           0 :   return sleep;
      89           0 : }
      90             : 
      91             : static inline int
      92           0 : woken_cause( ulong const * word ) {
      93           0 :   return FD_VOLATILE_CONST( word[0] )==FD_SLEEP_WORD( FD_SLEEP_UNPARK_DEADLINE ) ? FD_SLEEP_UNPARK_DEADLINE : FD_SLEEP_UNPARK_RING;
      94           0 : }
      95             : 
      96             : int
      97             : fd_sleep_park_wait( ulong * word,
      98             :                     long    deadline_ticks,
      99           0 :                     double  tick_per_ns ) {
     100           0 :   struct timespec   ts;
     101           0 :   struct timespec * timeout = NULL; /* untimed: only a wake ends the park */
     102           0 :   if( FD_LIKELY( deadline_ticks!=LONG_MAX ) ) {
     103             :     /* Absolute timeout, anchored on a clock read taken before the tick
     104             :        read: preemption anywhere after can only shorten the sleep, never
     105             :        stretch it past deadline_ticks */
     106           0 :     clock_gettime( CLOCK_MONOTONIC, &ts );
     107           0 :     long remaining = (long)((double)(deadline_ticks-fd_tickcount())/tick_per_ns);
     108           0 :     if( FD_UNLIKELY( remaining<=0L ) ) return FD_SLEEP_UNPARK_DEADLINE;
     109           0 :     long abs_ns = ts.tv_sec*(long)1e9 + ts.tv_nsec + remaining;
     110           0 :     ts.tv_sec  = abs_ns/(long)1e9;
     111           0 :     ts.tv_nsec = abs_ns%(long)1e9;
     112           0 :     timeout = &ts;
     113           0 :   }
     114             : 
     115           0 :   for(;;) {
     116           0 :     long res = syscall( SYS_futex, (uint *)word, FUTEX_WAIT_BITSET, 0U, timeout, NULL, FUTEX_BITSET_MATCH_ANY );
     117           0 :     if( FD_LIKELY( !res ) ) return woken_cause( word );
     118             : 
     119           0 :     switch( errno ) {
     120           0 :       case ETIMEDOUT: return FD_SLEEP_UNPARK_DEADLINE;
     121           0 :       case EAGAIN:    return woken_cause( word );   /* word already set */
     122           0 :       case EINTR:     continue;                     /* signal: same absolute timeout */
     123           0 :       default: FD_LOG_ERR(( "futex(FUTEX_WAIT_BITSET) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     124           0 :     }
     125           0 :   }
     126           0 : }
     127             : 
     128             : void
     129             : fd_sleep_wake_one( ulong * word,
     130           0 :                    int     cause ) {
     131           0 :   FD_VOLATILE( word[0] ) = FD_SLEEP_WORD( cause );
     132           0 :   long res = syscall( SYS_futex, (uint *)word, FUTEX_WAKE, 1, NULL, NULL, 0 );
     133           0 :   if( FD_UNLIKELY( -1L==res ) ) FD_LOG_ERR(( "futex(FUTEX_WAKE) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     134           0 : }
     135             : 
     136             : void
     137             : fd_sleep_wake_eventfd( ulong * word,
     138             :                        int     eventfd,
     139           0 :                        int     cause ) {
     140           0 :   FD_VOLATILE( word[0] ) = FD_SLEEP_WORD( cause );
     141           0 :   ulong one = 1UL;
     142           0 :   long res = write( eventfd, &one, sizeof(ulong) );
     143           0 :   if( FD_UNLIKELY( res!=(long)sizeof(ulong) ) ) FD_LOG_ERR(( "write(eventfd %d) failed (%i-%s)", eventfd, errno, fd_io_strerror( errno ) ));
     144           0 : }
     145             : 
     146             : int
     147             : fd_sleep_park_wait_epoll( int                  epfd,
     148             :                           ulong const *        word,
     149             :                           struct epoll_event * evs,
     150             :                           int                  ev_max,
     151             :                           int *                ev_cnt,
     152             :                           long                 deadline_ticks,
     153           0 :                           double               tick_per_ns ) {
     154           0 :   *ev_cnt = 0;
     155           0 :   for(;;) {
     156             :     /* ms timeout: round up, the sweep delivers the exact deadline.
     157             :        LONG_MAX: no timeout, the sweep enforces the cap. */
     158           0 :     int timeout_ms = -1;
     159           0 :     if( FD_LIKELY( deadline_ticks!=LONG_MAX ) ) {
     160           0 :       long remaining = (long)((double)(deadline_ticks-fd_tickcount())/tick_per_ns);
     161           0 :       if( FD_UNLIKELY( remaining<=0L ) ) return FD_SLEEP_UNPARK_DEADLINE;
     162           0 :       timeout_ms = (int)( (remaining+999999L)/1000000L );
     163           0 :     }
     164             : 
     165           0 :     int n = epoll_pwait( epfd, evs, ev_max, timeout_ms, NULL );
     166           0 :     if( FD_UNLIKELY( n<0 ) ) {
     167           0 :       if( FD_LIKELY( errno==EINTR ) ) continue;
     168           0 :       FD_LOG_ERR(( "epoll_pwait() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     169           0 :     }
     170           0 :     if( FD_UNLIKELY( !n ) ) continue; /* timed out, recheck the deadline */
     171             : 
     172           0 :     int doorbell_only = 1;
     173           0 :     for( int i=0; i<n; i++ ) doorbell_only &= evs[ i ].data.u64==FD_SLEEP_EPOLL_DOORBELL;
     174           0 :     if( FD_UNLIKELY( doorbell_only ) ) {
     175           0 :       if( FD_UNLIKELY( !FD_VOLATILE_CONST( word[0] ) ) ) continue; /* stale */
     176           0 :       if( FD_UNLIKELY( woken_cause( word )==FD_SLEEP_UNPARK_DEADLINE ) ) return FD_SLEEP_UNPARK_DEADLINE;
     177           0 :     }
     178             : 
     179           0 :     *ev_cnt = n;
     180           0 :     return FD_SLEEP_UNPARK_RING;
     181           0 :   }
     182           0 : }
     183             : 
     184             : void
     185           0 : fd_sleep_eventfd_install( fd_topo_t const * topo ) {
     186           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     187           0 :     fd_topo_tile_t const * tile = &topo->tiles[ i ];
     188           0 :     if( FD_LIKELY( !tile->sleep_eventfd ) ) continue;
     189           0 :     FD_TEST( tile->id<FD_TOPO_MAX_TILES );
     190           0 :     int fd = eventfd( 0U, EFD_NONBLOCK );
     191           0 :     if( FD_UNLIKELY( -1==fd ) ) FD_LOG_ERR(( "eventfd() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     192           0 :     if( FD_LIKELY( fd!=FD_SLEEP_EVENTFD( tile->id ) ) ) {
     193           0 :       if( FD_UNLIKELY( -1==dup2( fd, FD_SLEEP_EVENTFD( tile->id ) ) ) ) FD_LOG_ERR(( "dup2(%d,%d) failed (%i-%s)", fd, FD_SLEEP_EVENTFD( tile->id ), errno, fd_io_strerror( errno ) ));
     194           0 :       if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     195           0 :     }
     196           0 :   }
     197           0 : }

Generated by: LCOV version 1.14