I have a GRPC server application I'm developing on macOS. In this application, I need to handle SIGINT and SIGTERM in order to terminate gracefully.
Normally, on Linux, I would use POSIX semaphores, as the functions involved to wait and notify using a POSIX semaphore are async-signal-safe.
However, in macOS, POSIX sempahores are not implemented. I've looked at other possible synchronization primitives to use and I've stumbled upon os_unfair_lock
, which looks nice, but in the documentation it does not state that it is async-signal-safe.
How can I safely have a thread waiting on a condition variable be woken up from a signal handler without resorting to polling an atomic variable, which would be burning CPU?
Thanks
EDIT: The application is in C/C++
I've resorted to use pipe()
.
int fds[2];
if(pipe(fds) != 0) {
LOG(ERROR) << "Unable to open pipe: " << strerror(errno) << std::endl;
return 1;
}
struct sigaction newAction;
newAction.sa_handler = signalHandler;
sigemptyset(&newAction.sa_mask);
newAction.sa_flags = 0;
sigaction(SIGINT, &newAction, NULL);
sigaction(SIGTERM, &newAction, NULL);
read()
with blocking IO to not waste CPU cycles. Bear in mind the call can return if interrupted by a signal, hence why we check for EINTR
:ssize_t bytes_read = read(pipeFD, &msg, 4);
if(bytes_read == -1) {
if(errno != EINTR) {
LOG(ERROR) << "Error reading from pipe: " << strerror(errno) << std::endl;
exitFlag = true;
break;
}
}
write
to the pipe. The write
syscall is async-signal-safe:void signalHandler(int signal) {
write(pipeFD, "EXIT", 4);
}
That does the trick. The user can validate the data sent by the pipe or not, according to pipe(2)
, when both file descriptors are closed any remaining data is discarded.