10

In a graphical application I execute debug commands using the console input. When the console is created a new thread is also created to gather the user commands that handles all that input, the graphical application continues running parallel. I use boost::thread library.

It works good so far, however I haven't found a nice solution to stop the execution of this thread. The thread is always waiting for a user input:

 while(appRunning)
 {
     std::cin>>theUserCommand;
     // ...do stuff
 }

Then when the graphical application ends, it will stop all console functions, in which I include the thread:

 appRunning = false;
 // do some more related clean up
 myListeningThread->join();

As you can see the std::cin will be waiting for user input, after the join has being called. One of the solutions I tried is to create events "synthetizing keystrokes", the std::cin will get whatever value you send with an ENTER, the thread will end nicely, this solutions is horrible and I don't want to keep it. Besides, it did work in one of the environments the tool is executed, but fails when I tried using it along with a UI API. Could you guys guide me how can I fix this in a correct way? Can't really say for sure if in the C++ documentation there is a function to stop std::cin waiting for user input, and just and continue the program execution, is it even possible?

EDIT: Fine I find that keybd_event is a bit misleading for some environments, explicitly specifying the input handler with the WriteConsoleInput works good.

notNullGothik
  • 432
  • 5
  • 20
  • There are several possibilities, but all of them are OS-dependent. What operating system is this program for? – zwol Dec 19 '11 at 23:42
  • Win/VStudio9.0, I used the keybd_event for the solution I mentioned, there is no problem if it will be OS-dependent. how would you recommend me to fix it? – notNullGothik Dec 20 '11 at 00:13
  • BTW, SetConsoleCtrlHandler doesn't really work for me, this will end the application process skipping all the clean up for the application to end. – notNullGothik Dec 20 '11 at 00:21
  • @curiousguy: that's definitely a UNIX-only solution and will never work on Windows. There is not `close(int);` function and `0` is not the standard input handle. – André Caron Dec 20 '11 at 20:41
  • 1
    There is `close(int)`, it's just called `_close` for no good reason whatsoever (yes, I've had to port POSIX-API code to Windows, why do you ask?) But I wouldn't expect that to do anything constructive. The equivalent in terms of actual system calls would be `CloseHandle(GetStdHandle(STD_INPUT_HANDLE)))` and ... I have no idea what that would do to a different thread reading from the same handle. Try it and tell us! – zwol Dec 21 '11 at 04:50
  • 2
    Hmm, if you can require Vista or later, [CancelSynchronousIo](http://msdn.microsoft.com/en-us/library/windows/desktop/aa363794%28v=vs.85%29.aspx) might be very helpful here. – zwol Dec 21 '11 at 04:52

1 Answers1

1

I am not much of a Windows programmer, I know a whole lot more about Unix. And I am totally unfamiliar with boost::thread. That said, based on the advice at the bottom of this MSDN page, here is my recommendation:

  • Create an event object before you create the console-reading thread.
  • When you want to shut down, call SetEvent on the event object immediately before calling the thread's ->join method.
  • Change the main loop in your console-reading thread to block in WaitForMultipleObjects rather than istream::operator>>, something like this:

    for (;;) {
        HANDLE h[2];
        h[0] = GetStdHandle(STD_INPUT_HANDLE);
        h[1] = that_event_object_I_mentioned;
        DWORD which = WaitForMultipleObjects(2, h, FALSE, INFINITE);
    
        if (which == WAIT_OBJECT_0)
            processConsoleCommand();
        else if (which == WAIT_OBJECT_0 + 1)
            break;
        else
            abort();
    }
    
  • This thread must take care not to do any blocking operation other than the WaitForMultipleObjects call. Per the discussion in the comments below, that means processConsoleCommand can't use cin at all. You will need to use the low-level console input functions instead, notably GetNumberOfConsoleInputEvents and ReadConsoleInput, to ensure that you do not block; you will need to accumulate characters across many calls to processConsoleCommand until you read a carriage return; and you will also need to do your own echoing.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • Note that blocking on the standard input using `WaitForMultipleObjects()` to avoid a blocking read is fundamentally unreliable. If the standard input is the keyboard (`CONIN$` device), then pressing any key (even those, such as `CTRL`, that produce no input) will unblock the wait only to block on the next input operation indefinitely, resulting in the thread ignoring the event's state. Check out a [related question](http://stackoverflow.com/questions/8347642/checking-win32-file-streams-for-available-input) for more details. – André Caron Dec 20 '11 at 20:38
  • It doesn't really matter if this thread winds up spinning through `WaitForMultipleObjects` when the user is typing, as long as it goes properly idle when the user *isn't* typing. I would like to think that there's some equivalent of [`FIONREAD`](http://www.daemon-systems.org/man/ioctl.2.html) that can be used to ensure that `ReadFile` will not block, but Windows has surprised me with the absence of similar facilities before. If something like this can't be made to work, I think I'd be looking at implementing my own console window with the GUI APIs. – zwol Dec 21 '11 at 00:02
  • ... The discussion in that related question doesn't really help; the recommendation seems to be "use a dedicated thread to read from the console" and here we are discussing how to implement precisely such a thread. – zwol Dec 21 '11 at 00:03
  • That's the thing. I had a problem with exactly this code where pressing `CTRL+TAB` or `ALT+TAB` would unblock from the wait and then the next operation on `std::cin` would block because there was no real input in the console's buffer. And you are not implementing a dedicated loop for the standard input since you are waiting on both the standard input *and* and event. – André Caron Dec 21 '11 at 04:08
  • It looks to me like that problem can be worked around by using the low-level Win32 console input functions instead of `cin`. More work, yeah. And how _else_ are you to make your dedicated console-reading thread exit, short of `TerminateThread`, which is a bad idea for obvious reasons? That is, after all, the question that was originally asked. – zwol Dec 21 '11 at 04:46
  • I'm not aware of all the capabilities of the console functions, but they won't work as expected if the standard input is redirected to a file or a pipe. I'm afraid there is no way to implement a general solution to the problem OP asked, at least not on Windows. The way I "solved" it for my related question is to have a blocking call on the standard input read operation in the main thread and have the I/O on my other stream (a socket) in a background thread. The two threads join at the end of main and it works. But that was clean for my problem since I always want to exhaust both streams. – André Caron Dec 21 '11 at 04:56
  • This looks an possible solution, let me try this I will let you know how it behaves.. – notNullGothik Dec 22 '11 at 03:00