Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exit application while stdin blocking on windows

I have an application, which reads data from standard input using getline() in a thread. I want to close the application from the main thread, while getline still block the other thread. How can this be achieved?

I don't want to force the users to have to press ctrl-Z to close stdin and the application.

I have tried so far with my complier settings (RuntimeLibrary=/MT) on Windows 8.1 64bit, v120 platform toolset:

  • freopen stdin, but it is blocked by internal lock
  • destructing the thread, which calls abort()
  • putback an Eof, line end to std::cin, which also blocked

* Update *

  • detach() does not work, exit() blocked by a lock
  • winapi TerminatThread() calls abort()
  • winapi CloseHandle(GetStdHandle(STD_INPUT_HANDLE)) hangs
  • calling TerminateProcess() - works, but I would like to exit gracefully

* Update 2: Solution *

  • WriteConsoleInput() can make std::getline() return from blocking read. This works with any msvc runtime libray. For code of working solution see accepted answer.

Example code showing the problem:

#include <iostream>
#include <thread>
#include <string>
#include <chrono>

int main(int argc, char *argv[])
{
    bool stop = false;
    std::thread *t = new std::thread([&]{
        std::string line;
        while (!stop && std::getline(std::cin, line, '\n')) {
            std::cout << line;
        }
    });

    std::this_thread::sleep_for(std::chrono::seconds(1));

    stop = true;
    // how to stop thread or make getline to return here?

    return 0;
}
like image 865
simon Avatar asked Jul 27 '15 15:07

simon


1 Answers

writeConsoleInput() can make std::getline return from blocking read, so it can solve the problem even when /MT compiler option used.

#include <Windows.h>

#include <iostream>
#include <thread>
#include <string>
#include <chrono>
#include <atomic>

int main(int argc, char *argv[])
{
    std::atomic_bool stop;

    stop = false;

    std::thread t([&]{
        std::string line;
        while (!stop.load() && std::getline(std::cin, line, '\n')) {
            std::cout << line;
        }
    });


    std::this_thread::sleep_for(std::chrono::seconds(1));

    stop = true;

    DWORD dwTmp;
    INPUT_RECORD ir[2];
    ir[0].EventType = KEY_EVENT;
    ir[0].Event.KeyEvent.bKeyDown = TRUE;
    ir[0].Event.KeyEvent.dwControlKeyState = 0;
    ir[0].Event.KeyEvent.uChar.UnicodeChar = VK_RETURN;
    ir[0].Event.KeyEvent.wRepeatCount = 1;
    ir[0].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
    ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC);
    ir[1] = ir[0];
    ir[1].Event.KeyEvent.bKeyDown = FALSE;
    WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), ir, 2, &dwTmp);

    t.join();

    return 0;
}
like image 88
simon Avatar answered Oct 21 '22 23:10

simon