LCOV - code coverage report
Current view: top level - src/util - select.h (source / functions) Hit Total Coverage
Test: mosh-1.3.2 Code Coverage Lines: 76 79 96.2 %
Date: 2022-02-06 20:19:53 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :     Mosh: the mobile shell
       3             :     Copyright 2012 Keith Winstein
       4             : 
       5             :     This program is free software: you can redistribute it and/or modify
       6             :     it under the terms of the GNU General Public License as published by
       7             :     the Free Software Foundation, either version 3 of the License, or
       8             :     (at your option) any later version.
       9             : 
      10             :     This program is distributed in the hope that it will be useful,
      11             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :     GNU General Public License for more details.
      14             : 
      15             :     You should have received a copy of the GNU General Public License
      16             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      17             : 
      18             :     In addition, as a special exception, the copyright holders give
      19             :     permission to link the code of portions of this program with the
      20             :     OpenSSL library under certain conditions as described in each
      21             :     individual source file, and distribute linked combinations including
      22             :     the two.
      23             : 
      24             :     You must obey the GNU General Public License in all respects for all
      25             :     of the code used other than OpenSSL. If you modify file(s) with this
      26             :     exception, you may extend this exception to your version of the
      27             :     file(s), but you are not obligated to do so. If you do not wish to do
      28             :     so, delete this exception statement from your version. If you delete
      29             :     this exception statement from all source files in the program, then
      30             :     also delete it here.
      31             : */
      32             : 
      33             : #ifndef SELECT_HPP
      34             : #define SELECT_HPP
      35             : 
      36             : #include <string.h>
      37             : #include <errno.h>
      38             : #include <signal.h>
      39             : #include <sys/select.h>
      40             : #include <assert.h>
      41             : 
      42             : #include "fatal_assert.h"
      43             : #include "timestamp.h"
      44             : 
      45             : /* Convenience wrapper for pselect(2).
      46             : 
      47             :    Any signals blocked by calling sigprocmask() outside this code will still be
      48             :    received during Select::select().  So don't do that. */
      49             : 
      50             : class Select {
      51             : public:
      52        1394 :   static Select &get_instance( void ) {
      53             :     /* COFU may or may not be thread-safe, depending on compiler */
      54        1394 :     static Select instance;
      55        1394 :     return instance;
      56             :   }
      57             : 
      58             : private:
      59         900 :   Select()
      60         900 :     : max_fd( -1 )
      61             :     /* These initializations are not used; they are just
      62             :        here to appease -Weffc++. */
      63         900 :     , all_fds( dummy_fd_set )
      64         900 :     , read_fds( dummy_fd_set )
      65         900 :     , empty_sigset( dummy_sigset )
      66         900 :     , consecutive_polls( 0 )
      67             :   {
      68       15300 :     FD_ZERO( &all_fds );
      69       15300 :     FD_ZERO( &read_fds );
      70             : 
      71        1800 :     clear_got_signal();
      72         900 :     fatal_assert( 0 == sigemptyset( &empty_sigset ) );
      73         900 :   }
      74             : 
      75       34032 :   void clear_got_signal( void )
      76             :   {
      77         900 :     for ( volatile sig_atomic_t *p = got_signal;
      78     2246112 :           p < got_signal + sizeof( got_signal ) / sizeof( *got_signal );
      79             :           p++ ) {
      80     2212080 :       *p = 0;
      81             :     }
      82             :   }
      83             : 
      84             :   /* not implemented */
      85             :   Select( const Select & );
      86             :   Select &operator=( const Select & );
      87             : 
      88             : public:
      89       65241 :   void add_fd( int fd )
      90             :   {
      91       65241 :     if ( fd > max_fd ) {
      92        1352 :       max_fd = fd;
      93             :     }
      94       65241 :     FD_SET( fd, &all_fds );
      95       65241 :   }
      96             : 
      97             :   void clear_fds( void )
      98             :   {
      99      563244 :     FD_ZERO( &all_fds );
     100             :   }
     101             : 
     102        4044 :   static void add_signal( int signum )
     103             :   {
     104        4044 :     fatal_assert( signum >= 0 );
     105        4044 :     fatal_assert( signum <= MAX_SIGNAL_NUMBER );
     106             : 
     107             :     /* Block the signal so we don't get it outside of pselect(). */
     108        4044 :     sigset_t to_block;
     109        4044 :     fatal_assert( 0 == sigemptyset( &to_block ) );
     110        4044 :     fatal_assert( 0 == sigaddset( &to_block, signum ) );
     111        4044 :     fatal_assert( 0 == sigprocmask( SIG_BLOCK, &to_block, NULL ) );
     112             : 
     113             :     /* Register a handler, which will only be called when pselect()
     114             :        is interrupted by a (possibly queued) signal. */
     115        4044 :     struct sigaction sa;
     116        4044 :     sa.sa_flags = 0;
     117        4044 :     sa.sa_handler = &handle_signal;
     118        4044 :     fatal_assert( 0 == sigfillset( &sa.sa_mask ) );
     119        4044 :     fatal_assert( 0 == sigaction( signum, &sa, NULL ) );
     120        4044 :   }
     121             : 
     122             :   /* timeout unit: milliseconds; negative timeout means wait forever */
     123       33132 :   int select( int timeout )
     124             :   {
     125       33132 :     memcpy( &read_fds,  &all_fds, sizeof( read_fds  ) );
     126       33132 :     clear_got_signal();
     127             : 
     128             :     /* Rate-limit and warn about polls. */
     129       33132 :     if ( verbose > 1 && timeout == 0 ) {
     130           2 :       fprintf( stderr, "%s: got poll (timeout 0)\n", __func__ );
     131             :     }
     132       33132 :     if ( timeout == 0 && ++consecutive_polls >= MAX_POLLS ) {
     133           0 :       if ( verbose > 1 && consecutive_polls == MAX_POLLS ) {
     134           0 :         fprintf( stderr, "%s: got %d polls, rate limiting.\n", __func__, MAX_POLLS );
     135             :       }
     136             :       timeout = 1;
     137       33132 :     } else if ( timeout != 0 && consecutive_polls ) {
     138         448 :       if ( verbose > 1 && consecutive_polls >= MAX_POLLS ) {
     139           0 :         fprintf( stderr, "%s: got %d consecutive polls\n", __func__, consecutive_polls );
     140             :       }
     141         448 :       consecutive_polls = 0;
     142             :     }
     143             : 
     144             : #ifdef HAVE_PSELECT
     145       33132 :     struct timespec ts;
     146       66264 :     struct timespec *tsp = NULL;
     147             : 
     148       33132 :     if ( timeout >= 0 ) {
     149       33132 :       ts.tv_sec  = timeout / 1000;
     150       33132 :       ts.tv_nsec = 1000000 * (long( timeout ) % 1000);
     151       33132 :       tsp = &ts;
     152             :     }
     153             : 
     154       33132 :     int ret = ::pselect( max_fd + 1, &read_fds, NULL, NULL, tsp, &empty_sigset );
     155             : #else
     156             :     struct timeval tv;
     157             :     struct timeval *tvp = NULL;
     158             :     sigset_t old_sigset;
     159             : 
     160             :     if ( timeout >= 0 ) {
     161             :       tv.tv_sec  = timeout / 1000;
     162             :       tv.tv_usec = 1000 * (long( timeout ) % 1000);
     163             :       tvp = &tv;
     164             :     }
     165             : 
     166             :     int ret = sigprocmask( SIG_SETMASK, &empty_sigset, &old_sigset );
     167             :     if ( ret != -1 ) {
     168             :       ret = ::select( max_fd + 1, &read_fds, NULL, NULL, tvp );
     169             :       sigprocmask( SIG_SETMASK, &old_sigset, NULL );
     170             :     }
     171             : #endif
     172             : 
     173       33132 :     if ( ret == 0 || ( ret == -1 && errno == EINTR ) ) {
     174             :       /* Look for and report Cygwin select() bug. */
     175        5574 :       if ( ret == 0 ) {
     176       36656 :         for ( int fd = 0; fd <= max_fd; fd++ ) {
     177       31128 :           if ( FD_ISSET( fd, &read_fds ) ) {
     178       31128 :             fprintf( stderr, "select(): nfds = 0 but read fd %d is set\n", fd );
     179             :           }
     180             :         }
     181             :       }
     182             :       /* The user should process events as usual. */
     183       94758 :       FD_ZERO( &read_fds );
     184             :       ret = 0;
     185             :     }
     186             : 
     187       33132 :     freeze_timestamp();
     188             : 
     189       33132 :     return ret;
     190             :   }
     191             : 
     192       65241 :   bool read( int fd )
     193             : #if FD_ISSET_IS_CONST
     194             :     const
     195             : #endif
     196             :   {
     197       65241 :     assert( FD_ISSET( fd, &all_fds ) );
     198       65241 :     return FD_ISSET( fd, &read_fds );
     199             :   }
     200             : 
     201             :   /* This method consumes a signal notification. */
     202       63572 :   bool signal( int signum )
     203             :   {
     204       63572 :     fatal_assert( signum >= 0 );
     205       63572 :     fatal_assert( signum <= MAX_SIGNAL_NUMBER );
     206             :     /* XXX This requires a guard against concurrent signals. */
     207       63572 :     bool rv = got_signal[ signum ];
     208       63572 :     got_signal[ signum ] = 0;
     209       33176 :     return rv;
     210             :   }
     211             : 
     212             :   /* This method does not consume signal notifications. */
     213       27044 :   bool any_signal( void ) const
     214             :   {
     215       27044 :     bool rv = false;
     216     1757860 :     for (int i = 0; i < MAX_SIGNAL_NUMBER; i++) {
     217     1730816 :       rv |= got_signal[ i ];
     218             :     }
     219       27044 :     return rv;
     220             :   }
     221             : 
     222         900 :   static void set_verbose( unsigned int s_verbose ) { verbose = s_verbose; }
     223             : 
     224             : private:
     225             :   static const int MAX_SIGNAL_NUMBER = 64;
     226             :   /* Number of 0-timeout selects after which we begin to think
     227             :    * something's wrong. */
     228             :   static const int MAX_POLLS = 10;
     229             : 
     230             :   static void handle_signal( int signum );
     231             : 
     232             :   int max_fd;
     233             : 
     234             :   /* We assume writes to got_signal are atomic, though we also try to mask out
     235             :      concurrent signal handlers. */
     236             :   volatile sig_atomic_t got_signal[ MAX_SIGNAL_NUMBER + 1 ];
     237             : 
     238             :   fd_set all_fds, read_fds;
     239             : 
     240             :   sigset_t empty_sigset;
     241             : 
     242             :   static fd_set dummy_fd_set;
     243             :   static sigset_t dummy_sigset;
     244             :   int consecutive_polls;
     245             :   static unsigned int verbose;
     246             : };
     247             : 
     248             : #endif

Generated by: LCOV version 1.14