Cross test priprava
[mirrors/Programs.git] / c / pthread_pause.c
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
5
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
8
9 #include <signal.h>
10 #include <pthread.h>
11 //#include <pthread_extra.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <assert.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <sys/resource.h>
18
19 #define PTHREAD_XSIG_STOP (SIGRTMIN+0)
20 #define PTHREAD_XSIG_CONT (SIGRTMIN+1)
21 #define PTHREAD_XSIGRTMIN (SIGRTMIN+2) //First unused RT signal
22
23 pthread_t main_thread;
24
25 void pthread_pause_handler(int signal) {
26 //Do nothing when there are more signals pending (to cleanup the queue)
27 sigset_t pending;
28 sigpending(&pending);
29 if(sigismember(&pending, PTHREAD_XSIG_STOP)) return;
30 if(sigismember(&pending, PTHREAD_XSIG_CONT)) return;
31
32 if(signal == PTHREAD_XSIG_STOP) {
33 sigset_t sigset;
34 sigfillset(&sigset);
35 sigdelset(&sigset, PTHREAD_XSIG_STOP);
36 sigdelset(&sigset, PTHREAD_XSIG_CONT);
37 sigsuspend(&sigset); //Wait for next signal
38 } else return;
39 }
40
41 void pthread_pause_enable() {
42 //Having signal queue too deep might not be necessary
43 //It can be limited using RLIMIT_SIGPENDING
44 //You can get runtime SigQ stats using following command:
45 //grep -i sig /proc/$(pgrep binary)/status
46 struct rlimit sigq = {.rlim_cur = 32, .rlim_max=32};
47 setrlimit(RLIMIT_SIGPENDING, &sigq);
48
49 //Register signal handlers
50 signal(PTHREAD_XSIG_STOP, pthread_pause_handler);
51 signal(PTHREAD_XSIG_CONT, pthread_pause_handler);
52
53 //UnBlock signals
54 sigset_t sigset;
55 sigemptyset(&sigset);
56 sigaddset(&sigset, PTHREAD_XSIG_STOP);
57 sigaddset(&sigset, PTHREAD_XSIG_CONT);
58 pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
59 }
60
61 void pthread_pause_disable() {
62 //This is important for when you want to do some signal unsafe stuff
63 //Eg.: locking mutex, calling printf() which has internal mutex, etc...
64 //After unlocking mutex, you can enable pause again.
65
66 //Block signals
67 sigset_t sigset;
68 sigemptyset(&sigset);
69 sigaddset(&sigset, PTHREAD_XSIG_STOP);
70 sigaddset(&sigset, PTHREAD_XSIG_CONT);
71 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
72 }
73
74
75 int pthread_pause(pthread_t thread) {
76 //If signal queue is full, we keep retrying
77 while(pthread_kill(thread, PTHREAD_XSIG_STOP) == EAGAIN) usleep(1000);
78 return 0;
79 }
80
81 int pthread_unpause(pthread_t thread) {
82 //If signal queue is full, we keep retrying
83 while(pthread_kill(thread, PTHREAD_XSIG_CONT) == EAGAIN) usleep(1000);
84 return 0;
85 }
86
87 void *thread_test() {
88 //Whole process dies if you kill thread immediately before it is pausable
89 //pthread_pause_enable();
90 while(1) {
91 usleep(1000*300);
92 //Printf() is not async signal safe (because it holds internal mutex),
93 //you should call it only with pause disabled!
94 //Will throw helgrind warnings anyway, not sure why...
95 //See: man 7 signal-safety
96 pthread_pause_disable();
97 printf("Running!\n");
98 pthread_pause_enable();
99 //pthread_pause(main_thread);
100 //pthread_unpause(main_thread);
101 }
102 }
103
104 int main() {
105 pthread_t t;
106 main_thread = pthread_self();
107 pthread_pause_enable(); //Will get inherited by all threads from now on
108 //you need to call pthread_pause_enable (or disable) before creating threads,
109 //otherwise first signal will kill whole process
110 pthread_create(&t, NULL, thread_test, NULL);
111
112 while(1) {
113 pthread_pause(t);
114 printf("PAUSED\n");
115 sleep(3);
116
117 printf("UNPAUSED\n");
118 pthread_unpause(t);
119 sleep(1);
120 }
121
122 pthread_join(t, NULL);
123 printf("DIEDED!\n");
124 }
This page took 0.4046 seconds and 4 git commands to generate.