1 %****************************************************************************
3 \section[Sysman.lc]{GUM Sysman Program}
5 % (c) The Parade/AQUA Projects, Glasgow University, 1994-1995.
6 % P. Trinder, November 30th. 1994.
8 %****************************************************************************
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,
19 OK children, buckle down for some serious weirdness, it works like this ...
22 \item The argument vector (argv) for SysMan has one the following 2 shapes:
24 -------------------------------------------------------------------------------
25 | SysMan path | debug flag | pvm-executable path | Num. PEs | Program Args ...|
26 -------------------------------------------------------------------------------
28 -------------------------------------------------------------------
29 | SysMan path | pvm-executable path | Num. PEs | Program Args ... |
30 -------------------------------------------------------------------
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
36 -------------------------------
37 | Num. PEs | Program Args ... |
38 -------------------------------
40 The arguments passed to the Main-thread PE-executable are
42 -------------------------------------------------------------------
43 | main flag | pvm-executable path | Num. PEs | Program Args ... |
44 -------------------------------------------------------------------
46 \item SysMan's algorithm is as follows.
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,
59 The forked Main-thread algorithm, in SysMan, is as follows.
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)
66 The pvm-executable run by each PE-task, is initialised as follows.
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.
78 #define NON_POSIX_SOURCE /* so says Solaris */
84 The following definitions included so that SysMan can be linked with
85 Low Level Communications module (LLComms). They are not used in
89 GLOBAL_TASK_ID mytid, SysManTask;
90 rtsBool IAmMainThread;
95 static GLOBAL_TASK_ID gtids[MAX_PES];
96 static long PEbuffer[MAX_PES];
98 static GLOBAL_TASK_ID sysman_id, sender_id, mainThread_id;
99 static unsigned PEsTerminated = 0;
100 static rtsBool Finishing = rtsFalse;
104 #define checkerr(c) do {if((c)<0) { pvm_perror("Sysman"); EXIT(EXIT_FAILURE); }} while(0)
107 This Function not yet implemented for GUM
111 DoGlobalGC(STG_NO_ARGS)
115 HandleException(PACKET p)
121 main(int argc, char **argv)
127 int spawn_flag = PvmTaskDefault;
130 char *petask, *pvmExecutable;
132 setbuf(stdout, NULL);
133 setbuf(stderr, NULL);
136 if (*argv[1] == '-') {
137 spawn_flag = PvmTaskDebug;
141 sysman_id = pvm_mytid();/* This must be the first PVM call */
146 Get the full path and filename of the pvm executable (stashed in some
149 pvmExecutable = argv[1];
151 nPEs = atoi(argv[2]);
153 if ((petask = getenv(PETASK)) == NULL)
157 fprintf(stderr, "nPEs (%s) = %d\n", petask, nPEs);
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);
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
172 /* Initialise the PE task arguments from Sysman's arguments */
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");
181 checkerr(pvm_spawn(petask, pargv, spawn_flag, "", nPEs, gtids));
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.
186 for (i = 0; i < nPEs; i++)
187 PEbuffer[i+1] = (long) gtids[i];
189 fprintf(stderr, "Spawned /* PWT */\n");
194 Create the MainThread PE by forking SysMan. This arcane coding
195 is required to allow MainThread to read stdin and write to stdout.
198 nPEs++; /* Record that the number of PEs is increasing */
200 checkerr(cc); /* Parent continues as SysMan */
202 fprintf(stderr, "SysMan Task is [t%x]\n", sysman_id);
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.
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.
213 checkerr(pvm_joingroup(PECTLGROUP));
215 fprintf(stderr, "Joined PECTLGROUP /* PWT */\n");
217 /* Wait for all the PEs to arrive */
218 checkerr(pvm_barrier(PECTLGROUP, nPEs + 1));
220 fprintf(stderr, "PECTLGROUP barrier passed /* HWL */\n");
222 /* Broadcast SysMan's ID, so Main Thread PE knows it */
223 pvm_initsend(PvmDataDefault);
224 pvm_bcast(PEGROUP, PP_SYSMAN_TID);
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;
231 fprintf(stderr,"SysMan received Main Task = %x\n",mainThread_id);
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);
238 fprintf(stderr, "Sysman successfully initialized!\n");
240 /* Process incoming messages */
242 if ((rbufid = pvm_recv(ANY_TASK, ANY_OPCODE)) < 0)
243 pvm_perror("Sysman: Receiving Message");
245 pvm_bufinfo(rbufid, &nbytes, &opcode, &sender_id);
247 fprintf(stderr, "HWL-DBG(SysMan; main loop): rbufid=%x, nbytes = %d, opcode = %x, sender_id = %x\n",
248 rbufid, nbytes, opcode, sender_id);
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);
257 /* broadcast(PEGROUP, PP_INIT); */
262 /* This Function not yet implemented for GUM */
267 fprintf(stderr, "\nFinish from %x\n", sender_id);
269 pvm_initsend(PvmDataDefault);
270 pvm_bcast(PEGROUP, PP_FINISH);
274 if (PEsTerminated >= nPEs) {
275 broadcast(PEGROUP, PP_FINISH);
276 broadcast(MGRGROUP, PP_FINISH);
277 pvm_lvgroup(PECTLGROUP);
278 pvm_lvgroup(MGRGROUP);
285 fprintf(stderr, "Fail from %x\n", sender_id);
288 broadcast(PEGROUP, PP_FAIL);
294 /* char *opname = GetOpName(opcode);
295 fprintf(stderr,"Sysman: Unrecognised opcode %s (%x)\n",
297 fprintf(stderr, "Sysman: Unrecognised opcode (%x)\n",
304 } /* forked Sysman Process */
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 */
315 @myexit@ for the system manager.