[project @ 1996-07-19 18:36:04 by partain]
[ghc-hetmet.git] / ghc / runtime / gum / SysMan.lc
1 %****************************************************************************
2 %
3 \section[Sysman.lc]{GUM Sysman Program}
4 %
5 %  (c) The Parade/AQUA Projects, Glasgow University, 1994-1995.
6 %      P. Trinder, November 30th. 1994.
7
8 %****************************************************************************
9
10 The Sysman task currently controls initiation, termination, of a
11 parallel Haskell program running under GUM. In the future it may
12 control global GC synchronisation and statistics gathering. Based on
13 K. Hammond's SysMan.lc in Graph for PVM. SysMan is unusual in that it
14 is not part of the executable produced by ghc: it is a free-standing
15 program that spawns PVM tasks (logical PEs) to evaluate the
16 program. After initialisation it runs in parallel with the PE tasks,
17 awaiting messages.
18
19 OK children, buckle down for some serious weirdness, it works like this ...
20
21 \begin{itemize}
22 \item The argument vector (argv) for SysMan has one the following 2 shapes:
23 \begin{verbatim}        
24 -------------------------------------------------------------------------------
25 | SysMan path | debug flag | pvm-executable path | Num. PEs | Program Args ...|
26 -------------------------------------------------------------------------------
27
28 -------------------------------------------------------------------
29 | SysMan path | pvm-executable path | Num. PEs | Program Args ... |
30 -------------------------------------------------------------------
31 \end{verbatim}
32 The "pvm-executable path" is an absolute path of where PVM stashes the
33 code for each PE. The arguments passed on to each PE-executable
34 spawned by PVM are:
35 \begin{verbatim}
36 -------------------------------
37 | Num. PEs | Program Args ... |
38 -------------------------------
39 \end{verbatim}
40 The arguments passed to the Main-thread PE-executable are
41 \begin{verbatim}
42 -------------------------------------------------------------------
43 | main flag | pvm-executable path | Num. PEs | Program Args ... |
44 -------------------------------------------------------------------
45 \end{verbatim}
46 \item SysMan's algorithm is as follows.
47 \begin{itemize}
48 \item use PVM to spawn (nPE-1) PVM tasks 
49 \item fork SysMan to create the main-thread PE. This permits the main-thread to 
50 read and write to stdin and stdout. 
51 \item Barrier-synchronise waiting for all of the PE-tasks to start.
52 \item Broadcast the SysMan task-id, so that the main thread knows it.
53 \item Wait for the Main-thread PE to send it's task-id.
54 \item Broadcast an array of the PE task-ids to all of the PE-tasks.
55 \item Enter a loop awaiting incoming messages, e.g. failure, Garbage-collection, 
56 termination.
57 \end{itemize}
58
59 The forked Main-thread algorithm, in SysMan, is as follows.
60 \begin{itemize}
61 \item disconnects from PVM.
62 \item sets a flag in argv to indicate that it is the main thread.
63 \item `exec's a copy of the pvm-executable (i.e. the program being run)
64 \end{itemize}
65
66 The pvm-executable run by each PE-task, is initialised as follows.
67 \begin{itemize}
68 \item Registers with PVM, obtaining a task-id.
69 \item Joins the barrier synchronisation awaiting the other PEs.
70 \item Receives and records the task-id of SysMan, for future use.
71 \item If the PE is the main thread it sends its task-id to SysMan.
72 \item Receives and records the array of task-ids of the other PEs.
73 \item Begins execution.
74 \end{itemize}
75
76 \end{itemize}
77 \begin{code}
78 #define NON_POSIX_SOURCE /* so says Solaris */
79
80 #include "rtsdefs.h"
81 #include "LLC.h"
82 \end{code}
83
84 The following definitions included so that SysMan can be linked with
85 Low Level Communications module (LLComms). They are not used in
86 SysMan.
87
88 \begin{code}
89 GLOBAL_TASK_ID mytid, SysManTask;
90 rtsBool IAmMainThread;
91 \end{code}
92
93
94 \begin{code}
95 static GLOBAL_TASK_ID gtids[MAX_PES];
96 static long PEbuffer[MAX_PES];
97 int nPEs = 0;
98 static GLOBAL_TASK_ID sysman_id, sender_id, mainThread_id;
99 static unsigned PEsTerminated = 0;
100 static rtsBool Finishing = rtsFalse;
101 \end{code}
102
103 \begin{code}
104 #define checkerr(c)     do {if((c)<0) { pvm_perror("Sysman"); EXIT(EXIT_FAILURE); }} while(0)
105 \end{code}
106
107 This Function not yet implemented for GUM 
108
109 \begin{code}
110 static void
111 DoGlobalGC(STG_NO_ARGS)
112 {}
113 /*
114 static void
115 HandleException(STG_NO_ARGS)
116 {}
117 */
118 \end{code}
119
120 \begin{code}
121 main(int argc, char **argv)
122 {
123     int rbufid;
124     int opcode, nbytes;
125     char **pargv;
126     int i, cc;
127     int spawn_flag = PvmTaskDefault;
128     PACKET addr;
129
130     char *petask, *pvmExecutable;
131
132     setbuf(stdout, NULL);
133     setbuf(stderr, NULL);
134
135     if (argc > 1) {
136         if (*argv[1] == '-') {
137             spawn_flag = PvmTaskDebug;
138             argv[1] = argv[0];
139             argv++; argc--;
140         }
141         sysman_id = pvm_mytid();/* This must be the first PVM call */
142
143         checkerr(sysman_id);
144
145         /* 
146         Get the full path and filename of the pvm executable (stashed in some
147         PVM directory.
148         */
149         pvmExecutable = argv[1];
150
151         nPEs = atoi(argv[2]);
152
153         if ((petask = getenv(PETASK)) == NULL)
154             petask = PETASK;
155
156 #if 0
157         fprintf(stderr, "nPEs (%s) = %d\n", petask, nPEs);
158 #endif
159
160         /* Check that we can create the number of PE and IMU tasks requested */
161         if (nPEs > MAX_PES) {
162             fprintf(stderr, "No more than %d PEs allowed (%d requested)\n", MAX_PES, nPEs);
163             EXIT(EXIT_FAILURE);
164         }
165         
166         /* 
167         Now create the PE Tasks. We spawn (nPEs-1) pvm threads: the Main Thread 
168         (which starts execution and performs IO) is created by forking SysMan 
169         */
170         nPEs--;
171         if (nPEs > 0) {
172             /* Initialise the PE task arguments from Sysman's arguments */
173             pargv = argv + 2;
174 #if 0
175             fprintf(stderr, "Spawning %d PEs(%s) ...\n", nPEs, petask);
176             fprintf(stderr, "  args: ");
177             for (i = 0; pargv[i]; ++i)
178                 fprintf(stderr, "%s, ", pargv[i]);
179             fprintf(stderr, "\n");
180 #endif
181             checkerr(pvm_spawn(petask, pargv, spawn_flag, "", nPEs, gtids));
182             /*
183              * Stash the task-ids of the PEs away in a buffer, once we know 
184              * the Main Thread's task-id, we'll broadcast them all.
185              */     
186             for (i = 0; i < nPEs; i++)
187                 PEbuffer[i+1] = (long) gtids[i];
188 #if 0
189             fprintf(stderr, "Spawned /* PWT */\n");
190 #endif
191         }
192
193         /* 
194         Create the MainThread PE by forking SysMan. This arcane coding 
195         is required to allow MainThread to read stdin and write to stdout.
196         PWT 18/1/96 
197         */
198         nPEs++;                         /* Record that the number of PEs is increasing */
199         if (cc = fork()) {
200             checkerr(cc);               /* Parent continues as SysMan */
201 #if 0
202             fprintf(stderr, "SysMan Task is [t%x]\n", sysman_id);
203 #endif
204             /*
205             SysMan joins PECTLGROUP, so that it can wait (at the
206             barrier sysnchronisation a few instructions later) for the
207             other PE-tasks to start.
208            
209             The manager group (MGRGROUP) is vestigial at the moment. It
210             may eventually include a statistics manager, and a (global) 
211             garbage collector manager.
212             */
213             checkerr(pvm_joingroup(PECTLGROUP));
214 #if 0
215             fprintf(stderr, "Joined PECTLGROUP /* PWT */\n");
216 #endif
217             /* Wait for all the PEs to arrive */
218             checkerr(pvm_barrier(PECTLGROUP, nPEs + 1));
219 #if 0
220             fprintf(stderr, "PECTLGROUP  barrier passed /* HWL */\n");
221 #endif
222             /* Broadcast SysMan's ID, so Main Thread PE knows it */
223             pvm_initsend(PvmDataDefault);
224             pvm_bcast(PEGROUP, PP_SYSMAN_TID);
225
226             /* Wait for Main Thread to identify itself*/
227             addr = WaitForPEOp(PP_MAIN_TASK, ANY_GLOBAL_TASK);
228             pvm_bufinfo(addr, &nbytes, &opcode, &mainThread_id );
229             PEbuffer[0] = mainThread_id;
230 #if 0
231             fprintf(stderr,"SysMan received Main Task = %x\n",mainThread_id); 
232 #endif      
233             /* Now that we have them all, broadcast Global Task Ids of all PEs */
234             pvm_initsend(PvmDataDefault);
235             PutArgs(PEbuffer, nPEs);
236             pvm_bcast(PEGROUP, PP_PETIDS);
237 #if 0
238             fprintf(stderr, "Sysman successfully initialized!\n");
239 #endif
240             /* Process incoming messages */
241             while (1) {
242                 if ((rbufid = pvm_recv(ANY_TASK, ANY_OPCODE)) < 0)
243                     pvm_perror("Sysman: Receiving Message");
244                 else {
245                     pvm_bufinfo(rbufid, &nbytes, &opcode, &sender_id);
246 #if 0
247                   fprintf(stderr, "HWL-DBG(SysMan; main loop): rbufid=%x, nbytes = %d, opcode = %x, sender_id = %x\n",
248                       rbufid, nbytes, opcode, sender_id);
249 #endif
250                   switch (opcode) {
251                     case PP_GC_INIT:
252                       /* This Function not yet implemented for GUM */
253                       fprintf(stderr, "Global GC from %x Not yet implemented for GUM!\n", sender_id);
254                       sync(PECTLGROUP, PP_FULL_SYSTEM);
255                       broadcast(PEGROUP, PP_GC_INIT);
256                       DoGlobalGC();
257 /*                    broadcast(PEGROUP, PP_INIT); */
258                       break;
259
260                     case PP_STATS_ON:
261                     case PP_STATS_OFF:
262                         /* This Function not yet implemented for GUM */
263                         break;
264
265                     case PP_FINISH:
266                         if (!Finishing) {
267                           fprintf(stderr, "\nFinish from %x\n", sender_id);
268                           Finishing = rtsTrue;
269                           pvm_initsend(PvmDataDefault);
270                           pvm_bcast(PEGROUP, PP_FINISH);
271                       } else {
272                           ++PEsTerminated;
273                       }
274                       if (PEsTerminated >= nPEs) {
275                           broadcast(PEGROUP, PP_FINISH);
276                           broadcast(MGRGROUP, PP_FINISH);
277                           pvm_lvgroup(PECTLGROUP);
278                           pvm_lvgroup(MGRGROUP);
279                           pvm_exit();
280                           EXIT(EXIT_SUCCESS);
281                       }
282                       break;
283
284                   case PP_FAIL:
285                       fprintf(stderr, "Fail from %x\n", sender_id);
286                       if (!Finishing) {
287                           Finishing = rtsTrue;
288                           broadcast(PEGROUP, PP_FAIL);
289                       }
290                       break;
291
292                   default:
293                       {
294 /*                        char *opname = GetOpName(opcode);
295                           fprintf(stderr,"Sysman: Unrecognised opcode %s (%x)\n",
296                                 opname,opcode); */
297                           fprintf(stderr, "Sysman: Unrecognised opcode (%x)\n",
298                                 opcode);
299                       }
300                       break;
301                   }     /* switch */
302               }         /* else */
303           }             /* while 1 */
304       }                 /* forked Sysman Process */
305       else {
306             pvmendtask();               /* Disconnect from PVM to avoid confusion: */
307                                         /* executable reconnects  */
308             *argv[0] = '-';             /* Flag that this is the Main Thread PE */
309             execv(pvmExecutable,argv);  /* Parent task becomes Main Thread PE */
310       }
311   }                     /* argc > 1 */  
312 }                       /* main */
313 \end{code}
314
315 @myexit@ for the system manager.
316
317 \begin{code}
318
319 void
320 myexit(n)
321 I_ n;
322 {
323 #ifdef exit
324 #undef exit
325 #endif
326     exit(n);
327 }
328
329 \end{code}