[project @ 1996-01-08 20:28:12 by partain]
[ghc-hetmet.git] / ghc / runtime / gum / LLComms.lc
diff --git a/ghc/runtime/gum/LLComms.lc b/ghc/runtime/gum/LLComms.lc
new file mode 100644 (file)
index 0000000..8839bde
--- /dev/null
@@ -0,0 +1,438 @@
+%****************************************************************************
+%
+\section[LLComms.lc]{GUM Low-Level Inter-Task Communication}
+%
+% This module defines PVM Routines for PE-PE  communication.
+%
+% (c) The Parade/AQUA Projects, Glasgow University, 1994-1995
+%     P. Trinder, December 5th. 1994.
+%
+%****************************************************************************
+
+
+\begin{code}
+#ifdef PAR /* whole file */
+\end{code}
+
+This module defines the routines which communicate between PEs.  The
+code is based on Kevin Hammond's GRIP RTS. (@Opcodes.h@ defines
+@PEOp1@ etc. in terms of @SendOp1@ etc.).  
+
+\begin{onlylatex}
+\begin{center}
+\end{onlylatex}
+\begin{tabular}{|l|l|} \hline
+Routine                &       Arguments \\ \hline
+               &               \\
+@SendOp@       &       0                       \\
+@SendOp1@      &       1                       \\
+@SendOp2@      &       2                       \\
+@SendOpN@      &       vector                  \\
+@SendOpV@      &       variable                \\
+@SendOpNV@     &       variable+ vector        \\
+\end{tabular}
+\begin{onlylatex}
+\end{center}
+\end{onlylatex}
+
+First the standard include files.
+
+\begin{code}
+#define NON_POSIX_SOURCE /* so says Solaris */
+
+#include "rtsdefs.h"
+
+#include "LLC.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+\end{code}
+
+Then some miscellaneous functions. 
+@GetOpName@ returns the character-string name of any opcode.
+
+\begin{code}
+char *UserPEOpNames[] = { PEOP_NAMES };
+
+char *
+GetOpName(op)
+unsigned op;
+{
+    if (op >= MIN_PEOPS && op <= MAX_PEOPS)
+       return (UserPEOpNames[op - MIN_PEOPS]);
+
+    else
+       return ("Unknown PE Opcode");
+}
+
+void NullException(STG_NO_ARGS)
+{
+  fprintf(stderr,"Null_Exception: called");
+}
+void (*ExceptionHandler)() = NullException;
+
+
+\end{code}
+
+@trace_SendOp@ handles the tracing of messages at the OS level.  If
+tracing is on (as specified by @PETrace@, @SystemTrace@ and
+@ReplyTrace@), then a message is printed.  The opcode and address word
+of the previous PE opcode is recorded in the variables @lastSendOp@ and
+@lastPEaddress@. @PElastop@ is a Boolean which records whether the
+last message sent was for a PE or an IMU.
+
+\begin{code}
+rtsBool PETrace = rtsFalse, IMUTrace = rtsFalse, SystemTrace = rtsFalse, ReplyTrace = rtsFalse;
+
+static void
+trace_SendOp(op, dest, data1, data2)
+OPCODE op;
+GLOBAL_TASK_ID dest;
+unsigned data1, data2;
+{
+    char *OpName;
+
+    if (!ReplyTrace && op == REPLY_OK)
+       return;
+
+    OpName = GetOpName(op);
+/*    fprintf(stderr, " %s [%x,%x] sent from %x to %x\n", OpName, data1, data2, mytid, dest);*/
+}
+
+\end{code}
+
+@SendOp@ sends a 0-argument message with opcode {\em op} to
+the global task {\em task}.
+
+\begin{code}
+void
+SendOp(op, task)
+OPCODE op;
+GLOBAL_TASK_ID task;
+{
+    trace_SendOp(op, task,0,0);
+
+    pvm_initsend(PvmDataRaw);
+    pvm_send( task, op );
+}
+\end{code}
+
+@SendOp1@ sends a 1-argument message with opcode {\em op}
+to the global task {\em task}.
+
+\begin{code}
+void
+SendOp1(op, task, arg1)
+OPCODE op;
+GLOBAL_TASK_ID task;
+StgWord arg1;
+{
+    trace_SendOp(op, task, arg1,0);
+
+    pvm_initsend(PvmDataRaw);
+    PutArg1(arg1);
+    pvm_send( task, op );
+}
+
+\end{code}
+
+@SendOp2@ is used by the FP code only. 
+
+\begin{code}
+void
+SendOp2(op, task, arg1, arg2)
+OPCODE op;
+GLOBAL_TASK_ID task;
+StgWord arg1;
+StgWord arg2;
+{
+    trace_SendOp(op, task, arg1, arg2);
+
+    pvm_initsend(PvmDataRaw);
+    PutArg1(arg1);
+    PutArg2(arg2);
+    pvm_send( task, op );
+}
+\end{code}
+
+@SendOpV@ takes a variable number of arguments, as specified by {\em n}.  
+For example,
+\begin{verbatim}
+    SendOpV( PP_STATS, StatsTask, 3, start_time, stop_time, sparkcount);
+\end{verbatim}
+
+\begin{code}
+
+#ifdef __STDC__
+void
+SendOpV(OPCODE op, GLOBAL_TASK_ID task, int n, ...)
+#else
+void
+SendOpV(op, task, n, va_alist)
+OPCODE op;
+GLOBAL_TASK_ID task;
+int n;
+va_dcl
+#endif
+{
+    va_list ap;
+    int i;
+    StgWord arg;
+
+#ifdef __STDC__
+    va_start(ap, n);
+#else
+    va_start(ap);
+#endif
+
+    trace_SendOp(op, task, 0, 0);
+
+    pvm_initsend(PvmDataRaw);
+
+    for (i = 0; i < n; ++i) {
+       arg = va_arg(ap, StgWord);
+       PutArgN(i, arg);
+    }
+    va_end(ap);
+
+    pvm_send(task, op);
+}
+\end{code}    
+
+@SendOpNV@ takes a variable-size datablock, as specified by {\em
+nelem} and a variable number of arguments, as specified by {\em
+narg}. N.B. The datablock and the additional arguments are contiguous
+and are copied over together.  For example,
+
+\begin{verbatim}
+        SendOpNV(PP_RESUME, tsoga.pe, 6, nelem, data,
+           (W_) ga.weight, (W_) ga.loc.gc.gtid, (W_) ga.loc.gc.slot, 
+           (W_) tsoga.weight, (W_) tsoga.loc.gc.gtid, (W_) tsoga.loc.gc.slot);
+\end{verbatim}
+
+Important: The variable arguments must all be StgWords.
+
+\begin{code}
+
+#ifdef __STDC__
+void
+SendOpNV(OPCODE op, GLOBAL_TASK_ID task, int nelem, StgWord *datablock, int narg, ...)
+#else
+void
+SendOpNV(op, task, nelem, datablock, narg, va_alist)
+OPCODE op;
+GLOBAL_TASK_ID task;
+int nelem;
+StgWord *datablock;
+int narg;
+va_dcl
+#endif
+{
+    va_list ap;
+    int i;
+    StgWord arg;
+
+#ifdef __STDC__
+    va_start(ap, narg);
+#else
+    va_start(ap);
+#endif
+
+    trace_SendOp(op, task, 0, 0);
+/*  fprintf(stderr,"SendOpNV: op = %x, task = %x, narg = %d, nelem = %d\n",op,task,narg,nelem); */
+
+    pvm_initsend(PvmDataRaw);
+
+    for (i = 0; i < narg; ++i) {
+       arg = va_arg(ap, StgWord);
+/*      fprintf(stderr,"SendOpNV: arg = %d\n",arg); */
+       PutArgN(i, arg);
+    }
+    arg = (StgWord) nelem;
+    PutArgN(narg, arg);
+
+/*  for (i=0; i < nelem; ++i) fprintf(stderr, "%d ",datablock[i]); */
+/*  fprintf(stderr," in SendOpNV\n");*/
+
+    PutArgs(datablock, nelem);
+    va_end(ap);
+
+    pvm_send(task, op);
+}
+\end{code}    
+
+
+@SendOpN@ take a variable size array argument, whose size is given by
+{\em n}.  For example,
+
+\begin{verbatim}
+    SendOpN( PP_STATS, StatsTask, 3, stats_array);
+\end{verbatim}
+
+\begin{code}
+
+void
+SendOpN(op, task, n, args)
+OPCODE op;
+GLOBAL_TASK_ID task;
+int n;
+StgWord *args;
+
+{
+    long arg;
+
+    trace_SendOp(op, task, 0, 0);
+
+    pvm_initsend(PvmDataRaw);
+    arg = (long) n;
+    PutArgN(0, arg);
+    PutArgs(args, n);
+    pvm_send(task, op);
+}
+\end{code}
+
+@WaitForPEOp@ waits for a packet from global task {\em who} with the
+opcode {\em op}.  Other opcodes are handled by the standard exception handler.
+
+\begin{code}
+PACKET WaitForPEOp(op, who)
+OPCODE op;
+GLOBAL_TASK_ID who;
+{
+  PACKET p;
+  int nbytes;
+  OPCODE opcode;
+  GLOBAL_TASK_ID sender_id;
+  rtsBool match;
+
+  do {
+/*    fprintf(stderr,"WaitForPEOp: op = %x, who = %x\n",op,who); */
+    while((p = pvm_recv(ANY_TASK,ANY_OPCODE)) < 0)
+      pvm_perror("WaitForPEOp: Waiting for PEOp");
+      
+    pvm_bufinfo( p, &nbytes, &opcode, &sender_id );
+
+    match = (op == ANY_OPCODE || op == opcode) && (who == ANY_TASK || who == sender_id);
+
+    if(match)
+      return(p);
+
+    /* Handle the unexpected opcodes */
+    HandleException(p);
+
+  } while(rtsTrue);
+}
+\end{code}
+
+\begin{code}
+
+OPCODE 
+Opcode(p)
+PACKET p;
+{
+  int nbytes;
+  OPCODE opcode;
+  GLOBAL_TASK_ID sender_id;
+  pvm_bufinfo( p, &nbytes, &opcode, &sender_id );
+  return(opcode);
+}
+
+GLOBAL_TASK_ID
+Sender_Task(p)
+PACKET p;
+{
+  int nbytes;
+  OPCODE opcode;
+  GLOBAL_TASK_ID sender_id;
+  pvm_bufinfo( p, &nbytes, &opcode, &sender_id );
+  return(sender_id);
+}
+
+void
+get_opcode_and_sender(p,popcode,psender_id)
+PACKET p;
+OPCODE *popcode;
+GLOBAL_TASK_ID *psender_id;
+{
+  int nbytes;
+  pvm_bufinfo( p, &nbytes, popcode, psender_id );
+}
+
+\end{code}
+
+@PEStartUp@ does the low-level comms specific startup stuff for a
+PE. It initialises the comms system, joins the appropriate groups,
+synchronises with the other PEs. Finally it receives from Control the
+array of Global Task Ids.
+
+\begin{code}
+
+static char *
+xmalloc(n)
+unsigned n;
+{
+    char *p = malloc(n);
+
+    if (p == NULL) {
+       fprintf(stderr, "Memory allocation of %u bytes failed\n", n);
+       EXIT(EXIT_FAILURE);
+    }
+    return p;
+}
+
+GLOBAL_TASK_ID *
+PEStartUp(nPEs)
+unsigned nPEs;
+{
+    int i;
+    PACKET addr;
+    long *buffer = (long *) xmalloc(sizeof(long) * nPEs);
+    GLOBAL_TASK_ID *PEs = (GLOBAL_TASK_ID *) xmalloc(sizeof(GLOBAL_TASK_ID) * nPEs);
+
+    mytid = _my_gtid;          /* Initialise PVM and get task id into global
+                                * variable */
+
+/*    fprintf(stderr,"PEStartup, No. PEs = %d \n", nPEs); */
+    checkComms(pvm_joingroup(PEGROUP), "PEStartup");
+/*    fprintf(stderr,"PEStartup, Joined PEGROUP\n"); */
+    checkComms(pvm_joingroup(PECTLGROUP), "PEStartup");
+/*    fprintf(stderr,"PEStartup, Joined PECTLGROUP\n"); */
+    checkComms(pvm_barrier(PECTLGROUP, nPEs + 1), "PEStartup");
+/*    fprintf(stderr,"PEStartup, Passed PECTLGROUP barrier\n"); */
+
+    addr = WaitForPEOp(PP_PETIDS, ANY_GLOBAL_TASK);
+    GetArgs(buffer, nPEs);
+    for (i = 0; i < nPEs; ++i) {
+       PEs[i] = (GLOBAL_TASK_ID) buffer[i];
+       /* fprintf(stderr,"PEs[%d] = %x \n", i, PEs[i]);  */
+    }
+    free(buffer);
+    return PEs;
+}
+\end{code}
+
+@PEShutdown@ does the low-level comms-specific shutdown stuff for a
+single PE. It leaves the groups and then exits from pvm.
+
+\begin{code}
+void
+PEShutDown(STG_NO_ARGS)
+{    
+     checkComms(pvm_lvgroup(PEGROUP),"PEShutDown");
+     checkComms(pvm_lvgroup(PECTLGROUP),"PEShutDown");
+     checkComms(pvm_exit(),"PEShutDown");
+}
+\end{code}
+
+@heapChkCounter@ tracks the number of heap checks since the last probe.
+Not currently used! We check for messages when a thread is resheduled.
+
+\begin{code}
+int heapChkCounter = 0;
+\end{code}
+
+\begin{code}
+#endif /* PAR -- whole file */
+\end{code}