The ainvoke interface in Cheetah controllers is particularly useful when the function you're calling has very few synchronization needs. The send/receive interface of MPI is very useful when you have strong synchronization needs. Many applications have stronger synchronization needs than bare ainvoke, but you want something with more functionality than send/receive. For these applications we have the MatchingHandler class, which delays calling an ainvoke until a tag is provided on the receiving side.
This is a useful functionality if the function to be ainvoked needs to access data on the receiving side that may not be constructed yet. Many uses of ainvoke don't need this because when you do an ainvoke you often have a pointer to whatever data the function will use. If you have a pointer to the function you know that it exists.
You may not have a pointer to it though. One case when you won't have a pointer is when you have a tag of some kind which needs to be converted into a pointer. You may well want to do that when you have a particular kind of object in each context, and instead of storing a different pointer for each context, you just remember one tag. Then in each specific context you can turn that tag into the pointer. If you have thousands of contexts, all of which could communicate with each other, this can be a big savings in complexity and memory.
MatchingHandlers are intended to be used in a data parallel fashion. For each MatchingHandler in a given context there is a corresponding MatchingHandler in every other context. The correspondence between them is set up by constructing the MatchingHandlers in every context in the same order. Messages sent through one MatchingHandler always connect to its corresponding MatchingHandler, and in this way MatchingHandlers are like MPI communicators.
Each MatchingHandler has a Controller. You can change the controller dynamically, though if you change the controller in one context you have to change it for the corresponding MatchingHandlers in all of the contexts.
MatchingHandlers use the Cheetah serialization API to pack C++ objects into and out of buffers. That means you can send up to four objects essentially transparently, and the MatchingHandler will pack them into a buffer, ship them across the wire, unpack them into objects again and call the requested function. This is an important simplification over the ainvoke API in controllers.
You use a MatchingHandler in a very similar manner to an ainvoke. You have some data on the calling context that you wish to pass to another context and run a function using that data. The ways in which it is different from a controller ainvoke are:
Using a MatchingHandler then looks something like this. On the context c2, suppose you have a MatchingHandler mh, you're expecting a request with tag my_tag from context c1, and when it comes in you want to call the function 'void foo(T1,T2,T3)'. On the receiving side you say:
mh.request(c1,my_tag,foo);
On the sending side you have objects x1, x2 and x3 of types T1, T2, T3, and you send them by saying:
mh.send(c2,my_tag,x1,x2,x3);
If the request has not been posted before the send arrives, it is queued up in the MatchingHandler on c2 until the request is posted.
There are a number of variations on this pattern you can:
For the details of the syntax, see the API Reference.