1 //Filename: pthread_pause.c
2 //Author: Tomas 'Harvie' Mudrunka 2021
3 //Build: CFLAGS=-lpthread make pthread_pause; ./pthread_pause
4 //Test: valgrind --tool=helgrind ./pthread_pause
6 //I've wrote this code as excercise to solve following stack overflow question:
7 // https://stackoverflow.com/questions/9397068/how-to-pause-a-pthread-any-time-i-want/68119116#68119116
9 #define _GNU_SOURCE //pthread_yield() needs this
12 //#include <pthread_extra.h>
13 #include <semaphore.h>
19 #include <sys/resource.h>
22 #define PTHREAD_XSIG_STOP (SIGRTMIN+0)
23 #define PTHREAD_XSIG_CONT (SIGRTMIN+1)
24 #define PTHREAD_XSIGRTMIN (SIGRTMIN+2) //First unused RT signal
26 pthread_t main_thread
;
27 sem_t pthread_pause_sem
;
28 pthread_once_t pthread_pause_once_ctrl
= PTHREAD_ONCE_INIT
;
30 void pthread_pause_once(void) {
31 sem_init(&pthread_pause_sem
, 0, 1);
34 #define pthread_pause_init() (pthread_once(&pthread_pause_once_ctrl, &pthread_pause_once))
36 #define NSEC_PER_SEC (1000*1000*1000)
37 // timespec_normalise() from https://github.com/solemnwarning/timespec/
38 struct timespec
timespec_normalise(struct timespec ts
)
40 while(ts
.tv_nsec
>= NSEC_PER_SEC
) {
41 ++(ts
.tv_sec
); ts
.tv_nsec
-= NSEC_PER_SEC
;
43 while(ts
.tv_nsec
<= -NSEC_PER_SEC
) {
44 --(ts
.tv_sec
); ts
.tv_nsec
+= NSEC_PER_SEC
;
46 if(ts
.tv_nsec
< 0) { // Negative nanoseconds isn't valid according to POSIX.
47 --(ts
.tv_sec
); ts
.tv_nsec
= (NSEC_PER_SEC
+ ts
.tv_nsec
);
52 void pthread_nanosleep(struct timespec t
) {
53 //Sleep calls on Linux get interrupted by signals, causing premature wake
54 //Pthread (un)pause is built using signals
55 //Therefore we need self-restarting sleep implementation
56 //IO timeouts are restarted by SA_RESTART, but sleeps do need explicit restart
57 //We also need to sleep using absolute time, because relative time is paused
58 //You should use this in any thread that gets (un)paused
61 clock_gettime(CLOCK_MONOTONIC
, &wake
);
63 t
= timespec_normalise(t
);
64 wake
.tv_sec
+= t
.tv_sec
;
65 wake
.tv_nsec
+= t
.tv_nsec
;
66 wake
= timespec_normalise(wake
);
68 while(clock_nanosleep(CLOCK_MONOTONIC
, TIMER_ABSTIME
, &wake
, NULL
)) if(errno
!=EINTR
) break;
71 void pthread_nsleep(time_t s
, long ns
) {
78 void pthread_sleep(time_t s
) {
82 void pthread_pause_yield() {
83 //Call this to give other threads chance to run
84 //Wait until last (un)pause action gets finished
85 sem_wait(&pthread_pause_sem
);
86 sem_post(&pthread_pause_sem
);
88 //nanosleep(&((const struct timespec){.tv_sec=0,.tv_nsec=1}), NULL);
89 //pthread_nsleep(0,1); //pthread_yield() is not enough, so we use sleep
93 void pthread_pause_handler(int signal
) {
94 //Do nothing when there are more signals pending (to cleanup the queue)
95 //This is no longer needed, since we use semaphore to limit pending signals
99 if(sigismember(&pending, PTHREAD_XSIG_STOP)) return;
100 if(sigismember(&pending, PTHREAD_XSIG_CONT)) return;
103 //Post semaphore to confirm that signal is handled
104 sem_post(&pthread_pause_sem
);
106 if(signal
== PTHREAD_XSIG_STOP
) {
109 sigdelset(&sigset
, PTHREAD_XSIG_STOP
);
110 sigdelset(&sigset
, PTHREAD_XSIG_CONT
);
111 sigsuspend(&sigset
); //Wait for next signal
115 void pthread_pause_enable() {
116 //Having signal queue too deep might not be necessary
117 //It can be limited using RLIMIT_SIGPENDING
118 //You can get runtime SigQ stats using following command:
119 //grep -i sig /proc/$(pgrep binary)/status
120 //This is no longer needed, since we use semaphores
121 //struct rlimit sigq = {.rlim_cur = 32, .rlim_max=32};
122 //setrlimit(RLIMIT_SIGPENDING, &sigq);
124 pthread_pause_init();
128 sigemptyset(&sigset
);
129 sigaddset(&sigset
, PTHREAD_XSIG_STOP
);
130 sigaddset(&sigset
, PTHREAD_XSIG_CONT
);
132 //Register signal handlers
133 //signal(PTHREAD_XSIG_STOP, pthread_pause_handler);
134 //signal(PTHREAD_XSIG_CONT, pthread_pause_handler);
135 //We now use sigaction() instead of signal(), because it supports SA_RESTART
136 const struct sigaction pause_sa
= {
137 .sa_handler
= pthread_pause_handler
,
139 .sa_flags
= SA_RESTART
,
142 sigaction(PTHREAD_XSIG_STOP
, &pause_sa
, NULL
);
143 sigaction(PTHREAD_XSIG_CONT
, &pause_sa
, NULL
);
146 pthread_sigmask(SIG_UNBLOCK
, &sigset
, NULL
);
149 void pthread_pause_disable() {
150 //This is important for when you want to do some signal unsafe stuff
151 //Eg.: locking mutex, calling printf() which has internal mutex, etc...
152 //After unlocking mutex, you can enable pause again.
154 pthread_pause_init();
156 //Make sure all signals are dispatched before we block them
157 sem_wait(&pthread_pause_sem
);
161 sigemptyset(&sigset
);
162 sigaddset(&sigset
, PTHREAD_XSIG_STOP
);
163 sigaddset(&sigset
, PTHREAD_XSIG_CONT
);
164 pthread_sigmask(SIG_BLOCK
, &sigset
, NULL
);
166 sem_post(&pthread_pause_sem
);
170 int pthread_pause(pthread_t thread
) {
171 sem_wait(&pthread_pause_sem
);
172 //If signal queue is full, we keep retrying
173 while(pthread_kill(thread
, PTHREAD_XSIG_STOP
) == EAGAIN
) usleep(1000);
174 pthread_pause_yield();
178 int pthread_unpause(pthread_t thread
) {
179 sem_wait(&pthread_pause_sem
);
180 //If signal queue is full, we keep retrying
181 while(pthread_kill(thread
, PTHREAD_XSIG_CONT
) == EAGAIN
) usleep(1000);
182 pthread_pause_yield();
186 void *thread_test() {
187 //Whole process dies if you kill thread immediately before it is pausable
188 //pthread_pause_enable();
190 //Printf() is not async signal safe (because it holds internal mutex),
191 //you should call it only with pause disabled!
192 //Will throw helgrind warnings anyway, not sure why...
193 //See: man 7 signal-safety
194 pthread_pause_disable();
195 printf("Running!\n");
196 pthread_pause_enable();
198 //Pausing main thread should not cause deadlock
199 //We pause main thread here just to test it is OK
200 pthread_pause(main_thread
);
201 //pthread_nsleep(0, 1000*1000);
202 pthread_unpause(main_thread
);
205 //pthread_nsleep(0, 1000*1000*100);
206 pthread_unpause(main_thread
);
212 main_thread
= pthread_self();
213 pthread_pause_enable(); //Will get inherited by all threads from now on
214 //you need to call pthread_pause_enable (or disable) before creating threads,
215 //otherwise first (un)pause signal will kill whole process
216 pthread_create(&t
, NULL
, thread_test
, NULL
);
223 printf("UNPAUSED\n");
228 pthread_pause_disable();
229 printf("RUNNING!\n");
230 pthread_pause_enable();
236 pthread_join(t
, NULL
);