Absolute sleep
authorTomas Mudrunka <tomas@mudrunka.cz>
Fri, 2 Jul 2021 10:51:37 +0000 (12:51 +0200)
committerTomas Mudrunka <tomas@mudrunka.cz>
Fri, 2 Jul 2021 10:51:37 +0000 (12:51 +0200)
c/pthread_pause.c

index e5f46b1381e4788ea7b90f7d8dd23f3b70385628..6e46ea525f88cacb7ea12ec4e4694be9754040d2 100644 (file)
 
 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,
This page took 0.136343 seconds and 4 git commands to generate.