force exit from readline() function

前端 未结 4 1046
遥遥无期
遥遥无期 2020-12-21 10:47

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 re

相关标签:
4条回答
  • 2020-12-21 11:03

    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.

    0 讨论(0)
  • 2020-12-21 11:18

    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.

    0 讨论(0)
  • 2020-12-21 11:22

    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.

    0 讨论(0)
  • 2020-12-21 11:23

    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)

    0 讨论(0)
提交回复
热议问题