1 /* ----------------------------------------------------------------------------
2 * Time-stamp: <Mon Mar 19 2001 22:10:38 Stardate: [-30]6354.62 hwloidl>
3 * $Id: LLComms.c,v 1.4 2001/03/22 03:51:11 hwloidl Exp $
5 * GUM Low-Level Inter-Task Communication
7 * This module defines PVM Routines for PE-PE communication.
9 * P. Trinder, December 5th. 1994.
10 * P. Trinder, July 1998
11 * H-W. Loidl, November 1999 -
12 --------------------------------------------------------------------------- */
14 #ifdef PAR /* whole file */
16 //@node GUM Low-Level Inter-Task Communication, , ,
17 //@section GUM Low-Level Inter-Task Communication
20 *This module defines the routines which communicate between PEs. The
21 *code is based on Kevin Hammond's GRIP RTS. (OpCodes.h defines
22 *PEOp1 etc. in terms of sendOp1 etc.).
30 *sendOpV & variable \\
31 *sendOpNV & variable+ vector \\
33 *First the standard include files.
39 //* Auxiliary functions::
43 //@node Macros etc, Includes, GUM Low-Level Inter-Task Communication, GUM Low-Level Inter-Task Communication
44 //@subsection Macros etc
46 #define NON_POSIX_SOURCE /* so says Solaris */
47 #define UNUSED /* nothing */
49 //@node Includes, Auxiliary functions, Macros etc, GUM Low-Level Inter-Task Communication
50 //@subsection Includes
56 #include "ParallelRts.h"
58 # include "ParallelDebug.h"
68 /* Cannot use std macro when compiling for SysMan */
69 /* debugging enabled */
70 // #define IF_PAR_DEBUG(c,s) { s; }
71 /* debugging disabled */
72 #define IF_PAR_DEBUG(c,s) /* nothing */
74 //@node Auxiliary functions, Index, Includes, GUM Low-Level Inter-Task Communication
75 //@subsection Auxiliary functions
78 * heapChkCounter tracks the number of heap checks since the last probe.
79 * Not currently used! We check for messages when a thread is resheduled.
81 int heapChkCounter = 0;
84 * Then some miscellaneous functions.
85 * getOpName returns the character-string name of any OpCode.
88 char *UserPEOpNames[] = { PEOP_NAMES };
94 if (op >= MIN_PEOPS && op <= MAX_PEOPS)
95 return (UserPEOpNames[op - MIN_PEOPS]);
97 return ("Unknown PE OpCode");
101 * traceSendOp handles the tracing of messages.
104 //@cindex traceSendOp
106 traceSendOp(OpCode op, GlobalTaskId dest UNUSED,
107 unsigned int data1 UNUSED, unsigned int data2 UNUSED)
111 OpName = getOpName(op);
113 fprintf(stderr," %s [%x,%x] sent from %x to %x",
114 OpName, data1, data2, mytid, dest));
118 * sendOp sends a 0-argument message with OpCode {\em op} to
119 * the global task {\em task}.
124 sendOp(OpCode op, GlobalTaskId task)
126 traceSendOp(op, task,0,0);
128 pvm_initsend(PvmDataRaw);
133 * sendOp1 sends a 1-argument message with OpCode {\em op}
134 * to the global task {\em task}.
139 sendOp1(OpCode op, GlobalTaskId task, StgWord arg1)
141 traceSendOp(op, task, arg1,0);
143 pvm_initsend(PvmDataRaw);
150 * sendOp2 is used by the FP code only.
155 sendOp2(OpCode op, GlobalTaskId task, StgWord arg1, StgWord arg2)
157 traceSendOp(op, task, arg1, arg2);
159 pvm_initsend(PvmDataRaw);
167 * sendOpV takes a variable number of arguments, as specified by {\em n}.
170 * sendOpV( PP_STATS, StatsTask, 3, start_time, stop_time, sparkcount);
175 sendOpV(OpCode op, GlobalTaskId task, int n, ...)
183 traceSendOp(op, task, 0, 0);
185 pvm_initsend(PvmDataRaw);
187 for (i = 0; i < n; ++i) {
188 arg = va_arg(ap, StgWord);
198 * sendOpNV takes a variable-size datablock, as specified by {\em
199 * nelem} and a variable number of arguments, as specified by {\em
200 * narg}. N.B. The datablock and the additional arguments are contiguous
201 * and are copied over together. For example,
203 * sendOpNV(PP_RESUME, tsoga.pe, 6, nelem, data,
204 * (W_) ga.weight, (W_) ga.loc.gc.gtid, (W_) ga.loc.gc.slot,
205 * (W_) tsoga.weight, (W_) tsoga.loc.gc.gtid, (W_) tsoga.loc.gc.slot);
207 * Important: The variable arguments must all be StgWords.
209 sendOpNV(_, tid, m, n, data, x1, ..., xm):
212 +------------------------------
213 | x1 | ... | xm | n | data ....
214 +------------------------------
219 sendOpNV(OpCode op, GlobalTaskId task, int nelem,
220 StgWord *datablock, int narg, ...)
228 traceSendOp(op, task, 0, 0);
230 fprintf(stderr,"~~ sendOpNV: op = %x (%s), task = %x, narg = %d, nelem = %d",
231 op, getOpName(op), task, narg, nelem));
233 pvm_initsend(PvmDataRaw);
235 for (i = 0; i < narg; ++i) {
236 arg = va_arg(ap, StgWord);
238 fprintf(stderr,"~~ sendOpNV: arg = %d\n",arg));
241 arg = (StgWord) nelem;
244 /* for (i=0; i < nelem; ++i) fprintf(stderr, "%d ",datablock[i]); */
245 /* fprintf(stderr," in sendOpNV\n");*/
247 PutArgs(datablock, nelem);
254 * sendOpN take a variable size array argument, whose size is given by
255 * {\em n}. For example,
257 * sendOpN( PP_STATS, StatsTask, 3, stats_array);
262 sendOpN(OpCode op, GlobalTaskId task, int n, StgPtr args)
266 traceSendOp(op, task, 0, 0);
268 pvm_initsend(PvmDataRaw);
276 * broadcastOpN is as sendOpN but broadcasts to all members of a group.
280 broadcastOpN(OpCode op, char *group, int n, StgPtr args)
284 //traceSendOp(op, task, 0, 0);
286 pvm_initsend(PvmDataRaw);
290 pvm_bcast(group, op);
294 waitForPEOp waits for a packet from global task who with the
295 OpCode op. If ignore is true all other messages are simply ignored;
296 otherwise they are handled by processUnexpected.
298 //@cindex waitForPEOp
300 waitForPEOp(OpCode op, GlobalTaskId who, void(*processUnexpected)(rtsPacket) )
305 GlobalTaskId sender_id;
308 IF_PAR_DEBUG(verbose,
309 fprintf(stderr,"~~ waitForPEOp: expecting op = %x (%s), who = [%x]\n",
310 op, getOpName(op), who));
313 while((p = pvm_recv(ANY_TASK,ANY_OPCODE)) < 0)
314 pvm_perror("waitForPEOp: Waiting for PEOp");
316 pvm_bufinfo( p, &nbytes, &opCode, &sender_id );
317 match = (op == ANY_OPCODE || op == opCode) &&
318 (who == ANY_TASK || who == sender_id);
321 IF_PAR_DEBUG(verbose,
323 "~~waitForPEOp: Qapla! received: OpCode = %#x (%s), sender_id = [%x]",
324 opCode, getOpName(opCode), sender_id));
329 /* Handle the unexpected OpCodes */
330 if (processUnexpected!=NULL) {
331 (*processUnexpected)(p);
333 IF_PAR_DEBUG(verbose,
335 "~~ waitForPEOp: ignoring OpCode = %#x (%s), sender_id = [%x]",
336 opCode, getOpName(opCode), sender_id));
343 processUnexpected processes unexpected messages. If the message is a
344 FINISH it exits the prgram, and PVM gracefully
346 //@cindex processUnexpectedMessage
348 processUnexpectedMessage(rtsPacket packet) {
349 OpCode opCode = getOpcode(packet);
351 IF_PAR_DEBUG(verbose,
352 GlobalTaskId sender = senderTask(packet);
353 fprintf(stderr,"~~ [%x] processUnexpected: Received %x (%s), sender %x\n",
354 mytid, opCode, getOpName(opCode), sender));
358 stg_exit(EXIT_SUCCESS);
361 /* Anything we're not prepared to deal with. Note that ALL OpCodes
362 are discarded during termination -- this helps prevent bizarre
365 // if (!GlobalStopPending)
367 GlobalTaskId errorTask;
370 getOpcodeAndSender(packet, &opCode, &errorTask);
371 fprintf(stderr,"== Task %x: Unexpected OpCode %x from %x in processUnexpected",
372 mytid, opCode, errorTask );
374 stg_exit(EXIT_FAILURE);
381 getOpcode(rtsPacket p)
385 GlobalTaskId sender_id;
386 /* read PVM buffer */
387 pvm_bufinfo(p, &nbytes, &OpCode, &sender_id);
388 /* return tag of the buffer as opcode */
392 //@cindex getOpcodeAndSender
394 getOpcodeAndSender(rtsPacket p, OpCode *opCodep, GlobalTaskId *senderIdp)
397 /* read PVM buffer */
398 pvm_bufinfo(p, &nbytes, opCodep, senderIdp);
403 senderTask(rtsPacket p)
407 GlobalTaskId sender_id;
408 /* read PVM buffer */
409 pvm_bufinfo(p, &nbytes, &opCode, &sender_id);
414 * startUpPE does the low-level comms specific startup stuff for a
415 * PE. It initialises the comms system, joins the appropriate groups
416 * allocates the PE buffer
423 mytid = _my_gtid; /* Initialise PVM and get task id into global var.*/
425 IF_PAR_DEBUG(verbose,
426 fprintf(stderr,"== [%x] PEStartup: Task id = [%x], No. PEs = %d \n",
427 mytid, mytid, nPEs));
428 checkComms(pvm_joingroup(PEGROUP), "PEStartup");
429 IF_PAR_DEBUG(verbose,
430 fprintf(stderr,"== [%x] PEStartup: Joined PEGROUP\n", mytid));
434 * PEShutdown does the low-level comms-specific shutdown stuff for a
435 * single PE. It leaves the groups and then exits from pvm.
441 IF_PAR_DEBUG(verbose,
442 fprintf(stderr, "== [%x] PEshutdown\n", mytid));
444 checkComms(pvm_lvgroup(PEGROUP),"PEShutDown");
445 checkComms(pvm_exit(),"PEShutDown");
449 Extract the exit code out of a PP_FINISH packet (used in SysMan)
452 getExitCode(int nbytes, GlobalTaskId *sender_idp) {
455 if (nbytes==4) { // Notification from a task doing pvm_exit
456 GetArgs(sender_idp,1); // Presumably this must be MainPE Id
458 } else if (nbytes==8) { // Doing a controlled shutdown
459 GetArgs(&exitCode,1); // HACK: controlled shutdown == 2 values
460 GetArgs(&exitCode,1);
462 exitCode = -2; // everything else
467 #endif /* PAR -- whole file */
469 //@node Index, , Auxiliary functions, GUM Low-Level Inter-Task Communication
473 //* getOpName:: @cindex\s-+getOpName
474 //* traceSendOp:: @cindex\s-+traceSendOp
475 //* sendOp:: @cindex\s-+sendOp
476 //* sendOp1:: @cindex\s-+sendOp1
477 //* sendOp2:: @cindex\s-+sendOp2
478 //* sendOpV:: @cindex\s-+sendOpV
479 //* sendOpNV:: @cindex\s-+sendOpNV
480 //* sendOpN:: @cindex\s-+sendOpN
481 //* waitForPEOp:: @cindex\s-+waitForPEOp
482 //* processUnexpectedMessage:: @cindex\s-+processUnexpectedMessage
483 //* getOpcode:: @cindex\s-+getOpcode
484 //* getOpcodeAndSender:: @cindex\s-+getOpcodeAndSender
485 //* senderTask:: @cindex\s-+senderTask
486 //* startUpPE:: @cindex\s-+startUpPE
487 //* shutDownPE:: @cindex\s-+shutDownPE