From 29f0a674a85bfb92443c56f070956a7dd60bb5f7 Mon Sep 17 00:00:00 2001 From: Jim Warner Date: Sat, 18 Sep 2021 00:00:00 -0500 Subject: [PATCH] top: introduce background updates via separate threads After the stage had been set in the previous patch, in this patch we will actually implement those background updates via 3 separate threads. The design was simple: . the do-while loops have now been made truly infinite . 2 semaphores per thread allow needed synchronization . 1 semaphore will provide for each thread to sem_wait . 1 semaphore will provide for display o/p to sem_wait . and all 3 thread's program name was made descriptive A complication was the potential for a signal directed to one of our new threads. Rather than having a thread try to deal with such signals, we pass a mask with all signals blocked at pthread_create time. Thereafter any subsequent signals are forwarded to the parent thread. [ also sigprocmask was exchanged for pthread_sigmask ] [ since warned about use "in multithreaded process". ] [ plus we also modified each of those POSIX comments ] [ about 2004 to agree with current signal-safety(7). ] Sadly, after all this effort there were no performance benefits to having separate threads. In fact there was a measurable performance degradation when running with ever smaller delay intervals. But even with a delay of 1/10 second the 'real' cost increase is only about 1%. There is one way whereby any additional costs might be eliminated (at least seemingly). One could introduce 2 separate sets of contexts for each of those 3 threads. Then retrieval & display could be overlapped. However, the resulting display wouldn't represent the real-time results. Rather it would be stale by 1 delay interval. Signed-off-by: Jim Warner --- top/top.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++---- top/top.h | 5 +- top/top_nls.c | 2 + top/top_nls.h | 1 + 4 files changed, 131 insertions(+), 10 deletions(-) diff --git a/top/top.c b/top/top.c index fabf3d74..18c660f0 100644 --- a/top/top.c +++ b/top/top.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -269,6 +271,21 @@ enum Rel_memitems { swp_TOT, swp_FRE, swp_USE }; // mem stack results extractor macro, where e=rel enum #define MEM_VAL(e) MEMINFO_VAL(e, ul_int, Mem_stack, Mem_ctx) + + /* Support for concurrent library updates via + multithreaded background processes */ +#ifndef THREADNO_CPU +static pthread_t Thread_id_cpus; +static sem_t Semaphore_cpus_beg, Semaphore_cpus_end; +#endif +#ifndef THREADNO_MEM +static pthread_t Thread_id_memory; +static sem_t Semaphore_memory_beg, Semaphore_memory_end; +#endif +#ifndef THREADNO_TSK +static pthread_t Thread_id_tasks; +static sem_t Semaphore_tasks_beg, Semaphore_tasks_end; +#endif /*###### Tiny useful routine(s) ########################################*/ @@ -348,9 +365,9 @@ static void bye_bye (const char *str) __attribute__((__noreturn__)); static void bye_bye (const char *str) { sigset_t ss; -// POSIX.1-2004 async-signal-safe: sigfillset, sigprocmask +// POSIX.1 async-signal-safe: sigfillset, pthread_sigmask sigfillset(&ss); - sigprocmask(SIG_BLOCK, &ss, NULL); + pthread_sigmask(SIG_BLOCK, &ss, NULL); at_eoj(); // restore tty in preparation for exit #ifdef ATEOJ_RPTSTD { @@ -419,6 +436,24 @@ static void bye_bye (const char *str) { // there's lots of signal-unsafe stuff in the following ... if (Frames_signal != BREAK_sig) { +#ifndef THREADNO_CPU + pthread_cancel(Thread_id_cpus); + pthread_join(Thread_id_cpus, NULL); + sem_destroy(&Semaphore_cpus_beg); + sem_destroy(&Semaphore_cpus_end); +#endif +#ifndef THREADNO_MEM + pthread_cancel(Thread_id_memory); + pthread_join(Thread_id_memory, NULL); + sem_destroy(&Semaphore_memory_beg); + sem_destroy(&Semaphore_memory_end); +#endif +#ifndef THREADNO_TSK + pthread_cancel(Thread_id_tasks); + pthread_join(Thread_id_tasks, NULL); + sem_destroy(&Semaphore_tasks_end); + sem_destroy(&Semaphore_tasks_beg); +#endif procps_pids_unref(&Pids_ctx); procps_stat_unref(&Stat_ctx); procps_meminfo_unref(&Mem_ctx); @@ -462,16 +497,16 @@ static void sig_abexit (int sig) __attribute__((__noreturn__)); static void sig_abexit (int sig) { sigset_t ss; -// POSIX.1-2004 async-signal-safe: sigfillset, sigprocmask, signal, sigemptyset, sigaddset, raise +// POSIX.1 async-signal-safe: sigfillset, signal, sigemptyset, sigaddset, pthread_sigmask, raise sigfillset(&ss); - sigprocmask(SIG_BLOCK, &ss, NULL); + pthread_sigmask(SIG_BLOCK, &ss, NULL); at_eoj(); // restore tty in preparation for exit fprintf(stderr, N_fmt(EXIT_signals_fmt) , sig, signal_number_to_name(sig), Myname); signal(sig, SIG_DFL); // allow core dumps, if applicable sigemptyset(&ss); sigaddset(&ss, sig); - sigprocmask(SIG_UNBLOCK, &ss, NULL); + pthread_sigmask(SIG_UNBLOCK, &ss, NULL); raise(sig); // ( plus set proper return code ) _exit(EXIT_FAILURE); // if default sig action is ignore } // end: sig_abexit @@ -493,7 +528,7 @@ static void sig_endpgm (int dont_care_sig) { * Catches: * SIGTSTP, SIGTTIN and SIGTTOU */ static void sig_paused (int dont_care_sig) { -// POSIX.1-2004 async-signal-safe: tcsetattr, tcdrain, raise +// POSIX.1 async-signal-safe: tcsetattr, tcdrain, raise if (-1 == tcsetattr(STDIN_FILENO, TCSAFLUSH, &Tty_original)) error_exit(fmtmk(N_fmt(FAIL_tty_set_fmt), strerror(errno))); if (keypad_local) putp(keypad_local); @@ -524,7 +559,7 @@ static void sig_paused (int dont_care_sig) { * Catches: * SIGCONT and SIGWINCH */ static void sig_resize (int dont_care_sig) { -// POSIX.1-2004 async-signal-safe: tcdrain +// POSIX.1 async-signal-safe: tcdrain tcdrain(STDOUT_FILENO); Frames_signal = BREAK_sig; (void)dont_care_sig; @@ -2350,7 +2385,7 @@ static void zap_fieldstab (void) { #undef maX } // end: zap_fieldstab -/*###### Library Interface #############################################*/ +/*###### Library Interface (as separate threads) #######################*/ /* * This guy's responsible for interfacing with the library API @@ -2360,6 +2395,9 @@ static void *cpus_refresh (void *unused) { enum stat_reap_type which; do { +#ifndef THREADNO_CPU + sem_wait(&Semaphore_cpus_beg); +#endif which = STAT_REAP_CPUS_ONLY; if (CHKw(Curwin, View_CPUNOD)) which = STAT_REAP_NUMA_NODES_TOO; @@ -2380,7 +2418,12 @@ static void *cpus_refresh (void *unused) { Cpu_cnt = 48; #endif } +#ifndef THREADNO_CPU + sem_post(&Semaphore_cpus_end); + } while (1); +#else } while (0); +#endif return NULL; (void)unused; } // end: cpus_refresh @@ -2394,6 +2437,9 @@ static void *memory_refresh (void *unused) { time_t cur_secs; do { +#ifndef THREADNO_MEM + sem_wait(&Semaphore_memory_beg); +#endif if (Frames_signal) sav_secs = 0; cur_secs = time(NULL); @@ -2403,7 +2449,12 @@ static void *memory_refresh (void *unused) { error_exit(fmtmk(N_fmt(LIB_errormem_fmt),__LINE__, strerror(errno))); sav_secs = cur_secs; } +#ifndef THREADNO_MEM + sem_post(&Semaphore_memory_end); + } while (1); +#else } while (0); +#endif return NULL; (void)unused; } // end: memory_refresh @@ -2423,6 +2474,9 @@ static void *tasks_refresh (void *unused) { int i, what; do { +#ifndef THREADNO_TSK + sem_wait(&Semaphore_tasks_beg); +#endif procps_uptime(&uptime_cur, NULL); et = uptime_cur - uptime_sav; if (et < 0.01) et = 0.005; @@ -2451,7 +2505,12 @@ static void *tasks_refresh (void *unused) { for (i = 0; i < GROUPSMAX; i++) memcpy(Winstk[i].ppt, Pids_reap->stacks, sizeof(void *) * PIDSmaxt); } +#ifndef THREADNO_TSK + sem_post(&Semaphore_tasks_end); + } while (1); +#else } while (0); +#endif return NULL; (void)unused; #undef nALIGN @@ -3310,6 +3369,36 @@ static void before (char *me) { if ((rc = procps_pids_new(&Pids_ctx, Pids_itms, Pids_itms_tot))) error_exit(fmtmk(N_fmt(LIB_errorpid_fmt),__LINE__, strerror(-rc))); + /* in case any of our threads have neen enabled, they'll inherit this mask + with everything blocked. therefore, signals go to the main thread (us). */ + sigfillset(&sa.sa_mask); + pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL); + +#ifndef THREADNO_CPU + if (0 != sem_init(&Semaphore_cpus_beg, 0, 0) + || (0 != sem_init(&Semaphore_cpus_end, 0, 0))) + error_exit(fmtmk(N_fmt(X_SEMAPHORES_fmt),__LINE__, strerror(errno))); + if (0 != pthread_create(&Thread_id_cpus, NULL, cpus_refresh, NULL)) + error_exit(fmtmk(N_fmt(X_THREADINGS_fmt),__LINE__, strerror(errno))); + pthread_setname_np(Thread_id_cpus, "update cpus"); +#endif +#ifndef THREADNO_MEM + if (0 != sem_init(&Semaphore_memory_beg, 0, 0) + || (0 != sem_init(&Semaphore_memory_end, 0, 0))) + error_exit(fmtmk(N_fmt(X_SEMAPHORES_fmt),__LINE__, strerror(errno))); + if (0 != pthread_create(&Thread_id_memory, NULL, memory_refresh, NULL)) + error_exit(fmtmk(N_fmt(X_THREADINGS_fmt),__LINE__, strerror(errno))); + pthread_setname_np(Thread_id_memory, "update memory"); +#endif +#ifndef THREADNO_TSK + if (0 != sem_init(&Semaphore_tasks_beg, 0, 0) + || (0 != sem_init(&Semaphore_tasks_end, 0, 0))) + error_exit(fmtmk(N_fmt(X_SEMAPHORES_fmt),__LINE__, strerror(errno))); + if (0 != pthread_create(&Thread_id_tasks, NULL, tasks_refresh, NULL)) + error_exit(fmtmk(N_fmt(X_THREADINGS_fmt),__LINE__, strerror(errno))); + pthread_setname_np(Thread_id_tasks, "update tasks"); +#endif + #ifndef SIGRTMAX // not available on hurd, maybe others too #define SIGRTMAX 32 #endif @@ -5623,6 +5712,12 @@ static void summary_show (void) { Msg_row += 1; } // end: View_LOADAV +#ifdef THREADED_CPU + sem_wait(&Semaphore_cpus_end); +#endif +#ifdef THREADED_TSK + sem_wait(&Semaphore_tasks_end); +#endif // Display Task and Cpu(s) States if (isROOM(View_STATES, 2)) { show_special(0, fmtmk(N_unq(STATE_line_1_fmt) @@ -5711,6 +5806,9 @@ numa_oops: } } // end: View_STATES +#ifdef THREADED_MEM + sem_wait(&Semaphore_memory_end); +#endif // Display Memory and Swap stats if (isROOM(View_MEMORY, 2)) { #define bfT(n) buftab[n].buf @@ -6268,15 +6366,32 @@ static void frame_make (void) { // whoa either first time or thread/task mode change, (re)prime the pump... if (Pseudo_row == PROC_XTRA) { +#ifndef THREADNO_TSK + sem_post(&Semaphore_tasks_beg); + sem_wait(&Semaphore_tasks_end); +#else tasks_refresh(NULL); +#endif usleep(LIB_USLEEP); putp(Cap_clr_scr); } else putp(Batch ? "\n\n" : Cap_home); +#ifndef THREADNO_TSK + sem_post(&Semaphore_tasks_beg); +#else + tasks_refresh(NULL); +#endif +#ifndef THREADNO_CPU + sem_post(&Semaphore_cpus_beg); +#else cpus_refresh(NULL); +#endif +#ifndef THREADNO_MEM + sem_post(&Semaphore_memory_beg); +#else memory_refresh(NULL); - tasks_refresh(NULL); +#endif Tree_idx = Pseudo_row = Msg_row = scrlins = 0; summary_show(); diff --git a/top/top.h b/top/top.h index fcd839ba..23d0aadd 100644 --- a/top/top.h +++ b/top/top.h @@ -50,6 +50,9 @@ //#define SCROLLV_BY_1 /* when scrolling left/right do not move 8 */ //#define STRINGCASENO /* case insenstive compare/locate versions */ //#define TERMIOS_ONLY /* use native input only (just limp along) */ +//#define THREADNO_CPU /* suppress background thread for cpu updt */ +//#define THREADNO_MEM /* suppress background thread for mem updt */ +//#define THREADNO_TSK /* suppress background thread for tsk updt */ //#define TOG4_NOFORCE /* no force 2 abreast mode with '4' toggle */ //#define TOG4_NOTRUNC /* ensure no truncation for 2 abreast mode */ //#define TOG4_OFF_MEM /* don't show two abreast memory statistic */ @@ -627,7 +630,7 @@ typedef struct WIN_t { //atic void fields_utility (void); //atic inline void widths_resize (void); //atic void zap_fieldstab (void); -/*------ Library Interface ---------------------------------------------*/ +/*------ Library Interface (as separate threads) -----------------------*/ //atic void *cpus_refresh (void *unused); //atic void *memory_refresh (void *unused); //atic void *tasks_refresh (void *unused); diff --git a/top/top_nls.c b/top/top_nls.c index 259eec27..c2db9b93 100644 --- a/top/top_nls.c +++ b/top/top_nls.c @@ -569,6 +569,8 @@ static void build_norm_nlstab (void) { Norm_nlstab[XTRA_size2up_txt] = _("terminal is not wide enough"); Norm_nlstab[XTRA_modebad_txt] = _("wrong mode, command inactive"); Norm_nlstab[XTRA_warnold_txt] = _("saving prevents older top from reading, save anyway?"); + Norm_nlstab[X_SEMAPHORES_fmt] = _("failed sem_init() at %d: %s"); + Norm_nlstab[X_THREADINGS_fmt] = _("failed pthread_create() at %d: %s"); } diff --git a/top/top_nls.h b/top/top_nls.h index df38c778..4995f177 100644 --- a/top/top_nls.h +++ b/top/top_nls.h @@ -84,6 +84,7 @@ enum norm_nls { WORD_process_txt, WORD_threads_txt, WRITE_rcfile_fmt, WRONG_switch_fmt, XTRA_badflds_fmt, XTRA_fixwide_fmt, XTRA_modebad_txt, XTRA_size2up_txt, XTRA_vforest_fmt, XTRA_warncfg_txt, XTRA_warnold_txt, XTRA_winsize_txt, + X_SEMAPHORES_fmt, X_THREADINGS_fmt, #ifndef INSP_OFFDEMO YINSP_demo01_txt, YINSP_demo02_txt, YINSP_demo03_txt, YINSP_deqfmt_txt, YINSP_deqtyp_txt, YINSP_dstory_txt, -- 2.40.0