sigtimedwait approach

Pros

  • Signals can be broadcasted to multiple processes

Cons

  • We need to know PID of the program
  • OpenBSD doesn't have `sigtimedwait`
int main(int argc, char **argv) {
	if (argc > 1 && strcmp(argv[1], "update") == 0) {
		return system("pkill -f -USR1 '^PROGRAM_NAME$' >/dev/null 2>/dev/null");
	}
	sigset_t wakeup_signals = {0};
	struct timespec wakeup_delay = {1, 0L};
	if (sigemptyset(&wakeup_signals) != 0) {
		return 1;
	}
	if (sigaddset(&wakeup_signals, SIGUSR1) != 0) {
		return 1;
	}
	if (pthread_sigmask(SIG_BLOCK, &wakeup_signals, NULL) != 0) {
		return 1;
	}
	while (true) {
		do_work();
		sigtimedwait(&wakeup_signals, NULL, &wakeup_delay);
	}
	return 0;
}

sigwait approach

Pros

  • Signals can be broadcasted to multiple processes
  • OpenBSD-compatible

Cons

  • We need to know PID of the program
  • Requires 2 threads and a mutex to synchronize them
void try_do_work(void) {
	static pthread_mutex_t work_lock = PTHREAD_MUTEX_INITIALIZER;
	if (pthread_mutex_trylock(&work_lock) == 0) {
		do_work();
		pthread_mutex_unlock(&work_lock);
	}
}

void *work_handler(void *dummy) {
	(void)dummy;
	struct timespec delay = {1, 0};
	while (true) {
		try_do_work();
		nanosleep(&delay, NULL);
	}
	return NULL;
}

int main(int argc, char **argv) {
	if (argc > 1 && strcmp(argv[1], "update") == 0) {
		return system("pkill -f -USR1 '^PROGRAM_NAME$' >/dev/null 2>/dev/null");
	}
	sigset_t wakeup_signals = {0};
	if (sigemptyset(&wakeup_signals) != 0) {
		return 1;
	}
	if (sigaddset(&wakeup_signals, SIGUSR1) != 0) {
		return 1;
	}
	if (sigprocmask(SIG_BLOCK, &wakeup_signals, NULL) != 0) {
		return 1;
	}
	pthread_t work_thread;
	if (pthread_create(&work_thread, NULL, work_handler, NULL) != 0) {
		return 1;
	}
	for (int sig = 0; true; sig = 0) {
		if (sigwait(&wakeup_signals, &sig) == 0 && sig == SIGUSR1) {
			try_print_status();
		}
	}
	return 0;
}

signalfd approach

Pros

  • Signals can be broadcasted to multiple processes
  • Signals from `signalfd` are queued in order of arrival
  • Versatility of `poll` synchronous I/O multiplexing

Cons

  • We need to know PID of the program
  • OpenBSD doesn't have `signalfd`
int main(int argc, char **argv) {
	if (argc > 1 && strcmp(argv[1], "update") == 0) {
		return system("pkill -f -USR1 '^PROGRAM_NAME$' >/dev/null 2>/dev/null");
	}
	sigset_t wakeup_signals = {0};
	if (sigemptyset(&wakeup_signals) != 0) {
		return 1;
	}
	if (sigaddset(&wakeup_signals, SIGUSR1) != 0) {
		return 1;
	}
	if (pthread_sigmask(SIG_BLOCK, &wakeup_signals, NULL) != 0) {
		return 1;
	}
	int sfd = signalfd(-1, &wakeup_signals, 0);
	if (sfd < 0) {
		return 1;
	}
	int res = 0;
	struct pollfd pfd = {.fd = sfd, .events = POLLIN};
	do {
		do_work();
		res = poll(&pfd, 1, 1000);
		if (res > 0) {
			struct signalfd_siginfo siginfo;
			if (read(sfd, &siginfo, sizeof(siginfo)) != sizeof(siginfo)) {
				break;
			}
		}
	} while (res >= 0);
	return 0;
}