It is possible for the user to create new types of CpmDestinations, and to write functions that return these new destinations. In order to do this, one must have a mental model of the steps performed when a Cpm message is sent. This knowledge is only necessary to those wishing to invent new kinds of destinations. Others can skip this section.
The basic steps taken when sending a CPM message are:
The send-function varies because messages take different routes to get to their final destinations. Compare, for example, CpmSend to CpmEnqueueFIFO. When CpmSend is used, the message goes straight to the target processor and gets handled. When CpmEnqueueFIFO is used, the message goes to the target processor, goes into the queue, comes out of the queue, and then gets handled. The send-function must implement not only the transmission of the message, but also the possible ``detouring'' of the message through queues or into threads.
We now show an example CPM command, and describe the steps that are taken when the command is executed. The command we will consider is this one:
Which sends a message to processor 3, ordering it to call print_integer(12).
The first step is taken by CpmEnqueueFIFO, which builds the CpmDestination. The following is the code for CpmEnqueueFIFO:
Notice that the CpmDestination structure varies, depending upon which kind of destination is being used. In this case, the destination structure contains a pointer to the send-function CpmEnqueueFIFO1, a field that controls the size of the envelope, and the destination-processor. In a CpmDestination, the sendfn and envsize fields are required, additional fields are optional.
After CpmEnqueueFIFO builds the destination-structure, the launcher Cpm_print_integer is invoked. Cpm_print_integer performs all the steps normally taken by a launcher:
The code for the send-function is here:
The send-function CpmEnqueueFIFO1 starts by switching the handler. The original handler is removed using using CmiGetHandler. It is set aside in the message buffer in the ``envelope'' space described earlier -- notice the use of CpmEnv to obtain the envelope. This is the purpose of the envelope in the message -- it is a place where the send-function can store information. The destination-function must anticipate how much space the send-function will need, and it must specify that amount of space in the destination-structure field envsize. In this case, the envelope is used to store the original handler, and the message's handler is set to an internal function called CpmEnqueueFIFO2.
After switching the handler, CpmEnqueueFIFO1 sends the message. Eventually, the message will be received by CsdScheduler, and its handler will be called. The result will be that CpmEnqueueFIFO2 will be called on the destination processor. Here is the code for CpmEnqueueFIFO2:
This function takes ownership of the message-buffer from CONVERSE using CmiGrabBuffer. It extracts the original handler from the envelope (the handler that calls print_integer), and restores it using CmiSetHandler. Having done so, it enqueues the message with the FIFO queueing policy. Eventually, the scheduler picks the message from the queue, and print_integer is invoked.
In summary, the procedure for implementing new kinds of destinations is to write one send-function, one function returning a CpmDestination (which contains a reference to the send-function), and one or more CONVERSE handlers to manipulate the message.
The destination-function must return a pointer to a ``destination-structure'', which can in fact be any structure matching the following specifications:
This pointer must be coerced to type CpmDestination.
The send-function must have the following prototype:
It can access the envelope of the message using CpmEnv:
It can also access the data stored in the destination-structure by the destination-function.
June 29, 2008
Charm Homepage