5

In my application, I have a thread which just runs std::getline() constantly to get user input, to then send it to a server via a socket. I've made a signal handler to close the program properly when I receive SIGINT. The signal calls a function named stop(), which closes the socket, and then I try to join my input thread. I have a problem stopping this thread, which I don't know how to solve.

atomic<bool> running = true;
int sock; //socket descriptor

void stop() {
    running = false; 
    close(sock);
}

wstring message = L"";
thread inputThread = thread([&]{
    while(running) {
        getline(wcin, message, L'\n');
        if (message.size() > 0) {
            send(sock, reinterpret_cast<const void*>(message.data()), (message.size()+1) * sizeof(wchar_t), 0);
        }
    }
});

The problem is that getline() blocks the thread even when running is false, preventing the thread from joining properly. send() does not block it, because the socket is already closed by that time. How do I close this thread, or how do I interrupt getline()?

I've tried using close(STDIN_FILENO); in the stop() function, but this seemed to not help, it does not end the thread.

5
  • 2
    For asynchronous input you need to use platform-specific functionality. For e.g. Linux or macOS look into termcap, for Windows there are other functions. Commented Sep 17 at 20:24
  • 1
    Use std::jthread and you won't need a atomic<bool> running = true; or a stop() function, and you can stop the thread by simply destroying it. Commented Sep 17 at 20:40
  • 1
    @DrewDormann You cannot stop a thread. You can request a thread to stop. A thread that was not programmed to honour a request to stop won't. Destroying a jthread doesn't stop anything. Commented Sep 17 at 23:44
  • 2
    IMO just don't try to join the thread when you exit. It's not clean but the alternatives are more complex and platform specific. Forget the atomic variable and just accept that this thread never ends until the program does. You probably need to have a mutex protecting your send() though. Commented Sep 17 at 23:49
  • The standard has no support of asynchronous input, and no means to programatically "interrupt" synchronous functions like std::getline(). Either your user needs to provide input that causes the loop to terminate, or you need to use some platform-specific means of asynchronous input and means of interrupting it. Commented Sep 18 at 2:34

2 Answers 2

2

Your current approach may work quite fine with normal files, as these getline() would return in a relatively predictable time and each occurence of the loop is an opportunity to leave it. The only issue is that you don't check if the read was successful or if we're aleady at the end fo the file.

If getline() ought to read from an input device like cin, you cannot prevent the function from being blocked waiting for input in absence of any fatal error. The approach is then to read the input only if we're sure there is some input to read.

Unfortunately, there is no universal and portable way to deal with such situations. The simplest approach could just be to check for available input as explained in this SO answer or here. If no input is available, you'd just loop once more giving the opportunity of stopping the thread.

Sign up to request clarification or add additional context in comments.

4 Comments

having fd_set fds; FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); then using select(STDIN_FILENO + 1, &fds, NULL, NULL, &a_non_null_timeout); doesn't allow in a portable way to know in a given timeout if something is newly available or not on std::cinor std::wcin?
Good point. It's POSIX pubs.opengroup.org/onlinepubs/009695199/basedefs/sys/… - so portable (unix linux and windows) on some families of OS but not as largely as the std library
I see that windows for example support it but use another header: learn.microsoft.com/en-us/windows/win32/api/winsock/… and shows subtle differences learn.microsoft.com/en-us/windows/win32/winsock/…
As I remember I always used the FD_XXX macros even being under unix linux. Of course on eof on the standard input select returns 1 (only one FD_SET) but readline does not block and the eofbit will be set
-3

well getline on wcin is blocking and can not be interrupted just by setting a flag or closing the socket. That why your thread never exits, i used C++ back in uni, but as far i know the simple way is to just detach the thread instead of trying to join it. this should work fine.

thread inputThread([&]{
    while (true) {
        wstring message;
        if (!getline(wcin, message)) break
        if (!message.empty()) {
            send(sock, message.data(), (message.size()+1) * sizeof(wchar_t), 0);
        }
    }
});

running = false;
close(sock);
inputThread.detach(); // don't wait on getline

18 Comments

Asker will probably get away with this, but there is always uncertainty around what will happen when a thread outlives the program and whatever program data the thread is using.
yeah fair point, detach is not really clean. safer way would be non blocking input or restructuring shutdown.
An easy way to avoid issues with C++ compilers cleaning up on exit is to call std::_Exit -- it's basically a shortcut out of your program without the compiler running pointless cleanups.
@JaredMcCarthy I like that you try to give advice but "the simple way is to just detach the thread" as a remedy is possibly acceptable in experiments. It's horrible pratice. The following "this should work fine" is a feast for any bughunters that'll follow. Simply: No.
yeah true, mine was more like quick workaround. for real clean solution need non blocking input or redesign shutdown.
I'm not sure this buys you anything because you could just not call join() on the thread before exiting for much the same effect.
std::thread's destructor will call std::terminate if the thread is not joined or detached.
Ah, well yes that does change things.
I'm not sure about these down votes. I understand that exiting a thread before program termination is clean and ideal, but there are times (and this is one) where the thread code can be constructed such that exiting the program while it is still running is benign. I think this is therefore a valid answer to the specific question of what to do when blocking on input like this. There are no standard input functions that do not bock.
yep exactly, i just meant it as simple workaround since input is blocking.
There is nothing horrible. or remotely wrong really, with exiting a program while additional detached threads are running. That's what detached threads are for.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.