The simple client/server request/response protocol that I’m currently harvesting uses the concept of an ‘invocation’ to tie a request to a response. An id is generated on the client and placed in the request header. The server simply copies the id from the request header to the response header and the client can then match responses to requests. This works nicely but the implementation has evolved with the protocol.
The first version used a 4 byte invocation id, allocated an instance of an invocation data class and stored the allocation address of the object as the invocation id. This worked OK as the code was never ported to x64. The client ‘connector’, that is the class that managed the request/response processing, cached these invocations and reused them. If you only ever had one outstanding synchronous call then it would always use the same invocation id. A later version of the code was x64 compatible from the start and so couldn’t use the value of a pointer as a 4 byte invocation id as the pointer was 8 bytes long in x64. This code increments a ’next invocation id’ value and uses that as the invocation id, storing it in the invocation data and using it in the message. Once again it caches the invocations and reuses them so the prospect of the invocation id counter wrapping isn’t one we need to consider. For the harvested code I’m going with the later design.
Once the invocation data is allocated we store it, indexed by id. If the call is an ‘asynchronous request/response’ call then we store the callback in the invocation data and return, if it’s a polled asynchronous request/response call then we return the invocation id to the caller so that it can poll us later to see if the call has completed. If it’s a synchronous call then we wait on the event that is stored in the invocation data.
When a message comes in from the server the client looks at the invocation id in the message header. If it’s zero then the message is asynchronous and not part of a request/response pair and is dispatched directly to the handler that deals with such messages. If the invocation id is non zero then the invocation data is located. If it contains a callback then the callback is called with the response and the invocation id. If it doesn’t then the event in the invocation id is set. For synchronous calls, setting the event releases the thread that was blocking and allows it to return the response. For polled asynchronous request/response messages, setting the event means that when the caller polls for the response it will be located and returned.