50a6cd211a8367ddbc79ce27d508788417911462
[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         checkerr(sysman_id);
143
144         /* 
145         Get the full path and filename of the pvm executable (stashed in some
146         PVM directory.
147         */
148         pvmExecutable = argv[1];
149
150         nPEs = atoi(argv[2]);
151
152         if ((petask = getenv(PETASK)) == NULL)
153             petask = PETASK;
154
155 #if 0
156         fprintf(stderr, "nPEs (%s) = %d\n", petask, nPEs);
157 #endif
158
159         /* Check that we can create the number of PE and IMU tasks requested */
160         if (nPEs > MAX_PES) {
161             fprintf(stderr, "No more than %d PEs allowed (%d requested)\n", MAX_PES, nPEs);
162             EXIT(EXIT_FAILURE);
163         }
164         
165         /* 
166         Now create the PE Tasks. We spawn (nPEs-1) pvm threads: the Main Thread 
167         (which starts execution and performs IO) is created by forking SysMan 
168         */
169         if (nPEs > 0) {
170             nPEs--;
171             /* Initialise the PE task arguments from Sysman's arguments */
172             pargv = argv + 2;
173 #if 0
174             fprintf(stderr, "Spawning %d PEs(%s) ...\n", nPEs, petask);
175             fprintf(stderr, "  args: ");
176             for (i = 0; pargv[i]; ++i)
177                 fprintf(stderr, "%s, ", pargv[i]);
178             fprintf(stderr, "\n");
179 #endif
180             checkerr(pvm_spawn(petask, pargv, spawn_flag, "", nPEs, gtids));
181             /*
182              * Stash the task-ids of the PEs away in a buffer, once we know 
183              * the Main Thread's task-id, we'll broadcast them all.
184              */     
185             for (i = 0; i < nPEs; i++)
186                 PEbuffer[i+1] = (long) gtids[i];
187 #if 0
188             fprintf(stderr, "Spawned /* PWT */\n");
189 #endif
190         }
191
192         /* 
193         Create the MainThread PE by forking SysMan. This arcane coding 
194         is required to allow MainThread to read stdin and write to stdout.
195         PWT 18/1/96 
196         */
197         nPEs++;                         /* Record that the number of PEs is increasing */
198         if (cc = fork()) {
199             checkerr(cc);               /* Parent continues as SysMan */
200 #if 0
201             fprintf(stderr, "SysMan Task is [t%x]\n", sysman_id);
202 #endif
203             /*
204             SysMan joins PECTLGROUP, so that it can wait (at the
205             barrier sysnchronisation a few instructions later) for the
206             other PE-tasks to start.
207            
208             The manager group (MGRGROUP) is vestigial at the moment. It
209             may eventually include a statistics manager, and a (global) 
210             garbage collector manager.
211             */
212             checkerr(pvm_joingroup(PECTLGROUP));
213 #if 0
214             fprintf(stderr, "Joined PECTLGROUP /* PWT */\n");
215 #endif
216             /* Wait for all the PEs to arrive */
217             checkerr(pvm_barrier(PECTLGROUP, nPEs + 1));
218 #if 0
219             fprintf(stderr, "PECTLGROUP  barrier passed /* HWL */\n");
220 #endif
221             /* Broadcast SysMan's ID, so Main Thread PE knows it */
222             pvm_initsend(PvmDataDefault);
223             pvm_bcast(PEGROUP, PP_SYSMAN_TID);
224
225             /* Wait for Main Thread to identify itself*/
226             addr = WaitForPEOp(PP_MAIN_TASK, ANY_GLOBAL_TASK);
227             pvm_bufinfo(addr, &nbytes, &opcode, &mainThread_id );
228             PEbuffer[0] = mainThread_id;
229 #if 0
230             fprintf(stderr,"SysMan received Main Task = %x\n",mainThread_id); 
231 #endif      
232             /* Now that we have them all, broadcast Global Task Ids of all PEs */
233             pvm_initsend(PvmDataDefault);
234             PutArgs(PEbuffer, nPEs);
235             pvm_bcast(PEGROUP, PP_PETIDS);
236 #if 0
237             fprintf(stderr, "Sysman successfully initialized!\n");
238 #endif
239             /* Process incoming messages */
240             while (1) {
241                 if ((rbufid = pvm_recv(ANY_TASK, ANY_OPCODE)) < 0)
242                     pvm_perror("Sysman: Receiving Message");
243                 else {
244                     pvm_bufinfo(rbufid, &nbytes, &opcode, &sender_id);
245 #if 0
246                   fprintf(stderr, "HWL-DBG(SysMan; main loop): rbufid=%x, nbytes = %d, opcode = %x, sender_id = %x\n",
247                       rbufid, nbytes, opcode, sender_id);
248 #endif
249                   switch (opcode) {
250                     case PP_GC_INIT:
251                       /* This Function not yet implemented for GUM */
252                       fprintf(stderr, "Global GC from %x Not yet implemented for GUM!\n", sender_id);
253                       sync(PECTLGROUP, PP_FULL_SYSTEM);
254                       broadcast(PEGROUP, PP_GC_INIT);
255                       DoGlobalGC();
256 /*                    broadcast(PEGROUP, PP_INIT); */
257                       break;
258
259                     case PP_STATS_ON:
260                     case PP_STATS_OFF:
261                         /* This Function not yet implemented for GUM */
262                         break;
263
264                     case PP_FINISH:
265                         if (!Finishing) {
266                           fprintf(stderr, "\nFinish from %x\n", sender_id);
267                           Finishing = rtsTrue;
268                           pvm_initsend(PvmDataDefault);
269                           pvm_bcast(PEGROUP, PP_FINISH);
270                       } else {
271                           ++PEsTerminated;
272                       }
273                       if (PEsTerminated >= nPEs) {
274                           broadcast(PEGROUP, PP_FINISH);
275                           broadcast(MGRGROUP, PP_FINISH);
276                           pvm_lvgroup(PECTLGROUP);
277                           pvm_lvgroup(MGRGROUP);
278                           pvm_exit();
279                           EXIT(EXIT_SUCCESS);
280                       }
281                       break;
282
283                   case PP_FAIL:
284                       fprintf(stderr, "Fail from %x\n", sender_id);
285                       if (!Finishing) {
286                           Finishing = rtsTrue;
287                           broadcast(PEGROUP, PP_FAIL);
288                       }
289                       break;
290
291                   default:
292                       {
293 /*                        char *opname = GetOpName(opcode);
294                           fprintf(stderr,"Sysman: Unrecognised opcode %s (%x)\n",
295                                 opname,opcode); */
296                           fprintf(stderr, "Sysman: Unrecognised opcode (%x)\n",
297                                 opcode);
298                       }
299                       break;
300                   }     /* switch */
301               }         /* else */
302           }             /* while 1 */
303       }                 /* forked Sysman Process */
304       else {
305             pvmendtask();               /* Disconnect from PVM to avoid confusion: */
306                                         /* executable reconnects  */
307             *argv[0] = '-';             /* Flag that this is the Main Thread PE */
308             execv(pvmExecutable,argv);  /* Parent task becomes Main Thread PE */
309       }
310   }                     /* argc > 1 */  
311 }                       /* main */
312 \end{code}
313
314 @myexit@ for the system manager.
315
316 \begin{code}
317
318 void
319 myexit(n)
320 I_ n;
321 {
322 #ifdef exit
323 #undef exit
324 #endif
325     exit(n);
326 }
327
328 \end{code}