void CldEnqueue(int pe, void *msg, int infofn)
{
int len, queueing, priobits; unsigned int *prioptr;
CldInfoFn ifn = (CldInfoFn)CmiHandlerToFunction(infofn);
CldPackFn pfn;
if (pe == CLD_ANYWHERE) {
/* do what you want with the message; in this case we'll just keep
it local */
ifn(msg, &pfn, &len, &queueing, &priobits, &prioptr);
CmiSetInfo(msg,infofn);
CsdEnqueueGeneral(msg, queueing, priobits, prioptr);
}
else {
/* pe contains a particular destination or broadcast */
ifn(msg, &pfn, &len, &queueing, &priobits, &prioptr);
if (pfn) {
pfn(&msg);
ifn(msg, &pfn, &len, &queueing, &priobits, &prioptr);
}
CldSwitchHandler(msg, CpvAccess(CldHandlerIndex));
CmiSetInfo(msg,infofn);
if (pe==CLD_BROADCAST)
CmiSyncBroadcastAndFree(len, msg);
else if (pe==CLD_BROADCAST_ALL)
CmiSyncBroadcastAllAndFree(len, msg);
else CmiSyncSendAndFree(pe, len, msg);
}
}
The primary function a load balancer must provide is the CldEnqueue function, which has the following prototype:
void CldEnqueue(int pe, void *msg, int infofn);
This function takes three parameters: pe,
msg and infofn. pe is the intended destination of
the msg. pe may take on one of the following values:
Any valid processor number - the message must be sent to
that processor
CLD_ANYWHERE - the message can be placed on any processor
CLD_BROADCAST - the message must be sent to all processors
excluding the local processor
CLD_BROADCAST_ALL - the message must be sent to all processors
including the local processor
CldEnqueue must handle all of these possibilities. The only
case in which the load balancer should get control of a message is when
pe = CLD_ANYWHERE. All other messages must be sent off to their
intended destinations and passed on to the scheduler as if they never
came in contact with the load balancer.
The integer parameter infofn is a handler index for a
user-provided function that allows CldEnqueue to extract information about
(mostly components of) the message msg.
Thus, an implementation of the CldEnqueue function might have
the following structure:
void CldEnqueue(int pe, void *msg, int infofn)
{
...
if (pe == CLD_ANYWHERE)
/* These messages can be load balanced */
else if (pe == CmiMyPe())
/* Enqueue the message in the scheduler locally */
else if (pe==CLD_BROADCAST)
/* Broadcast to all but self */
else if (pe==CLD_BROADCAST_ALL)
/* Broadcast to all plus self */
else /* Specific processor number was specified */
/* Send to specific processor */
}
In order to fill in the code above, we need to know more about the
message before we can send it off to a scheduler's queue, either
locally or remotely. For this, we have the info function. The
prototype of an info function must be as follows:
void ifn(msg, pfn, len, queueing, priobits, prioptr); void *msg; CldPackFn *pfn; int *len, *queueing, *priobits; unsigned int **prioptr;
Thus, to use the info function, we need to get the actual function via
the handler index provided to CldEnqueue. Typically, CldEnqueue would contain the following declarations:
int len, queueing, priobits;
unsigned int *prioptr;
CldPackFn pfn;
CldInfoFn ifn = (CldInfoFn)CmiHandlerToFunction(infofn);
The info function extracts information from the message about its size,
queuing strategy and priority, and also a pack function, which will be
used when we need to send the message elsewhere. For now, consider
the case where the message is to be locally enqueued:
...
else if (pe == CmiMyPe())
{
ifn(msg, &pfn, &len, &queueing, &priobits, &prioptr);
CsdEnqueueGeneral(msg, queueing, priobits, prioptr);
}
...
Thus, we see the info function is used to extract info from the
message that is necessary to pass on to CsdEnqueueGeneral.
In order to send the message to a remote destination and enqueue it in
the scheduler, we need to pack it up with a special pack function so
that it has room for extra handler information and a reference to the
info function. Therefore, before we handle the last three cases of
CldEnqueue, we have a little extra work to do:
Calling the info function once gets the pack function we need, if
there is one. We then call the pack function which rearranges the
message leaving space for the info function, which we will need to
call on the message when it is received at its destination, and also
room for the extra handler that will be used on the receiving side to
do the actual enqueuing. CldSwitchHandler is used to set this extra
handler, and the receiving side must restore the original handler.
In the above code, we call the info function again because some of the
values may have changed in the packing process.
Finally, we handle our last few cases:
...
if (pe==CLD_BROADCAST)
CmiSyncBroadcastAndFree(len, msg);
else if (pe==CLD_BROADCAST_ALL)
CmiSyncBroadcastAllAndFree(len, msg);
else CmiSyncSendAndFree(pe, len, msg);
}
}
The above example also provides CldHandler which is used to
receive messages that CldEnqueue forwards to other processors.
CpvDeclare(int, CldHandlerIndex);
void CldHandler(void *msg)
{
CldInfoFn ifn; CldPackFn pfn;
int len, queueing, priobits; unsigned int *prioptr;
Note that the CldHandler properly restores the message's original
handler using CldRestoreHandler, and calls the info function to obtain
the proper parameters to pass on to the scheduler. We talk about this
more below.
Finally, CONVERSE initialization functions call CldModuleInit to
initialize the load balancer module.