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;
280 fprintf(stderr,"WaitForPEOp: op = %x, who = %x\n",op,who);
282 while((p = pvm_recv(ANY_TASK,ANY_OPCODE)) < 0)
283 pvm_perror("WaitForPEOp: Waiting for PEOp");
285 pvm_bufinfo( p, &nbytes, &opcode, &sender_id );
287 fprintf(stderr,"WaitForPEOp: received: opcode = %x, sender_id = %x\n",opcode,sender_id);
289 match = (op == ANY_OPCODE || op == opcode) && (who == ANY_TASK || who == sender_id);
294 /* Handle the unexpected opcodes */
309 GLOBAL_TASK_ID sender_id;
310 pvm_bufinfo( p, &nbytes, &opcode, &sender_id );
320 GLOBAL_TASK_ID sender_id;
321 pvm_bufinfo( p, &nbytes, &opcode, &sender_id );
326 get_opcode_and_sender(p,popcode,psender_id)
329 GLOBAL_TASK_ID *psender_id;
332 pvm_bufinfo( p, &nbytes, popcode, psender_id );
337 @PEStartUp@ does the low-level comms specific startup stuff for a
338 PE. It initialises the comms system, joins the appropriate groups,
339 synchronises with the other PEs. Receives and records in a global
340 variable the task-id of SysMan. If this is the main thread (discovered
341 in main.lc), identifies itself to SysMan. Finally it receives
342 from SysMan an array of the Global Task Ids of each PE, which is
343 returned as the value of the function.
352 long *buffer = (long *) stgMallocBytes(sizeof(long) * nPEs, "PEStartUp (buffer)");
354 = (GLOBAL_TASK_ID *) stgMallocBytes(sizeof(GLOBAL_TASK_ID) * nPEs, "PEStartUp (PEs)");
356 mytid = _my_gtid; /* Initialise PVM and get task id into global var.*/
358 /* fprintf(stderr,"PEStartup, Task id = [%x], No. PEs = %d \n", mytid, nPEs); */
359 checkComms(pvm_joingroup(PEGROUP), "PEStartup");
360 /* fprintf(stderr,"PEStartup, Joined PEGROUP\n"); */
361 checkComms(pvm_joingroup(PECTLGROUP), "PEStartup");
362 /* fprintf(stderr,"PEStartup, Joined PECTLGROUP\n"); */
363 checkComms(pvm_barrier(PECTLGROUP, nPEs+1), "PEStartup");
364 /* fprintf(stderr,"PEStartup, Passed PECTLGROUP barrier\n"); */
366 addr = WaitForPEOp(PP_SYSMAN_TID, ANY_GLOBAL_TASK);
367 SysManTask = Sender_Task(addr);
368 if (IAmMainThread) { /* Main Thread Identifies itself to SysMan */
369 pvm_initsend(PvmDataDefault);
370 pvm_send(SysManTask, PP_MAIN_TASK);
372 addr = WaitForPEOp(PP_PETIDS, ANY_GLOBAL_TASK);
373 GetArgs(buffer, nPEs);
374 for (i = 0; i < nPEs; ++i) {
375 PEs[i] = (GLOBAL_TASK_ID) buffer[i];
377 fprintf(stderr,"PEs[%d] = %x \n", i, PEs[i]);
385 @PEShutdown@ does the low-level comms-specific shutdown stuff for a
386 single PE. It leaves the groups and then exits from pvm.
390 PEShutDown(STG_NO_ARGS)
392 checkComms(pvm_lvgroup(PEGROUP),"PEShutDown");
393 checkComms(pvm_lvgroup(PECTLGROUP),"PEShutDown");
394 checkComms(pvm_exit(),"PEShutDown");
398 @heapChkCounter@ tracks the number of heap checks since the last probe.
399 Not currently used! We check for messages when a thread is resheduled.
402 int heapChkCounter = 0;
406 #endif /* PAR -- whole file */