I am writing a console application that accepts input (one-line commands) from stdin. This application reads input in a dedicated thread, all input is stored in a queue and later processed by the main thread in a safe way. When the exit command is entered by the user, it is intercepted by the input thread which stops listening for new input, the thread is joined into the main one, and the application stops as requested.
Now I am containerizing this application, but I still want to be able to attach to the container and input commands from stdin, so I specified tty and stdin_open to be true in my docker compose service file, and that did the trick.
But I also want docker compose to be able to gracefully stop the application, so I decided to implement sigTerm() in my application so that it can receive the signal from docker compose and gracefully stop, however I'm stuck on that part, because the input thread is blocking while waiting for input on stdin. I can properly receive the signal, that's not at all the point here, but I'm looking for a way to be able to properly stop my containerized application while still being able to input commands from the keyboard.
My application could be simplified like that :
void gracefulStop() {
  while (getThreadCount() > 1) { // this function exists somewhere else.
    if (userInputThread.joinable()) {
      userInputThread.join();
      removeFromThreadCount(); // this function exists somewhere else.
    }
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }
  exit(SUCCESS);
}
void sigTerm(int s) {
  // Maybe do some stuff here, but what...
  gracefulStop();
}
void userInputLoopThreadFunc() {
  addToThreadCount(); // this function exists somewhere else.
  while (keepGoing) {
    char buf[4096];
    if (!fgets(buf, sizeof(buf), stdin)) {
      break; // we couldn't read from stdin, stop trying.
    }
    std::string input = std::string(buf); // we received a command
    // Intercept exit command
    if (input.starts_with("exit")) {
      keepGoing = false;
    }
    // IRL there's thread safety 
    userInputQueue.push(input); // this will be processed by mainLoop() later
  }
}
int main(int argc, char **argv) {
  // Register the signal
  signal(SIGTERM, sigTerm);
  // Begin listening to user input
  userInputThread = std::thread(&userInputLoopThreadFunc, this);
  // this mainLoop function contains the core of the application
  // as well as the processing code of the user input
  mainLoop();
  // if mainLoop function returned, we received the 'exit' command
  gracefulStop();
}
I've read multiple question/answers like this one about non-blocking user input (the accepted answer advises to use a dedicated thread for input, which is what I am doing), or this other one about how to stop reading stdin, and the accepted answer seems promising but :
- using ncurses for what I'm trying to do seems really overkill
 - If using 
select()and the timeout mechanism described, what would happen if the timeout occurs while typing a command? 
Also I've read about the c++20 jthread here :
The class jthread represents a single thread of execution. It has the same general behavior as std::thread, except that jthread automatically rejoins on destruction, and can be cancelled/stopped in certain situations.
But I'm not sure that would help me here.
I'm thinking about multiple possibilities to solve my issue :
- Find a way to send a newline character to the 
stdinof my application without user interaction, would be hackish if at all possible but would probably unblockfgets. - Kill the thread, I understand killing a thread is considered a bad practice, but since the only thing I'm doing here is stopping the application, maybe I can live with that, would there be any side effect? How would I do that?
 - Rewriting user input in another way (unknown to me yet, 
jthread, something else?) that would allowsigTerm()to stop the application. - Maybe use ncurses (would that really help me to stop the application by receiving a signal?)
 - Go with 
select()and the timeout mechanism and live with the risk of an interrupted input - Give up on user input and have some vacation time.