Skip to content

Instantly share code, notes, and snippets.

@ysbaddaden
Last active June 21, 2022 14:12
Show Gist options
  • Save ysbaddaden/9a484ef55ed451673176a7614207a1c1 to your computer and use it in GitHub Desktop.
Save ysbaddaden/9a484ef55ed451673176a7614207a1c1 to your computer and use it in GitHub Desktop.
Stop the World in C
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ucontext.h>
#define SIG_SUSPEND SIGUSR1
#define SIG_RESUME SIGUSR2
int running = 1;
atomic_int started_threads;
atomic_int stopped_threads;
void *thread_main(__attribute__((__unused__)) void *data) {
printf("starting thread (%lu)\n", pthread_self());
++started_threads;
int count;
do {
++count;
} while(running);
printf("exiting thread: count=%d (%lu)\n", count, pthread_self());
--started_threads;
return NULL;
}
pthread_t thread_start() {
pthread_t tid;
int err = pthread_create(&tid, NULL, thread_main, NULL);
if (err != 0) {
fprintf(stderr, "pthread_create: %s (%d)\n", strerror(err), err);
abort();
}
return tid;
}
void thread_suspend(__attribute__((__unused__)) int signal, __attribute__((__unused__)) siginfo_t *info, __attribute__((__unused__)) void *ucontext) {
fprintf(stderr, "stopping thread (waiting for RESUME signal) (%lu)\n", pthread_self());
++stopped_threads;
sigset_t mask;
sigfillset(&mask);
sigdelset(&mask, SIG_RESUME);
sigsuspend(&mask);
fprintf(stderr, "resuming suspended signal handler (%lu)\n", pthread_self());
--stopped_threads;
}
void thread_resume(__attribute__((__unused__)) int signal, __attribute__((__unused__)) siginfo_t *info, __attribute__((__unused__)) void *ucontext) {
fprintf(stderr, "waking up thread (%lu)\n", pthread_self());
}
int main() {
struct sigaction action;
action.sa_handler = NULL;
sigemptyset(&action.sa_mask);
action.sa_restorer = NULL;
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = thread_suspend;
if (sigaction(SIG_SUSPEND, &action, NULL) < 0) {
fprintf(stderr, "sigaction: %s (%d)\n", strerror(errno), errno);
abort();
}
action.sa_flags = 0;
action.sa_sigaction = thread_resume;
if (sigaction(SIG_RESUME, &action, NULL) < 0) {
fprintf(stderr, "sigaction: %s (%d)\n", strerror(errno), errno);
abort();
}
fprintf(stderr, "starting threads...\n");
pthread_t t1 = thread_start();
pthread_t t2 = thread_start();
pthread_t t3 = thread_start();
pthread_t t4 = thread_start();
do {
usleep(1);
} while (started_threads < 4);
fprintf(stderr, "started_threads=%d stopped_threads=%d\n", started_threads, stopped_threads);
fprintf(stderr, "\nWORLD IS RUNNING!\n");
fprintf(stderr, "\nsuspending threads...\n");
pthread_kill(t1, SIG_SUSPEND);
pthread_kill(t2, SIG_SUSPEND);
pthread_kill(t3, SIG_SUSPEND);
pthread_kill(t4, SIG_SUSPEND);
do {
usleep(1);
} while (stopped_threads < 4);
fprintf(stderr, "started_threads=%d stopped_threads=%d\n", started_threads, stopped_threads);
fprintf(stderr, "\nWORLD IS STOPPED!\n");
fprintf(stderr, "\nresuming threads...\n");
pthread_kill(t1, SIG_RESUME);
pthread_kill(t2, SIG_RESUME);
pthread_kill(t3, SIG_RESUME);
pthread_kill(t4, SIG_RESUME);
do {
usleep(1);
} while (stopped_threads > 0);
fprintf(stderr, "started_threads=%d stopped_threads=%d\n", started_threads, stopped_threads);
fprintf(stderr, "\nWORLD IS RUNNING AGAIN!\n");
fprintf(stderr, "\nstopping threads...\n");
running = 0;
void *retval;
pthread_join(t1, &retval);
pthread_join(t2, &retval);
pthread_join(t3, &retval);
pthread_join(t4, &retval);
fprintf(stderr, "started_threads=%d stopped_threads=%d\n", started_threads, stopped_threads);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment