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;
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();
}
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);
//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;
//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);
//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,