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
;
29 void pthread_nanosleep(struct timespec t
) {
30 //Sleep calls on Linux get interrupted by signals, causing premature wake
31 //Pthread (un)pause is built using signals
32 //Therefore we need self-restarting sleep implementation
33 //IO timeouts are restarted by SA_RESTART, but sleeps do need explicit restart
34 while(nanosleep(&t
, &t
)) if(errno
!=EINTR
) break;
38 void pthread_nsleep(time_t s
, long ns
) {
45 void pthread_sleep(time_t s
) {
49 void pthread_pause_yield() {
50 //Call this to give other threads chance to run
51 sem_wait(&pthread_pause_sem
);
52 sem_post(&pthread_pause_sem
);
54 //nanosleep(&((const struct timespec){.tv_sec=0,.tv_nsec=1}), NULL);
55 pthread_nsleep(0,1); //pthread_yield() is not enough, so we use sleep
59 void pthread_pause_handler(int signal
) {
60 //Do nothing when there are more signals pending (to cleanup the queue)
61 //This is no longer needed, since we use semaphore to limit pending signals
65 if(sigismember(&pending, PTHREAD_XSIG_STOP)) return;
66 if(sigismember(&pending, PTHREAD_XSIG_CONT)) return;
69 sem_post(&pthread_pause_sem
);
70 if(signal
== PTHREAD_XSIG_STOP
) {
73 sigdelset(&sigset
, PTHREAD_XSIG_STOP
);
74 sigdelset(&sigset
, PTHREAD_XSIG_CONT
);
75 sigsuspend(&sigset
); //Wait for next signal
79 void pthread_pause_enable() {
80 //Having signal queue too deep might not be necessary
81 //It can be limited using RLIMIT_SIGPENDING
82 //You can get runtime SigQ stats using following command:
83 //grep -i sig /proc/$(pgrep binary)/status
84 struct rlimit sigq
= {.rlim_cur
= 32, .rlim_max
=32};
85 setrlimit(RLIMIT_SIGPENDING
, &sigq
);
90 sigaddset(&sigset
, PTHREAD_XSIG_STOP
);
91 sigaddset(&sigset
, PTHREAD_XSIG_CONT
);
93 //Register signal handlers
94 //signal(PTHREAD_XSIG_STOP, pthread_pause_handler);
95 //signal(PTHREAD_XSIG_CONT, pthread_pause_handler);
96 //We now use sigaction() instead of signal(), because it supports SA_RESTART
97 const struct sigaction pause_sa
= {
98 .sa_handler
= pthread_pause_handler
,
100 .sa_flags
= SA_RESTART
,
103 sigaction(PTHREAD_XSIG_STOP
, &pause_sa
, NULL
);
104 sigaction(PTHREAD_XSIG_CONT
, &pause_sa
, NULL
);
107 pthread_sigmask(SIG_UNBLOCK
, &sigset
, NULL
);
110 void pthread_pause_disable() {
111 //This is important for when you want to do some signal unsafe stuff
112 //Eg.: locking mutex, calling printf() which has internal mutex, etc...
113 //After unlocking mutex, you can enable pause again.
115 //Make sure all signals are dispatched before we block them
116 sem_wait(&pthread_pause_sem
);
120 sigemptyset(&sigset
);
121 sigaddset(&sigset
, PTHREAD_XSIG_STOP
);
122 sigaddset(&sigset
, PTHREAD_XSIG_CONT
);
123 pthread_sigmask(SIG_BLOCK
, &sigset
, NULL
);
125 sem_post(&pthread_pause_sem
);
129 int pthread_pause(pthread_t thread
) {
130 sem_wait(&pthread_pause_sem
);
131 //If signal queue is full, we keep retrying
132 while(pthread_kill(thread
, PTHREAD_XSIG_STOP
) == EAGAIN
) usleep(1000);
133 pthread_pause_yield();
137 int pthread_unpause(pthread_t thread
) {
138 sem_wait(&pthread_pause_sem
);
139 //If signal queue is full, we keep retrying
140 while(pthread_kill(thread
, PTHREAD_XSIG_CONT
) == EAGAIN
) usleep(1000);
141 pthread_pause_yield();
145 void *thread_test() {
146 //Whole process dies if you kill thread immediately before it is pausable
147 //pthread_pause_enable();
149 //Printf() is not async signal safe (because it holds internal mutex),
150 //you should call it only with pause disabled!
151 //Will throw helgrind warnings anyway, not sure why...
152 //See: man 7 signal-safety
153 pthread_pause_disable();
154 printf("Running!\n");
155 pthread_pause_enable();
157 //Pausing main thread should not cause deadlock
158 //We pause main thread here just to test it is OK
159 pthread_pause(main_thread
);
160 pthread_nsleep(0, 1000*1000);
161 pthread_unpause(main_thread
);
164 pthread_nsleep(0, 1000*1000*100);
165 pthread_unpause(main_thread
);
171 sem_init(&pthread_pause_sem
, 0, 1); //TODO: hide somewhere using pthred_once()
172 main_thread
= pthread_self();
173 pthread_pause_enable(); //Will get inherited by all threads from now on
174 //you need to call pthread_pause_enable (or disable) before creating threads,
175 //otherwise first (un)pause signal will kill whole process
176 pthread_create(&t
, NULL
, thread_test
, NULL
);
183 printf("UNPAUSED\n");
188 pthread_pause_disable();
189 printf("RUNNING!\n");
190 pthread_pause_enable();
196 pthread_join(t
, NULL
);
This page took 1.408436 seconds and 4 git commands to generate.