1 /* ----------------------------------------------------------------------------
2 * Time-stamp: <Wed Jan 12 2000 12:29:53 Stardate: [-30]4193.64 hwloidl>
3 * $Id: LLComms.c,v 1.2 2000/01/13 14:34:07 hwloidl Exp $
5 * GUM Low-Level Inter-Task Communication
7 * This module defines PVM Routines for PE-PE communication.
8 * P. Trinder, December 5th. 1994.
9 * Adapted for the new RTS
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 * waitForPEOp waits for a packet from global task {\em who} with the
277 * OpCode {\em op}. Other OpCodes are handled by processUnexpected.
279 //@cindex waitForPEOp
281 waitForPEOp(OpCode op, GlobalTaskId who)
286 GlobalTaskId sender_id;
290 IF_PAR_DEBUG(verbose,
291 fprintf(stderr,"waitForPEOp: op = %x (%s), who = %x\n",
292 op, getOpName(op), who));
294 while((p = pvm_recv(ANY_TASK,ANY_OPCODE)) < 0)
295 pvm_perror("waitForPEOp: Waiting for PEOp");
297 pvm_bufinfo( p, &nbytes, &opCode, &sender_id );
298 IF_PAR_DEBUG(verbose,
299 fprintf(stderr,"waitForPEOp: received: OpCode = %x, sender_id = %x",
300 opCode, getOpName(opCode), sender_id));
302 match = (op == ANY_OPCODE || op == opCode) &&
303 (who == ANY_TASK || who == sender_id);
308 /* Handle the unexpected OpCodes */
309 processUnexpected(p);
315 * processUnexpected processes unexpected messages. If the message is a
316 * FINISH it exits the prgram, and PVM gracefully
318 //@cindex processUnexpected
320 processUnexpected(rtsPacket packet)
322 OpCode opCode = getOpcode(packet);
324 IF_PAR_DEBUG(verbose,
325 GlobalTaskId sender = senderTask(packet);
326 fprintf(stderr,"== [%x] processUnexpected: Received %x (%s), sender %x\n",
327 mytid, opCode, getOpName(opCode), sender));
331 stg_exit(EXIT_SUCCESS);
334 /* Anything we're not prepared to deal with. Note that ALL OpCodes
335 are discarded during termination -- this helps prevent bizarre
338 if (!GlobalStopPending) {
339 GlobalTaskId errorTask;
342 getOpcodeAndSender(packet,&opCode,&errorTask);
343 fprintf(stderr,"Task %x: Unexpected OpCode %x from %x in processUnexpected",
344 mytid, opCode, errorTask );
346 stg_exit(EXIT_FAILURE);
353 getOpcode(rtsPacket p)
357 GlobalTaskId sender_id;
358 pvm_bufinfo(p, &nbytes, &OpCode, &sender_id);
362 //@cindex getOpcodeAndSender
364 getOpcodeAndSender(rtsPacket p, OpCode *opCodep, GlobalTaskId *senderIdp)
367 pvm_bufinfo(p, &nbytes, opCodep, senderIdp);
372 senderTask(rtsPacket p)
376 GlobalTaskId sender_id;
377 pvm_bufinfo(p, &nbytes, &opCode, &sender_id);
382 * PEStartUp does the low-level comms specific startup stuff for a
383 * PE. It initialises the comms system, joins the appropriate groups,
384 * synchronises with the other PEs. Receives and records in a global
385 * variable the task-id of SysMan. If this is the main thread (discovered
386 * in main.lc), identifies itself to SysMan. Finally it receives
387 * from SysMan an array of the Global Task Ids of each PE, which is
388 * returned as the value of the function.
397 long *buffer = (long *) stgMallocBytes(sizeof(long) * nPEs,
398 "PEStartUp (buffer)");
399 GlobalTaskId *thePEs = (GlobalTaskId *)
400 stgMallocBytes(sizeof(GlobalTaskId) * nPEs,
403 mytid = _my_gtid; /* Initialise PVM and get task id into global var.*/
405 IF_PAR_DEBUG(verbose,
406 fprintf(stderr,"== [%x] PEStartup: Task id = [%x], No. PEs = %d \n",
407 mytid, mytid, nPEs));
408 checkComms(pvm_joingroup(PEGROUP), "PEStartup");
409 IF_PAR_DEBUG(verbose,
410 fprintf(stderr,"== [%x] PEStartup: Joined PEGROUP\n", mytid));
411 checkComms(pvm_joingroup(PECTLGROUP), "PEStartup");
412 IF_PAR_DEBUG(verbose,
413 fprintf(stderr,"== [%x] PEStartup: Joined PECTLGROUP\n", mytid));
414 checkComms(pvm_barrier(PECTLGROUP, nPEs+1), "PEStartup");
415 IF_PAR_DEBUG(verbose,
416 fprintf(stderr,"== [%x] PEStartup, Passed PECTLGROUP barrier\n", mytid));
418 addr = waitForPEOp(PP_SYSMAN_TID, ANY_GLOBAL_TASK);
419 SysManTask = senderTask(addr);
420 if (IAmMainThread) { /* Main Thread Identifies itself to SysMan */
421 pvm_initsend(PvmDataDefault);
422 pvm_send(SysManTask, PP_MAIN_TASK);
424 IF_PAR_DEBUG(verbose,
425 fprintf(stderr,"== [%x] Thread waits for %s\n",
426 mytid, getOpName(PP_PETIDS)));
427 addr = waitForPEOp(PP_PETIDS, ANY_GLOBAL_TASK);
428 GetArgs(buffer, nPEs);
429 for (i = 0; i < nPEs; ++i) {
430 thePEs[i] = (GlobalTaskId) buffer[i];
431 IF_PAR_DEBUG(verbose,
432 fprintf(stderr,"== [%x] PEStartup: PEs[%d] = %x \n",
433 mytid, i, thePEs[i]));
440 * PEShutdown does the low-level comms-specific shutdown stuff for a
441 * single PE. It leaves the groups and then exits from pvm.
447 IF_PAR_DEBUG(verbose,
448 fprintf(stderr, "== [%x] PEshutdown\n", mytid));
450 checkComms(pvm_lvgroup(PEGROUP),"PEShutDown");
451 checkComms(pvm_lvgroup(PECTLGROUP),"PEShutDown");
452 checkComms(pvm_exit(),"PEShutDown");
455 #endif /* PAR -- whole file */
457 //@node Index, , Auxiliary functions, GUM Low-Level Inter-Task Communication
461 //* getOpName:: @cindex\s-+getOpName
462 //* traceSendOp:: @cindex\s-+traceSendOp
463 //* sendOp:: @cindex\s-+sendOp
464 //* sendOp1:: @cindex\s-+sendOp1
465 //* sendOp2:: @cindex\s-+sendOp2
466 //* sendOpV:: @cindex\s-+sendOpV
467 //* sendOpNV:: @cindex\s-+sendOpNV
468 //* sendOpN:: @cindex\s-+sendOpN
469 //* waitForPEOp:: @cindex\s-+waitForPEOp
470 //* processUnexpected:: @cindex\s-+processUnexpected
471 //* getOpcode:: @cindex\s-+getOpcode
472 //* getOpcodeAndSender:: @cindex\s-+getOpcodeAndSender
473 //* senderTask:: @cindex\s-+senderTask
474 //* startUpPE:: @cindex\s-+startUpPE
475 //* shutDownPE:: @cindex\s-+shutDownPE