Hi,
The tagInfo/callData can be whatever you want. In the example I attached, the 4 different rpc implementations encapsulate all the state machinery that async gRPC needs in a manner that is transparent to rest of the application code. The tagInfo just 'ends up' at the right state.
The idea is to provide meaningful implementation of the 'RpcHandlers' when your application instantiates the rpc. That implementation can be per rpc (as I have done in the sample - for example, each rpc gets it's own processor implementation) or can be per service etc. It just depends on how your application is set up. My application is adding gRPC support in addition to our current rpc framework so what I do is simply figure out the existing handler in the implementation of those methods and route the rpc appropriately (This is not how example I attached is set up btw).
For threading, yes, I have a dedicated thread for processing completion queue. It's not a busy loop though - the completion queue internally yields.
I have re-attached a new sample that is simplified than the one I attached earlier. This is pretty much a cleaned up version of what I use in my app today. This one adds the error handling capabilities and makes sending rpc responses and their lifetime management easier.
HTH.