Discussion:
[capnproto] Handling Signals with EzRpcServer
Richard Petri
2017-09-27 09:01:45 UTC
Permalink
Hello,

I'm trying to implement a graceful shutdown of a server if SIGTERM is
received. I'm new to capnproto, so I'm not sure on how to approach this.
Looking through the source, the UnixEventPort seems to be a way to catch
signals, but from what I can see this won't work together with the
EzRPCServer. For example, if a adapt the simple server example from
waiting forever like this

kj::NEVER_DONE.wait(waitScope);

and instead wait like this:

kj::UnixEventPort::captureSignal(SIGTERM);
kj::UnixEventPort evport;
kj::Promise<siginfo_t> p = evport.onSignal(SIGTERM).wait(waitScope);
std::cout << "Shutting down server..." << std::endl;

it appears to catch the signal (the server won't terminate when SIGTERM
arrives, so the default signal handler isn't called at least), but will
never handle it / fulfill the promise. I guess I don't understand enough
about the EventPorts, EventLoops, etc., and how this all works together.

Looking into the source, the AsyncIoContext created by an EzRpcServer
can access the eventport, but this is hidden.

Is there an easy way to achieve this that I'm missing, or will I have to
implement my own RpcServer spinoff?

Best Regards,
Richard
--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+***@googlegroups.com.
Visit this group at https://groups.google.com/group/capnproto.
'Kenton Varda' via Cap'n Proto
2017-10-02 18:45:29 UTC
Permalink
Hi Richard,

The EZ classes are really designed for the most basic use case. The intent
is that if you're doing anything more complicated, you skip them and use
the underlying APIs.

In particular you'll want to use kj::setupAsyncIo() to get your initial I/O
context, and then use capnp::TwoPartyServer and capnp::TwoPartyClient in
<capnp/rpc-twoparty.h>.

Honestly I should probably deprecate the EZ classes altogether and direct
people to these interfaces instead... it's only a couple extra lines to set
up and then you have full flexibility.

-Kenton
Post by Richard Petri
Hello,
I'm trying to implement a graceful shutdown of a server if SIGTERM is
received. I'm new to capnproto, so I'm not sure on how to approach this.
Looking through the source, the UnixEventPort seems to be a way to catch
signals, but from what I can see this won't work together with the
EzRPCServer. For example, if a adapt the simple server example from
waiting forever like this
kj::NEVER_DONE.wait(waitScope);
kj::UnixEventPort::captureSignal(SIGTERM);
kj::UnixEventPort evport;
kj::Promise<siginfo_t> p = evport.onSignal(SIGTERM).wait(waitScope);
std::cout << "Shutting down server..." << std::endl;
it appears to catch the signal (the server won't terminate when SIGTERM
arrives, so the default signal handler isn't called at least), but will
never handle it / fulfill the promise. I guess I don't understand enough
about the EventPorts, EventLoops, etc., and how this all works together.
Looking into the source, the AsyncIoContext created by an EzRpcServer
can access the eventport, but this is hidden.
Is there an easy way to achieve this that I'm missing, or will I have to
implement my own RpcServer spinoff?
Best Regards,
Richard
--
You received this message because you are subscribed to the Google Groups
"Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/group/capnproto.
--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+***@googlegroups.com.
Visit this group at https://groups.google.com/group/capnproto.
Richard Petri
2017-10-03 20:01:34 UTC
Permalink
Post by 'Kenton Varda' via Cap'n Proto
In particular you'll want to use kj::setupAsyncIo() to get your initial
I/O context, and then use capnp::TwoPartyServer and
capnp::TwoPartyClient in <capnp/rpc-twoparty.h>.
With this hint, I came up with the following:

kj::AsyncIoContext asyncio = kj::setupAsyncIo();
auto& waitScope = asyncio.waitScope;
auto& ioprovider = *asyncio.provider;
auto& network = ioprovider.getNetwork();
auto addr = network.parseAddress(SOCKET_FILE).wait(waitScope);
auto listener = addr->listen();
capnp::TwoPartyServer server(kj::heap<helloworld::TestInterfaceImpl>());
auto serverPromise = server.listen(*listener);
// Run until SIGTERM
kj::UnixEventPort::captureSignal(SIGTERM);
asyncio.unixEventPort.onSignal(SIGTERM).wait(waitScope);
std::cout << "Shutting down server..." << std::endl;

Works as far as I can see, thanks! From what I understand, the server
will close the socket if the serverPromise will be destroyed?
Post by 'Kenton Varda' via Cap'n Proto
Honestly I should probably deprecate the EZ classes altogether and
direct people to these interfaces instead... it's only a couple extra
lines to set up and then you have full flexibility.
It doesn't seem to be that much more complex, but doesn't look quite as
flashy as the Example with EZ.

/Richard
--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+***@googlegroups.com.
Visit this group at https://groups.google.com/group/capnproto.
'Kenton Varda' via Cap'n Proto
2017-10-03 22:03:18 UTC
Permalink
Post by Richard Petri
Post by 'Kenton Varda' via Cap'n Proto
In particular you'll want to use kj::setupAsyncIo() to get your initial
I/O context, and then use capnp::TwoPartyServer and
capnp::TwoPartyClient in <capnp/rpc-twoparty.h>.
kj::AsyncIoContext asyncio = kj::setupAsyncIo();
auto& waitScope = asyncio.waitScope;
auto& ioprovider = *asyncio.provider;
auto& network = ioprovider.getNetwork();
auto addr = network.parseAddress(SOCKET_FILE).wait(waitScope);
auto listener = addr->listen();
capnp::TwoPartyServer server(kj::heap<helloworld::Te
stInterfaceImpl>());
auto serverPromise = server.listen(*listener);
// Run until SIGTERM
kj::UnixEventPort::captureSignal(SIGTERM);
asyncio.unixEventPort.onSignal(SIGTERM).wait(waitScope);
std::cout << "Shutting down server..." << std::endl;
Works as far as I can see, thanks! From what I understand, the server
will close the socket if the serverPromise will be destroyed?
Yes.

That said, consider joining the promises:

asyncio.unixEventPort.onSignal(SIGTERM)
.exclusiveJoin(kj::mv(serverPromise))
.wait(waitScope);

A promise formed by exclusiveJoin() will finish (or throw) as soon as
either of its inputs finishes (or throws), automatically cancelling the
other input.

This way, if serverPromise throws an exception, it will propagate up rather
than sitting in limbo.

In general, a promise that is just sitting without being consumed is always
risky since you won't find out if it throws.

-Kenton
--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+***@googlegroups.com.
Visit this group at https://groups.google.com/group/capnproto.
Loading...