First, you'll need to arrange to catch the signal via UnixEventPort. When you call kj::setupAsyncIo(), the returned struct contains a reference on a UnixEventPort. You can use its onSignal() method to register interest in a signal like SIGTERM. This method returns a kj::Promise<void> that resolves when the signal is received. See `kj/async-unix.h` for full documentation.
The trick to ending the event loop is to make sure that wherever your server called .wait() to run the event loop, it is waiting for a promise that will complete when you want to exit.
So, instead of doing:
kj::NEVER_DONE.wait(io.waitScope);
You could instead do:
io.unixEventPort.onSignal(SIGTERM).wait(io.waitScope);
If you want to have multiple ways to terminate the loop, you should use promise.exclusiveJoin() to join multiple promises, and then wait on the result. When any of the joined promises completes, the wait will finish.
In order to wait on a particular RPC call, you probably want to create a Promise/Fulfiller pair.
auto paf = kj::newPromiseAndFulfiller<void>();
Pass off paf.fulfiller to the RPC server implementation. Call its fulfill() method when the shutdown RPC is received.
Then your wait looks like this:
io.unixEventPort.onSignal(SIGTERM)
.exclusiveJoin(kj::mv(paf.promise))
.wait(io.waitScope);