0

When I use ncurses write a library management system, terminals garbled when multi-threaded,my layout has three windows.

1.My code named ncurses.c

#include <stdio.h>
#include <ncurses.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>


typedef struct _WIN_struct {
    int startx, starty;
    int height, width;
} WIN;

WIN winTitle;      /* title win */
WIN winMenu;   /* Main menu win */
WIN winNews;       /* win news */

WINDOW *create_newwin(int height, int width, int starty, int startx) {
    WINDOW *local_win;
    local_win = newwin(height, width, starty, startx);
    box(local_win, 0, 0);
    wrefresh(local_win);
    return local_win;
}

char *getTimeNow() {
    time_t rawtime;
    struct tm *timeinfo;
    time(&rawtime);
    timeinfo = localtime(&rawtime);
    return asctime(timeinfo);
}

void *threadfunc_title(void *p) {
    WINDOW *windowTitle;
    windowTitle = create_newwin(winTitle.height, winTitle.width, winTitle.starty, winTitle.startx);

    /* show title and time */
    for (;;) {
        mvwprintw(windowTitle, winTitle.height/2, winTitle.width/2 - 10, "%s", "Library Management System");
        mvwprintw(windowTitle, winTitle.height-2, winTitle.width-30, "%s", getTimeNow());
        wrefresh(windowTitle);
        sleep(1);
    }

}

void *threadfunc_menu(void *p) {
    WINDOW *windowMenu;
    windowMenu = create_newwin(winMenu.height, winMenu.width, winMenu.starty, winMenu.startx);

    for (;;) {
        /* now do nothing */
        sleep(1);
    }

}

void *threadfunc_news(void *p) {
    WINDOW *windowNews;
    windowNews = create_newwin(winNews.height, winNews.width, winNews.starty, winNews.startx);

    for (;;) {
        wprintw(windowNews, "%d. %s,%s", getTimeNow(), 1, "a borrow a book from c");
        wrefresh(windowNews);
        wclear(windowNews);
        sleep(5);
    }
}

void initWin(WIN *p_win, int height, int width, int starty, int startx) {
    p_win->height = height;
    p_win->width = width;
    p_win->starty = starty;
    p_win->startx = startx;
}


int main(int argc, char *argv[])
{
    pthread_t pidTitle;
    pthread_t pidMenu;
    pthread_t pidNews;

    initscr();
    start_color();
    cbreak();
    keypad(stdscr, TRUE);
    noecho();

    /* init location */
    initWin(&winTitle, LINES*0.2, COLS, 0 , 0);
    initWin(&winMenu, LINES*0.7, COLS*0.7, LINES*0.25, 0);
    initWin(&winNews, LINES*0.7, COLS*0.2, LINES*0.25, COLS*0.7);

    pthread_create(&pidTitle, NULL, threadfunc_title, NULL);
    pthread_create(&pidMenu, NULL, threadfunc_menu, NULL);
    pthread_create(&pidNews, NULL, threadfunc_news, NULL);

    pthread_join(pidTitle, NULL);
    pthread_join(pidMenu, NULL);
    pthread_join(pidNews, NULL);

    endwin();
    return 0;
}

2.Three threads for three windows, thread_func_xxx() is pthread_create() third parameter.

3.Compile and Run

$ gcc ncurses.c -lncurses -lpthread -o ncurses
$ ./ncurses

then, I get terminal garbled.

ps:
1.I have changed -lncurses to -lncursesw, but no effect.
2.if you don not have installed ncurses, you can follow this.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Yang Bodong
  • 65
  • 10
  • 2
    I'm pretty sure the `ncurses` library is not thread safe. You may need one thread to do the display work at the request of the other threads. That could be modestly hard to do. An alternative is that you arrange for a mutex to control access to the `ncurses` functions, and ensure that the threads gain control of the mutex before making any series of changes to the display. How you do that is open to discussion; you might find it convenient (less inconvenient) to write a series of wrapper functions that automatically lock the mutex before doing the real work and unlock it when finished. – Jonathan Leffler Dec 19 '17 at 01:48
  • 1
    @JonathanLeffler is correct. Use one thread for the UI. (This is not true just for NCurses — it is pretty much true for every GTK you will ever encounter.) – Dúthomhas Dec 19 '17 at 01:57
  • @JonathanLeffler Thanks, I will use mutex to control different threads to display for thread safe. Refer to [this](https://stackoverflow.com/questions/29910562/why-has-no-one-written-a-threadsafe-branch-of-the-ncurses-library) question, I can also use this support multi-threaded version, right? – Yang Bodong Dec 19 '17 at 02:15
  • I wasn’t aware of that library/variant. If you can find it on your machine, you can use it. If you need it on customers’ machines, you need to be cautious. You may find you need to build it for yourself. (Thanks for the link.) – Jonathan Leffler Dec 19 '17 at 02:20
  • @JonathanLeffler I finish it, use mutex to control threads to display. But I meet another question is that when menu_window in `scanf()` , other window could not get mutex, so can't to diaplay. Do you have any suggestions in C code? Thanks. – Yang Bodong Dec 19 '17 at 04:50
  • You don't use `scanf()` to read in an `ncurses` application. You use `wscanw()` and friends. – Jonathan Leffler Dec 19 '17 at 04:57
  • @JonathanLeffler yes, I used `wscanw()`, but the question is `wscanw()` also block the thread, and other threads will not running. so, maybe I need timer. – Yang Bodong Dec 19 '17 at 06:05
  • No idea. I've not used threaded curses libraries, so I have nothing on which to base any guesses. Read the docs very carefully; the issue may be discussed there. If not, I'm not sure, but it would perhaps be a question that you could ask. I'm not sure how many people could answer it — but you only need to find one person who can give you a good answer. – Jonathan Leffler Dec 19 '17 at 06:08

1 Answers1

1

short: the example won't work because the lower-level functions used to update the screen share data which isn't mutex'd. If you compile ncurses with the pthread option, that improves the re-entrancy of the higher-level functions and adds some mutexes, it's possible to use multiple threads.

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105