1 %****************************************************************************
3 \section[LLComms.lc]{GUM Low-Level Inter-Task Communication}
5 % This module defines PVM Routines for PE-PE communication.
7 % (c) The Parade/AQUA Projects, Glasgow University, 1994-1995
8 % P. Trinder, December 5th. 1994.
10 %****************************************************************************
14 #ifdef PAR /* whole file */
17 This module defines the routines which communicate between PEs. The
18 code is based on Kevin Hammond's GRIP RTS. (@Opcodes.h@ defines
19 @PEOp1@ etc. in terms of @SendOp1@ etc.).
24 \begin{tabular}{|l|l|} \hline
25 Routine & Arguments \\ \hline
31 @SendOpV@ & variable \\
32 @SendOpNV@ & variable+ vector \\
38 First the standard include files.
41 #define NON_POSIX_SOURCE /* so says Solaris */
53 Then some miscellaneous functions.
54 @GetOpName@ returns the character-string name of any opcode.
57 char *UserPEOpNames[] = { PEOP_NAMES };
63 if (op >= MIN_PEOPS && op <= MAX_PEOPS)
64 return (UserPEOpNames[op - MIN_PEOPS]);
67 return ("Unknown PE Opcode");
71 NullException(STG_NO_ARGS)
73 fprintf(stderr,"Null_Exception: called");
76 void (*ExceptionHandler)() = NullException;
79 @trace_SendOp@ handles the tracing of messages at the OS level. If
80 tracing is on (as specified by @PETrace@, @SystemTrace@ and
81 @ReplyTrace@), then a message is printed. The opcode and address word
82 of the previous PE opcode is recorded in the variables @lastSendOp@ and
83 @lastPEaddress@. @PElastop@ is a Boolean which records whether the
84 last message sent was for a PE or an IMU.
87 rtsBool PETrace = rtsFalse, IMUTrace = rtsFalse, SystemTrace = rtsFalse, ReplyTrace = rtsFalse;
90 trace_SendOp(OPCODE op, GLOBAL_TASK_ID dest, unsigned int data1, unsigned int data2)
94 if (!ReplyTrace && op == REPLY_OK)
97 OpName = GetOpName(op);
98 /* fprintf(stderr, " %s [%x,%x] sent from %x to %x\n", OpName, data1, data2, mytid, dest);*/
103 @SendOp@ sends a 0-argument message with opcode {\em op} to
104 the global task {\em task}.
112 trace_SendOp(op, task,0,0);
114 pvm_initsend(PvmDataRaw);
115 pvm_send( task, op );
119 @SendOp1@ sends a 1-argument message with opcode {\em op}
120 to the global task {\em task}.
124 SendOp1(op, task, arg1)
129 trace_SendOp(op, task, arg1,0);
131 pvm_initsend(PvmDataRaw);
133 pvm_send( task, op );
138 @SendOp2@ is used by the FP code only.
142 SendOp2(op, task, arg1, arg2)
148 trace_SendOp(op, task, arg1, arg2);
150 pvm_initsend(PvmDataRaw);
153 pvm_send( task, op );
157 @SendOpV@ takes a variable number of arguments, as specified by {\em n}.
160 SendOpV( PP_STATS, StatsTask, 3, start_time, stop_time, sparkcount);
165 SendOpV(OPCODE op, GLOBAL_TASK_ID task, int n, ...)
173 trace_SendOp(op, task, 0, 0);
175 pvm_initsend(PvmDataRaw);
177 for (i = 0; i < n; ++i) {
178 arg = va_arg(ap, StgWord);
187 @SendOpNV@ takes a variable-size datablock, as specified by {\em
188 nelem} and a variable number of arguments, as specified by {\em
189 narg}. N.B. The datablock and the additional arguments are contiguous
190 and are copied over together. For example,
193 SendOpNV(PP_RESUME, tsoga.pe, 6, nelem, data,
194 (W_) ga.weight, (W_) ga.loc.gc.gtid, (W_) ga.loc.gc.slot,
195 (W_) tsoga.weight, (W_) tsoga.loc.gc.gtid, (W_) tsoga.loc.gc.slot);
198 Important: The variable arguments must all be StgWords.
203 SendOpNV(OPCODE op, GLOBAL_TASK_ID task, int nelem, StgWord *datablock, int narg, ...)
211 trace_SendOp(op, task, 0, 0);
212 /* fprintf(stderr,"SendOpNV: op = %x, task = %x, narg = %d, nelem = %d\n",op,task,narg,nelem); */
214 pvm_initsend(PvmDataRaw);
216 for (i = 0; i < narg; ++i) {
217 arg = va_arg(ap, StgWord);
218 /* fprintf(stderr,"SendOpNV: arg = %d\n",arg); */
221 arg = (StgWord) nelem;
224 /* for (i=0; i < nelem; ++i) fprintf(stderr, "%d ",datablock[i]); */
225 /* fprintf(stderr," in SendOpNV\n");*/
227 PutArgs(datablock, nelem);
235 @SendOpN@ take a variable size array argument, whose size is given by
236 {\em n}. For example,
239 SendOpN( PP_STATS, StatsTask, 3, stats_array);
245 SendOpN(op, task, n, args)
254 trace_SendOp(op, task, 0, 0);
256 pvm_initsend(PvmDataRaw);
264 @WaitForPEOp@ waits for a packet from global task {\em who} with the
265 opcode {\em op}. Other opcodes are handled by the standard exception handler.
268 PACKET WaitForPEOp(op, who)
275 GLOBAL_TASK_ID sender_id;
279 fprintf(stderr,"WaitForPEOp: op = %x, who = %x\n",op,who);
280 while((p = pvm_recv(ANY_TASK,ANY_OPCODE)) < 0)
281 pvm_perror("WaitForPEOp: Waiting for PEOp");
283 pvm_bufinfo( p, &nbytes, &opcode, &sender_id );
284 fprintf(stderr,"WaitForPEOp: received: opcode = %x, sender_id = %x\n",opcode,sender_id);
286 match = (op == ANY_OPCODE || op == opcode) && (who == ANY_TASK || who == sender_id);
291 /* Handle the unexpected opcodes */
306 GLOBAL_TASK_ID sender_id;
307 pvm_bufinfo( p, &nbytes, &opcode, &sender_id );
317 GLOBAL_TASK_ID sender_id;
318 pvm_bufinfo( p, &nbytes, &opcode, &sender_id );
323 get_opcode_and_sender(p,popcode,psender_id)
326 GLOBAL_TASK_ID *psender_id;
329 pvm_bufinfo( p, &nbytes, popcode, psender_id );
334 @PEStartUp@ does the low-level comms specific startup stuff for a
335 PE. It initialises the comms system, joins the appropriate groups,
336 synchronises with the other PEs. Finally it receives from Control the
337 array of Global Task Ids.
346 long *buffer = (long *) stgMallocBytes(sizeof(long) * nPEs, "PEStartUp (buffer)");
348 = (GLOBAL_TASK_ID *) stgMallocBytes(sizeof(GLOBAL_TASK_ID) * nPEs, "PEStartUp (PEs)");
350 mytid = _my_gtid; /* Initialise PVM and get task id into global var.*/
352 fprintf(stderr,"PEStartup, Task id = [%x], No. PEs = %d \n", mytid, nPEs);
353 checkComms(pvm_joingroup(PEGROUP), "PEStartup");
354 fprintf(stderr,"PEStartup, Joined PEGROUP\n");
355 checkComms(pvm_joingroup(PECTLGROUP), "PEStartup");
356 fprintf(stderr,"PEStartup, Joined PECTLGROUP\n");
357 checkComms(pvm_barrier(PECTLGROUP, nPEs+1), "PEStartup");
358 fprintf(stderr,"PEStartup, Passed PECTLGROUP barrier\n");
360 addr = WaitForPEOp(PP_SYSMAN_TID, ANY_GLOBAL_TASK);
361 SysManTask = Sender_Task(addr);
362 if (IAmMainThread) { /* Main Thread Identifies itself to SysMan */
363 pvm_initsend(PvmDataDefault);
364 pvm_send(SysManTask, PP_MAIN_TASK);
366 addr = WaitForPEOp(PP_PETIDS, ANY_GLOBAL_TASK);
367 GetArgs(buffer, nPEs);
369 for (i = 0; i < nPEs; ++i) {
370 PEs[i] = (GLOBAL_TASK_ID) buffer[i];
371 fprintf(stderr,"PEs[%d] = %x \n", i, PEs[i]);
378 @PEShutdown@ does the low-level comms-specific shutdown stuff for a
379 single PE. It leaves the groups and then exits from pvm.
383 PEShutDown(STG_NO_ARGS)
385 checkComms(pvm_lvgroup(PEGROUP),"PEShutDown");
386 checkComms(pvm_lvgroup(PECTLGROUP),"PEShutDown");
387 checkComms(pvm_exit(),"PEShutDown");
391 @heapChkCounter@ tracks the number of heap checks since the last probe.
392 Not currently used! We check for messages when a thread is resheduled.
395 int heapChkCounter = 0;
399 #endif /* PAR -- whole file */