libcyphal icon indicating copy to clipboard operation
libcyphal copied to clipboard

Asynchronous Service Server

Open nfriendly opened this issue 5 years ago • 2 comments

The current Service Server ( https://github.com/UAVCAN/libuavcan/blob/legacy-v0.5/libuavcan/include/uavcan/node/service_server.hpp ) requires applications to immediately (or synchronously) reply to service request inside their callback. Their is no mechanism to respond to request after some amount of work has been done externally (asynchronously) from the callback.

For example, say you had a service call that erased a large configuration. This erase process takes significant time and the application doesn't want to block in the service callback while the erase occurs. An asynchronous service server (insert better name here) would allow the callback to initiate the erase, and then later report the result of the operation as the service response to the client.

nfriendly avatar Jun 15 '20 16:06 nfriendly

Indeed, there is some value in this. But please note that processing a service request normally should not involve time-consuming operations because that may cause the client to time-out. In your configuration erase example you may want to express the asynchronous nature of the operation in the service definition itself. For example, you could define the protocol such that the server responds immediately upon commencement of the erase operation instead of blocking until it's completed.

@thirtytwobits do you think this is in the scope of Libuavcan v1?

pavel-kirienko avatar Jun 15 '20 17:06 pavel-kirienko

I was able to implement this using a ServiceServer<ServiceType, CallbackType> and a GenericPublisher<ServiceType, ServiceType::Response>. The callback for the service server used the full data structure types, using them to capture transfer data and suppress the automatic response.

serviceCallback(const uavcan::ReceivedDataStructure<Request>& request, uavcan::ServiceResponseDataStructure<Response>& response) {
    // Don't respond, we'll do it later
    response.setResponseEnabled(false);
    // Save the info required to respond correctly
    m_last_client_node_id = request.getSrcNodeID();
    m_last_tid = request.getTransferID();
    m_last_priority = request.getPriority();
}

I later used the genericPublisher to publish the response.

m_pub.publish(response, uavcan::TransferTypeServiceResponse, m_last_client_node_id, m_last_tid);

I still think adding this feature to libuavcan is valuable, but I'm happy that applications can currently do it in a sane manner.

nfriendly avatar Jun 18 '20 00:06 nfriendly

This is designed-in for v1, see #333

pavel-kirienko avatar Apr 04 '24 12:04 pavel-kirienko