1

I need to handle SIGINT in my console application, I found many examples about sa_flags = 0; of sigaction structure - and it's mean getchar will be aborted and return -1. But this is not working with multithreading

Look at my code


int p[2];
FILE *stdin_writer = nullptr;
void int_handler(int signum)
{
 //////do nothing
 //////or write something into stdin..... also no help
 //write(fileno(stdin), s, sizeof s - 1);
 //////using pipes deadlocks application
 //stdin_writer = fdopen(p[1], "w");
 //fputc('g', stdin_writer);
}
void run()
{
 //pipe(p);
 //dup2(p[0], STDIN_FILENO);
 printf("before getchar\n");
 auto c = getchar();
 printf("after getchar\n");
}
int main()
{
 //////Setup SIGINT handler
 struct sigaction sh;
 sh.sa_handler = int_handler;
 sigemptyset(&sh.sa_mask);
 sh.sa_flags = 0;
 sigaction(SIGINT, &sh, NULL);
 return 0;
 //////Setup console
 termios _prev{ 0 };
 // grab old terminal i/o settings
 tcgetattr(0, &_prev);
 // make new settings same as old settings 
 auto current = _prev;
 // disable buffered i/o
 current.c_lflag &= ~ICANON;
 // set no echo mode
 current.c_lflag &= ~ECHO;
 // use these new terminal i/o settings now
 tcsetattr(0, TCSANOW, &current);
 std::thread trd(&run);
 trd.join();
} 

I want to interrupt getchar on Ctrl+C - similar code works just fine in single thread version (without std::thread stuff), but not in multithread. Please help me anyone - I am already stuck in this problem on whole day

Misha Akopov
13.2k27 gold badges73 silver badges87 bronze badges
asked Dec 25, 2019 at 15:36
6
  • When a process receives a signal, it can be directed to any thread that does not have it blocked. Thus, if you want to be sure that the getchar() is interrupted by the signal then you must make sure that the signal is delivered to its thread by blocking it in all other threads. Commented Dec 25, 2019 at 15:55
  • My application is very big(its just test code), so i don't know all threads of my app, threads could be created by plugins, or in other user code or in 3rd libs. So i can't use this solution... Commented Dec 25, 2019 at 17:23
  • 1
    There is no other solution, @fsmoke. If you want to rely on using a signal to interrupt a system call, then you must take appropriate measures to ensure that the signal is delivered to the thread in which that syscall is running. If you cannot ensure that, then you need an altogether different approach. Commented Dec 25, 2019 at 20:12
  • " If you want to rely on using a signal to interrupt a system call", sorry but i don't know other way to interrupt getchar.. may be there is another way..without signals?? Commented Dec 26, 2019 at 7:59
  • 1
    Nothing in your answer contradicts anything I said, @fsmoke. You have given up on the SIGINT generated by the keystroke necessarily being delivered to any specific thread, so your approach does not satisfy the conditions expressed in my previous comments, and pthread_kill directs a signal to a specific thread, not to a whole process. You're welcome for being nudged in the right direction. Commented Dec 26, 2019 at 16:43

1 Answer 1

2

Finally I found the right solution. SIGINT signal interrupts getchar operation only if this signal sent to the same thread with getchar. So if your application have tons of threads - we have situation in which probability of catching SIGINT in right thread is very small. So you will be pending getchar infinitely....

But! if we will carafully read pthread documentation about pthread_kill function we can see this line

pthread_kill - send a signal to a thread

pthread_kill - not killing thread in fact it's send signal. Eureka! We can resend signal SIGINT into the right thread. See code below

std::optional<std::thread> trd;
std::mutex mtx;
void int_handler(int signum)
{
 std::lock_guard lk(mtx);
 if (trd && std::this_thread::get_id() != trd->get_id())
 pthread_kill(trd->native_handle(), signum);
}
void run()
{
 printf("before getchar\n");
 auto c = getchar();
 printf("after getchar\n");
}
int main()
{
 //////Setup SIGINT handler
 struct sigaction sh;
 sh.sa_handler = int_handler;
 sigemptyset(&sh.sa_mask);
 sh.sa_flags = 0;
 sigaction(SIGINT, &sh, NULL);
 return 0;
 //////Setup console
 termios _prev{ 0 };
 // grab old terminal i/o settings
 tcgetattr(0, &_prev);
 // make new settings same as old settings 
 auto current = _prev;
 // disable buffered i/o
 current.c_lflag &= ~ICANON;
 // set no echo mode
 current.c_lflag &= ~ECHO;
 // use these new terminal i/o settings now
 tcsetattr(0, TCSANOW, &current);
 {
 std::lock_guard lk(mtx);
 trd.emplace(&run);
 }
 trd->join();
} 
answered Dec 26, 2019 at 10:16
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for this! I found this question when I searched for ways to interrupt getchar, and found the solution to the thread problem before I even had it myself. It became part not only of my code but also of this blog post.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.