Many threads have a main loop that checks some exit variable in the loop condition. To stop a thread, another thread sets that exit variable, sends a SIGUSR1 to that thread to get it out of blocking calls like read(), write() or select(), and finally joins it.
There was a race condition though: if the signal arrived after the loop condition was checked but before the blocking system function was entered, the target thread essentially ignored the signal, made the blocking call anyway, and could get stuck forever there.
The only way around this race condition is to block SIGUSR1 in the destination thread during the critical time interval. This requires that the blocking system call and the call to unblock SIGUSR1 be an atomic unit. There is only one system call that makes this possible: pselect(), which takes a signal mask to replace the current signal mask with, but only while pselect() runs. Other functions like read(), recv(), write() and sendto() may only be called if we can be sure that they don't block. For this purpose, the file descriptors must be set to non-blocking mode and we must prepend the call to them with a call to pselect() to block until the socket becomes readable/writable.
The while loop should be repeated, if the last write attempt was a partial write only. In this case, however, rc is greater than 0 (should be 1), not equal to 0.
die(), warn(), debug() and inform() write the string into a limited internal buffer first. They now use vsnprintf() instead of vsprintf() to prevent an overflow of that buffer.
Commit f36352dc4e introduced the parameter
pipe_timeout for the metadata config. If shairport-sync is now configured
without metadata support, the build fails with:
```
shairport.c: In function ‘main’:
shairport.c:766:9: error: ‘shairport_cfg {aka struct <anonymous>}’ has no member named ‘metadata_pipe_timeout’
config.metadata_pipe_timeout = 5000; //milliseconds
```
Fix this by opting-out the affected code with CONFIG_METADATA.