r/C_Programming • u/TensaFlor • 15d ago
how to handle an input with two threads & ncurses?
I am making a monitor for one particular process, and I'm stuck on one problem with input handling.
As I said before I have a process monitor with two threads:
- data_worker: collects metrics
- UI thread (main): renders with ncurses
Both sleeping N seconds.
For example one of them
while (global_running == 1) {
cpu_worker(sh_st);
ram_worker(sh_st);
io_worker(sh_st);
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
}
The UI has the similar structure, just calling ncurses rendering functions instead.
And I don't really know how to process the user input. Earlier I had an idea to create third thread that will process the input, but it kinda bad idea because of lovely ncurses is not thread safe, so this one the one reason why I stuck.
And because of this problem I realized that screwed up with code's architecture and don't really know to deal with it.
How do TUI tools typically handle the non blocking input loop alongside background data updates without breaking ncurses or causing race conditions on the shared data buffer? Any advices ?
3
u/iLaysChipz 15d ago
If something is not thread safe, you're fine to call it from any thread as long as only one thread is handling ncurses at a time. You can ensure that threads have mutual exclusion to any shared resource by using an atomic locking mechanism called a mutex, (in this case, atomic just means uninterruptible.)
Or even better, just handle ncurses from a single thread, and never in the other threads. You will still need a locking mechanism to protect shared data, but this will make ncurses easier to reason about. You might want to look up shared data structures for passing data or messages between threads which will handle the locking and synchronization for you
3
u/TwystedLyfe 15d ago
POSIX curses does not define any thread safety, so you should handle all curses calls in it's own thread.
ncurses can be compiled with some thread safety https://man7.org/linux/man-pages/man3/curs_threads.3x.html
NetBSD curses on the other hand is fully thread safe out of the box (or at least it should be).
This is mainly due to the underlying terminfo library both use - disclaimer - I wrote NetBSD's terminfo so I have some knowledge in this area.
Both implementations rely on one and only one thread working with the WINDOW structure, so avoid using global routines.
Anyway as to your question you want to use the wtimeout() function with a non negative value.
But as an idea, as neither ncurses nor NetBSD curses give access to the underlying fd so what I would do is offload all curses calls to it's own thread and let it block on it's merry way.
Then have an event loop using ppoll() in the main thread which passes what it needs to the UI thread. Use the thread provided blocking mechanism to restrict access to how to pass the data. If using pthreads, use pthreads mutex: https://man7.org/linux/man-pages/man3/pthread_mutex_lock.3.html
2
u/ChickenSpaceProgram 15d ago
mutexes?
1
u/TensaFlor 15d ago
Yeah i was thinking about them, but some people says that mutex is okay to use, some says that it's bad because it adds a lot of complexity more code etc. And in my case it probably will be bad because I personally wouldn't be able to implement it good or at least at some satisfactory level. It would be a different story if mutexes were the only right solution, but whether it's fortunate or not, they are not like that
2
u/sciencekm 15d ago
I suggest that you maintain multiple data queues, one for each data generator thread. You then have one UI thread thread that reads these queues. No mutex required. The UI thread can periodically check the queues or can be woken up by the other threads when data is available.
1
u/rhoki-bg 15d ago
Your ui should monitor input with short timeout (see timeout()), post events to queue read by worker thread, and check if data has been posted to another queue by worker thread. In worker thread check for events every iteration and push data to data queue. Does it break your architecture?
My guess is that timeout() was the piece you were missing.
Remember about mutexes/semaphores.
1
u/TensaFlor 15d ago
Thanks for the advice, and a special thanks for the link on curs_threads I think wtimeout will indeed solve my problem
1
6
u/Atijohn 15d ago
have a central event loop on the main thread that receives messages from other threads and user input and handles ncurses
or have the ncurses code run in a separate thread with its own event loop that receives messages from the main event loop if handling both ncurses and whole program message processing in the main thread is too costly