force exit from readline() function

2019-01-28 08:14发布

问题:

I am writing program in c++ which runs GNU readline in separate thread. When main thread is exited I need to finish the thread in which readline() function is called. The readline() function is returned only when standart input came (enter pressed). Is there any way to send input to application or explicitly return from readline function? Thanks in advance.

回答1:

Instead of returning from main thread, call exit(errno). All other threads will be killed nastily!

Or, if you wanted to be nicer, and depending on your OS, you could send a signal to the readline thread, which would interrupt the syscall.

Or, if you wanted to be cleverer, you could run readline in async mode, using a select() loop with a timeout so that your thread never blocks in readine functions, and your thread can clean up after itself.



回答2:

I experimented with this situation as well. I thought perhaps one could call close(STDIN_FILENO), which does cause readline to return on the other thread, but for some reason it leaves the terminal in a bad state (doesn't echo characters so you can't see what you're typing). However, a call to the 'reset' command will fix this, so the full alternative is:

close(STDIN_FILENO);
pthread_join(...); // or whatever to wait for thread exit
system("reset -Q"); // -Q to avoid displaying cruft

However, the final better solution I used, inspired by the other suggestions, was to override rl_getc:

rl_getc_function = getc; // stdio's getc passes

and then you can use pthread_kill() to send a signal to interrupt the getc, which returns a -1 to readline, which returns a NULL to the calling thread so you can exit cleanly instead of looping for the next input (the same as would happen if the user EOF'd by ctrl-D)

Now you can have your cake (easy blocking readlines) and eat it too (be able to stop by external event without screwing up the terminal)



回答3:

C++ standard input is not designed to be thread safe. So, even if there was a method to programatically stop it from waiting input, you wouldn't be able to call it from another thread. Of course, there could be an implementation specific way to do so.



回答4:

Old thread but still readline API seems not explored.

In order to interrupt readline first I disabled readline signal handlers. Do not look at the ugly global_buffer I'm using - it's just an example

http://www.delorie.com/gnu/docs/readline/rlman_43.html

Reader Thread:

pthread_mutex_t lock;

int isBufferReady = 0;
char global_buffer[2500];  /// Assuming that reads will not be any bigger

void *reader_thread(void *arg)
{
   rl_getc_function = getc;
   rl_catch_signals = 0;
   rl_catch_sigwinch = 0;

   char *input;

   while ( (input = readline( NULL )) )
   {

      i = strlen(input)-1;


      if ( input[i] == '\0' )
         return NULL;

      /// Due to TAB there might be a whitespace in the end
      while ( i > 0 )
      {
          if ( isspace(input[i]) )
          {
              input[i] = '\0';
          }
          else
          {
             break;
          }
          i--;
     }

     pthread_mutex_lock(&lock);

     read_file_function( input, buffer );
     free(input);
     isBufferReady = 1;
     pthread_mutex_unlock(&lock);
   }

   printf( "Im closed \n" );

return NULL;
}

Signal handler:

volatile int keepRunning = 1;

void SIG_handler(int signal)
{

   int static sig_count = 0;

   switch ( signal )
   {


       case SIGUSR2:
       {
          /// Yeah I know I should not printf in a signal handler
          printf( "USR2: %d \n", sig_count++);

       break;
       }


       default:
       {
          printf( " SIGHANDLE\n" );
          keepRunning = 0;

       break;
       }
   }
}

main:

int main( int argc, char *argv[] )
{
   pthread_t file_reader;


    { /// Signal Handler registration
        struct sigaction sigact = {{0}};
        sigact.sa_handler = SIG_handler;

        // sigact.sa_flags = SA_RESTART;

        sigaction(SIGINT , &sigact, NULL);
        sigaction(SIGQUIT, &sigact, NULL);
        sigaction(SIGTERM, &sigact, NULL);
        sigaction(SIGHUP, &sigact, NULL);
        // sigaction(SIGUSR1, &sigact, NULL);
        sigaction(SIGUSR2, &sigact, NULL);
    }

   pthread_create( &file_reader, NULL, reader_thread, NULL );

   while(keepRunning)
   {
       pthread_mutex_lock(&lock);
           if( !isBufferReady )
           {
               ... fill in global_buffer according to some algorithm
           }
       pthread_mutex_unlock(&lock);
       usleep(10);

       pthread_mutex_lock(&lock);
           if(isBufferReady)
             isBufferReady = 0;

           ... some operation on the 'global_buffer' like write its contents to socket
       pthread_mutex_unlock(&lock);
       usleep(10);
   }

   signal(SIGINT, SIG_DFL);

   pthread_cancel( file_reader );
   pthread_join( file_reader, NULL);
   pthread_mutex_destroy(&lock);

   rl_cleanup_after_signal();

return 0;
}

With this (nowhere near perfect) code snippet I was able to finally interrupt readline without described prevously flakiness.

Used this code snippet for interactive debug purposes where I had prepared packets in simple text files and read-in those files with the help of readline.



标签: c++ readline