You should probably not use boost to handle signals. The page about signalfd (https://linux.die.net/man/2/signalfd) has this remark: Normally, the set of signals to be received via the file descriptor should be blocked using sigprocmask(2), to prevent the signals being handled according to their default dispositions. So I would expect that boost does exactly this and therefore the synchronous read doesn't get interrupted. (If boost didn't block the signal, the behavior would be "default disposition", i.e., termination as you have observed).
So try the following: install a signal handler for SIGALRM (or any other) and do NOT wait for it using boost. Have the handler just return and see whether blocking recv got interrupted. However... it's not that simple. Signals and threads don't play nicely together: a signal will be delivered to an arbitrary thread that didn't block it.
So you should have a variable holding the thread id of the thread running your io_context::run, and from within the signal handler: