Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does ncurses fail when I reload a dynamic library?

I've been following along with this blog post on "Interactive Programming in C". The general ideal behind it is that it displays an implementation of Conway's Game of Life in ncurses, but the game logic is loaded as a dynamic library. The main() loop monitors the .so for the game and reloads it interactively if it finds a newer version.

I've tried this in LinuxMint 17, but ncurses hangs when the shared library is dynamically reloaded. I've put in some debug fprintfs and followed the program execution in GDB, and the program seems to reload the library correctly and continue the main loop as usual.

It seems that ncurses is the problem here, but I've no idea how to debug it. The ncurses function refresh() in game.c:draw() starts returning -1 once the dynamic library is reloaded. Can anyone help? The code can be found in the github repo linked in the blog post.

Update

It looks like the variable stdscr in the ncurses library just disappears after the call to dlclose(). This doesn't happen on my RedHat machine at work.

like image 608
Marty Avatar asked Oct 19 '22 19:10

Marty


1 Answers

ncurses is a dependency of your libgame.so, not of the main executable. So if you unload the dynamic library and load the changed version, you also unload ncurses and then load it again. But you then fail to reinitialize it. That is why your program fails.

The proper solution is to call endwin() in game_unload to cleanup ncurses's state and then reinitialize it in game_reload:

static void game_reload(struct game_state *state)
{
    initscr();
    raw();
    timeout(0);
    noecho();
    curs_set(0);
    keypad(stdscr, TRUE);
}

static void game_unload(struct game_state *state)
{
    endwin();
}

Another solution would be to force the linker to link ncurses against the main executable. This will prevent the dynamic linker from unloading it when you unload the game library. You can do that by adding the -Wl,--no-as-needed flag before the $(LDLIBS) variable when you compile the main executable:

main : main.c game.h
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -Wl,--no-as-needed $(LDLIBS)

If you prefer this solution, consider to also move the ncurses initialization/cleanup to the main.c file. There is no technical reason for this, it's merely a matter of clean coding style.

like image 91
Phillip Avatar answered Oct 22 '22 23:10

Phillip