1

Consider a simple program: (require -std=c++11 -lpthread -lreadline -lSDL2 when compile with g++ with pthread)

#include <iostream>
#include <thread>
#include <SDL2/SDL.h>
#include <cstdlib>
extern "C" {
#include <readline/readline.h>
}

int main(int argc, char *argv[])
{
    SDL_Init(SDL_INIT_VIDEO);
    auto window = SDL_CreateWindow("title", 0, 0, 200, 200, SDL_WINDOW_SHOWN);

    std::thread console([](){
        while (true) {
            char *console_input_c_str = readline("> ");
            if (console_input_c_str == NULL)
                break;
            std::cout << "line: " << console_input_c_str << '\n';
            std::free(console_input_c_str);
        }
    });

    while (true) {
        SDL_Event event;
        SDL_WaitEvent(&event);
        std::cerr << "received event type "<<event.type<<'\n';
        if(event.type == SDL_WINDOWEVENT &&
                event.window.event == SDL_WINDOWEVENT_CLOSE)
            break;
    }

    SDL_DestroyWindow(window);
    SDL_Quit();
    console.join();
}

The main thread creates a window window with SDL2, enter a SDL event loop, while the thread console repeatedly read from the console using readline. When the window is exited, it waits for the console thread to finish then exit.

The program works fine; however, how can I make the console thread to stop when the window is exited?

It's fine if the program only use a single thread. It's also fine if exiting the console readline quits the program.


Exit readline is easy, but there is a problem with all the answers there - pthread_kill or reset -Q is not portable.

Terminating a thread is easy (using std::terminate or .detach()), but the console is left in a bad state because readline don't terminate normally. To reset the console it's possible to use reset -Q, but that's not portable.

Using readline's alternative (async) interface doesn't work because SDL don't listen for events on the console. Using select is also not portable.

user202729
  • 3,358
  • 3
  • 25
  • 36
  • In short: It's not possible to do in a totally platform agnostic and portable way. Use [`SDL_PollEvent`](https://wiki.libsdl.org/SDL_PollEvent) and [the alternate interface](https://tiswww.case.edu/php/chet/readline/readline.html#SEC41) together with conditional compilation to use either `select` or some Windows-specific polling. – Some programmer dude Jan 08 '19 at 06:54
  • Replace while(true) in it with while(no_need_to_end_this_thread)? – Öö Tiib Jan 08 '19 at 09:04
  • 1
    @ÖöTiib The major problem is that the call to `readline()` is *blocking*. – Some programmer dude Jan 08 '19 at 09:08
  • @Someprogrammerdude then he can use platform-specific non-blocking console functions (like _kbhit() or getch()), no standard C++ functions for that. – Öö Tiib Jan 08 '19 at 10:47

1 Answers1

1

Actually, readline is not completely blocking, it calls rl_event_hook every 1/10 seconds (by default). It's possible to check whether the program is stopped inside the event hook, and if so do a longjmp or throw to get out of the function (in this example int is used, although it's possible to define your own exception class):

std::atomic<bool> stopped (false);

rl_event_hook = [](){
    if (stopped)
        throw 0;
    return 0;
};

try {
    /* something calls readline() */
} catch (int) {
    rl_cleanup_after_signal();
}
return 0;

or by setting rl_done = 1: (this won't work when rl_event_hook is nullptr. Also note that rl_event_hook is not called when a key is held, it's necessary to override rl_getc_function to change the behavior. It's not recommended to modify rl_done from the other thread)

std::atomic<bool> stopped (false);

rl_event_hook = [](){
    if (stopped)
        rl_done = true;
    return 0;
};

/* something calls readline() */
if (stopped) return 0;
/* process */
return 0;

and in the main thread use:

stopped = true;
console.join();
user202729
  • 3,358
  • 3
  • 25
  • 36