From 3b8f487aed778edba3d8cffe5bdad8e56470c84a Mon Sep 17 00:00:00 2001 From: Tomas Mudrunka Date: Fri, 2 Jul 2021 12:51:37 +0200 Subject: [PATCH] Absolute sleep --- c/pthread_pause.c | 56 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/c/pthread_pause.c b/c/pthread_pause.c index e5f46b1..6e46ea5 100644 --- a/c/pthread_pause.c +++ b/c/pthread_pause.c @@ -25,16 +25,49 @@ pthread_t main_thread; sem_t pthread_pause_sem; +pthread_once_t pthread_pause_once_ctrl = PTHREAD_ONCE_INIT; + +void pthread_pause_once(void) { + sem_init(&pthread_pause_sem, 0, 1); +} + +#define pthread_pause_init() (pthread_once(&pthread_pause_once_ctrl, &pthread_pause_once)) + +#define NSEC_PER_SEC (1000*1000*1000) +// timespec_normalise() from https://github.com/solemnwarning/timespec/ +struct timespec timespec_normalise(struct timespec ts) +{ + while(ts.tv_nsec >= NSEC_PER_SEC) { + ++(ts.tv_sec); ts.tv_nsec -= NSEC_PER_SEC; + } + while(ts.tv_nsec <= -NSEC_PER_SEC) { + --(ts.tv_sec); ts.tv_nsec += NSEC_PER_SEC; + } + if(ts.tv_nsec < 0) { // Negative nanoseconds isn't valid according to POSIX. + --(ts.tv_sec); ts.tv_nsec = (NSEC_PER_SEC + ts.tv_nsec); + } + return ts; +} void pthread_nanosleep(struct timespec t) { //Sleep calls on Linux get interrupted by signals, causing premature wake //Pthread (un)pause is built using signals //Therefore we need self-restarting sleep implementation //IO timeouts are restarted by SA_RESTART, but sleeps do need explicit restart - while(nanosleep(&t, &t)) if(errno!=EINTR) break; + //We also need to sleep using absolute time, because relative time is paused + //You should use this in any thread that gets (un)paused + + struct timespec wake; + clock_gettime(CLOCK_MONOTONIC, &wake); + + t = timespec_normalise(t); + wake.tv_sec += t.tv_sec; + wake.tv_nsec += t.tv_nsec; + wake = timespec_normalise(wake); + + while(clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &wake, NULL)) if(errno!=EINTR) break; return; } - void pthread_nsleep(time_t s, long ns) { struct timespec t; t.tv_sec = s; @@ -48,11 +81,12 @@ void pthread_sleep(time_t s) { void pthread_pause_yield() { //Call this to give other threads chance to run + //Wait until last (un)pause action gets finished sem_wait(&pthread_pause_sem); sem_post(&pthread_pause_sem); //usleep(0); //nanosleep(&((const struct timespec){.tv_sec=0,.tv_nsec=1}), NULL); - pthread_nsleep(0,1); //pthread_yield() is not enough, so we use sleep + //pthread_nsleep(0,1); //pthread_yield() is not enough, so we use sleep pthread_yield(); } @@ -66,7 +100,9 @@ void pthread_pause_handler(int signal) { if(sigismember(&pending, PTHREAD_XSIG_CONT)) return; */ + //Post semaphore to confirm that signal is handled sem_post(&pthread_pause_sem); + //Suspend if needed if(signal == PTHREAD_XSIG_STOP) { sigset_t sigset; sigfillset(&sigset); @@ -81,8 +117,11 @@ void pthread_pause_enable() { //It can be limited using RLIMIT_SIGPENDING //You can get runtime SigQ stats using following command: //grep -i sig /proc/$(pgrep binary)/status - struct rlimit sigq = {.rlim_cur = 32, .rlim_max=32}; - setrlimit(RLIMIT_SIGPENDING, &sigq); + //This is no longer needed, since we use semaphores + //struct rlimit sigq = {.rlim_cur = 32, .rlim_max=32}; + //setrlimit(RLIMIT_SIGPENDING, &sigq); + + pthread_pause_init(); //Prepare sigset sigset_t sigset; @@ -112,6 +151,8 @@ void pthread_pause_disable() { //Eg.: locking mutex, calling printf() which has internal mutex, etc... //After unlocking mutex, you can enable pause again. + pthread_pause_init(); + //Make sure all signals are dispatched before we block them sem_wait(&pthread_pause_sem); @@ -157,18 +198,17 @@ void *thread_test() { //Pausing main thread should not cause deadlock //We pause main thread here just to test it is OK pthread_pause(main_thread); - pthread_nsleep(0, 1000*1000); + //pthread_nsleep(0, 1000*1000); pthread_unpause(main_thread); //Wait for a while - pthread_nsleep(0, 1000*1000*100); + //pthread_nsleep(0, 1000*1000*100); pthread_unpause(main_thread); } } int main() { pthread_t t; - sem_init(&pthread_pause_sem, 0, 1); //TODO: hide somewhere using pthred_once() main_thread = pthread_self(); pthread_pause_enable(); //Will get inherited by all threads from now on //you need to call pthread_pause_enable (or disable) before creating threads, -- 2.30.2