1 /* ----------------------------------------------------------------------------
2 * Time-stamp: <Mon Mar 19 2001 22:10:38 Stardate: [-30]6354.62 hwloidl>
4 * GUM Low-Level Inter-Task Communication
6 * This module defines PVM Routines for PE-PE communication.
8 * P. Trinder, December 5th. 1994.
9 * P. Trinder, July 1998
10 * H-W. Loidl, November 1999 -
11 --------------------------------------------------------------------------- */
13 #ifdef PAR /* whole file */
15 //@node GUM Low-Level Inter-Task Communication, , ,
16 //@section GUM Low-Level Inter-Task Communication
19 *This module defines the routines which communicate between PEs. The
20 *code is based on Kevin Hammond's GRIP RTS. (OpCodes.h defines
21 *PEOp1 etc. in terms of sendOp1 etc.).
29 *sendOpV & variable \\
30 *sendOpNV & variable+ vector \\
32 *First the standard include files.
38 //* Auxiliary functions::
42 //@node Macros etc, Includes, GUM Low-Level Inter-Task Communication, GUM Low-Level Inter-Task Communication
43 //@subsection Macros etc
45 /* Evidently not Posix */
46 /* #include "PosixSource.h" */
48 #define UNUSED /* nothing */
50 //@node Includes, Auxiliary functions, Macros etc, GUM Low-Level Inter-Task Communication
51 //@subsection Includes
57 #include "ParallelRts.h"
59 # include "ParallelDebug.h"
69 /* Cannot use std macro when compiling for SysMan */
70 /* debugging enabled */
71 // #define IF_PAR_DEBUG(c,s) { s; }
72 /* debugging disabled */
73 #define IF_PAR_DEBUG(c,s) /* nothing */
75 //@node Auxiliary functions, Index, Includes, GUM Low-Level Inter-Task Communication
76 //@subsection Auxiliary functions
79 * heapChkCounter tracks the number of heap checks since the last probe.
80 * Not currently used! We check for messages when a thread is resheduled.
82 int heapChkCounter = 0;
85 * Then some miscellaneous functions.
86 * getOpName returns the character-string name of any OpCode.
89 char *UserPEOpNames[] = { PEOP_NAMES };
95 if (op >= MIN_PEOPS && op <= MAX_PEOPS)
96 return (UserPEOpNames[op - MIN_PEOPS]);
98 return ("Unknown PE OpCode");
102 * traceSendOp handles the tracing of messages.
105 //@cindex traceSendOp
107 traceSendOp(OpCode op, GlobalTaskId dest UNUSED,
108 unsigned int data1 UNUSED, unsigned int data2 UNUSED)
112 OpName = getOpName(op);
114 fprintf(stderr," %s [%x,%x] sent from %x to %x",
115 OpName, data1, data2, mytid, dest));
119 * sendOp sends a 0-argument message with OpCode {\em op} to
120 * the global task {\em task}.
125 sendOp(OpCode op, GlobalTaskId task)
127 traceSendOp(op, task,0,0);
129 pvm_initsend(PvmDataRaw);
134 * sendOp1 sends a 1-argument message with OpCode {\em op}
135 * to the global task {\em task}.
140 sendOp1(OpCode op, GlobalTaskId task, StgWord arg1)
142 traceSendOp(op, task, arg1,0);
144 pvm_initsend(PvmDataRaw);
151 * sendOp2 is used by the FP code only.
156 sendOp2(OpCode op, GlobalTaskId task, StgWord arg1, StgWord arg2)
158 traceSendOp(op, task, arg1, arg2);
160 pvm_initsend(PvmDataRaw);
168 * sendOpV takes a variable number of arguments, as specified by {\em n}.
171 * sendOpV( PP_STATS, StatsTask, 3, start_time, stop_time, sparkcount);
176 sendOpV(OpCode op, GlobalTaskId task, int n, ...)
184 traceSendOp(op, task, 0, 0);
186 pvm_initsend(PvmDataRaw);
188 for (i = 0; i < n; ++i) {
189 arg = va_arg(ap, StgWord);
199 * sendOpNV takes a variable-size datablock, as specified by {\em
200 * nelem} and a variable number of arguments, as specified by {\em
201 * narg}. N.B. The datablock and the additional arguments are contiguous
202 * and are copied over together. For example,
204 * sendOpNV(PP_RESUME, tsoga.pe, 6, nelem, data,
205 * (W_) ga.weight, (W_) ga.loc.gc.gtid, (W_) ga.loc.gc.slot,
206 * (W_) tsoga.weight, (W_) tsoga.loc.gc.gtid, (W_) tsoga.loc.gc.slot);
208 * Important: The variable arguments must all be StgWords.
210 sendOpNV(_, tid, m, n, data, x1, ..., xm):
213 +------------------------------
214 | x1 | ... | xm | n | data ....
215 +------------------------------
220 sendOpNV(OpCode op, GlobalTaskId task, int nelem,
221 StgWord *datablock, int narg, ...)
229 traceSendOp(op, task, 0, 0);
231 fprintf(stderr,"~~ sendOpNV: op = %x (%s), task = %x, narg = %d, nelem = %d",
232 op, getOpName(op), task, narg, nelem));
234 pvm_initsend(PvmDataRaw);
236 for (i = 0; i < narg; ++i) {
237 arg = va_arg(ap, StgWord);
239 fprintf(stderr,"~~ sendOpNV: arg = %d\n",arg));
242 arg = (StgWord) nelem;
245 /* for (i=0; i < nelem; ++i) fprintf(stderr, "%d ",datablock[i]); */
246 /* fprintf(stderr," in sendOpNV\n");*/
248 PutArgs(datablock, nelem);
255 * sendOpN take a variable size array argument, whose size is given by
256 * {\em n}. For example,
258 * sendOpN( PP_STATS, StatsTask, 3, stats_array);
263 sendOpN(OpCode op, GlobalTaskId task, int n, StgPtr args)
267 traceSendOp(op, task, 0, 0);
269 pvm_initsend(PvmDataRaw);
277 * broadcastOpN is as sendOpN but broadcasts to all members of a group.
281 broadcastOpN(OpCode op, char *group, int n, StgPtr args)
285 //traceSendOp(op, task, 0, 0);
287 pvm_initsend(PvmDataRaw);
291 pvm_bcast(group, op);
295 waitForPEOp waits for a packet from global task who with the
296 OpCode op. If ignore is true all other messages are simply ignored;
297 otherwise they are handled by processUnexpected.
299 //@cindex waitForPEOp
301 waitForPEOp(OpCode op, GlobalTaskId who, void(*processUnexpected)(rtsPacket) )
306 GlobalTaskId sender_id;
309 IF_PAR_DEBUG(verbose,
310 fprintf(stderr,"~~ waitForPEOp: expecting op = %x (%s), who = [%x]\n",
311 op, getOpName(op), who));
314 while((p = pvm_recv(ANY_TASK,ANY_OPCODE)) < 0)
315 pvm_perror("waitForPEOp: Waiting for PEOp");
317 pvm_bufinfo( p, &nbytes, &opCode, &sender_id );
318 match = (op == ANY_OPCODE || op == opCode) &&
319 (who == ANY_TASK || who == sender_id);
322 IF_PAR_DEBUG(verbose,
324 "~~waitForPEOp: Qapla! received: OpCode = %#x (%s), sender_id = [%x]",
325 opCode, getOpName(opCode), sender_id));
330 /* Handle the unexpected OpCodes */
331 if (processUnexpected!=NULL) {
332 (*processUnexpected)(p);
334 IF_PAR_DEBUG(verbose,
336 "~~ waitForPEOp: ignoring OpCode = %#x (%s), sender_id = [%x]",
337 opCode, getOpName(opCode), sender_id));
344 processUnexpected processes unexpected messages. If the message is a
345 FINISH it exits the prgram, and PVM gracefully
347 //@cindex processUnexpectedMessage
349 processUnexpectedMessage(rtsPacket packet) {
350 OpCode opCode = getOpcode(packet);
352 IF_PAR_DEBUG(verbose,
353 GlobalTaskId sender = senderTask(packet);
354 fprintf(stderr,"~~ [%x] processUnexpected: Received %x (%s), sender %x\n",
355 mytid, opCode, getOpName(opCode), sender));
359 stg_exit(EXIT_SUCCESS);
362 /* Anything we're not prepared to deal with. Note that ALL OpCodes
363 are discarded during termination -- this helps prevent bizarre
366 // if (!GlobalStopPending)
368 GlobalTaskId errorTask;
371 getOpcodeAndSender(packet, &opCode, &errorTask);
372 fprintf(stderr,"== Task %x: Unexpected OpCode %x from %x in processUnexpected",
373 mytid, opCode, errorTask );
375 stg_exit(EXIT_FAILURE);
382 getOpcode(rtsPacket p)
386 GlobalTaskId sender_id;
387 /* read PVM buffer */
388 pvm_bufinfo(p, &nbytes, &OpCode, &sender_id);
389 /* return tag of the buffer as opcode */
393 //@cindex getOpcodeAndSender
395 getOpcodeAndSender(rtsPacket p, OpCode *opCodep, GlobalTaskId *senderIdp)
398 /* read PVM buffer */
399 pvm_bufinfo(p, &nbytes, opCodep, senderIdp);
404 senderTask(rtsPacket p)
408 GlobalTaskId sender_id;
409 /* read PVM buffer */
410 pvm_bufinfo(p, &nbytes, &opCode, &sender_id);
415 * startUpPE does the low-level comms specific startup stuff for a
416 * PE. It initialises the comms system, joins the appropriate groups
417 * allocates the PE buffer
424 mytid = _my_gtid; /* Initialise PVM and get task id into global var.*/
426 IF_PAR_DEBUG(verbose,
427 fprintf(stderr,"== [%x] PEStartup: Task id = [%x], No. PEs = %d \n",
428 mytid, mytid, nPEs));
429 checkComms(pvm_joingroup(PEGROUP), "PEStartup");
430 IF_PAR_DEBUG(verbose,
431 fprintf(stderr,"== [%x] PEStartup: Joined PEGROUP\n", mytid));
435 * PEShutdown does the low-level comms-specific shutdown stuff for a
436 * single PE. It leaves the groups and then exits from pvm.
442 IF_PAR_DEBUG(verbose,
443 fprintf(stderr, "== [%x] PEshutdown\n", mytid));
445 checkComms(pvm_lvgroup(PEGROUP),"PEShutDown");
446 checkComms(pvm_exit(),"PEShutDown");
450 Extract the exit code out of a PP_FINISH packet (used in SysMan)
453 getExitCode(int nbytes, GlobalTaskId *sender_idp) {
456 if (nbytes==4) { // Notification from a task doing pvm_exit
457 GetArgs(sender_idp,1); // Presumably this must be MainPE Id
459 } else if (nbytes==8) { // Doing a controlled shutdown
460 GetArgs(&exitCode,1); // HACK: controlled shutdown == 2 values
461 GetArgs(&exitCode,1);
463 exitCode = -2; // everything else
468 #endif /* PAR -- whole file */
470 //@node Index, , Auxiliary functions, GUM Low-Level Inter-Task Communication
474 //* getOpName:: @cindex\s-+getOpName
475 //* traceSendOp:: @cindex\s-+traceSendOp
476 //* sendOp:: @cindex\s-+sendOp
477 //* sendOp1:: @cindex\s-+sendOp1
478 //* sendOp2:: @cindex\s-+sendOp2
479 //* sendOpV:: @cindex\s-+sendOpV
480 //* sendOpNV:: @cindex\s-+sendOpNV
481 //* sendOpN:: @cindex\s-+sendOpN
482 //* waitForPEOp:: @cindex\s-+waitForPEOp
483 //* processUnexpectedMessage:: @cindex\s-+processUnexpectedMessage
484 //* getOpcode:: @cindex\s-+getOpcode
485 //* getOpcodeAndSender:: @cindex\s-+getOpcodeAndSender
486 //* senderTask:: @cindex\s-+senderTask
487 //* startUpPE:: @cindex\s-+startUpPE
488 //* shutDownPE:: @cindex\s-+shutDownPE