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 controls initiation, termination, global GC
11 synchronisation and statistics gathering of a parallel Haskell program
12 running under GUM. Based on K. Hammond's SysMan.lc in Graph for
13 PVM. SysMan is unusual in that it is not part of the executable
14 produced by ghc: it is a free-standing program that spawns PVM tasks
15 (logical PEs) to evaluate the program.
17 OK children, buckle down for some serious wierdness, it works like this.
20 \item The argument vector (argv) for SysMan has one the following 2 shapes:
22 -------------------------------------------------------------------------------
23 | SysMan path | debug flag | pvm-executable path | Num. PEs | Program Args ...|
24 -------------------------------------------------------------------------------
26 -------------------------------------------------------------------
27 | SysMan path | pvm-executable path | Num. PEs | Program Args ... |
28 -------------------------------------------------------------------
30 The "pvm-executable path" is an absolute path of where PVM stashes the
31 code for each PE. The arguments passed on to each PE-executable
34 -------------------------------
35 | Num. PEs | Program Args ... |
36 -------------------------------
38 The arguments passed to the Main-thread PE-executable are
40 -------------------------------------------------------------------
41 | main flag | pvm-executable path | Num. PEs | Program Args ... |
42 -------------------------------------------------------------------
44 \item SysMan's algorithm is as follows.
46 \item use PVM to spawn (nPE-1) PVM tasks
47 \item fork SysMan to create the main-thread PE. This permits the main-thread to
48 read and write to stdin and stdout.
49 \item Barrier-synchronise waiting for all of the PE-tasks to start.
50 \item Broadcast the SysMan task-id, so that the main thread knows it.
51 \item Wait for the Main-thread PE to send it's task-id.
52 \item Broadcast an array of the PE task-ids to all of the PE-tasks.
53 \item Enter a loop awaiting incoming messages, e.g. failure, Garbage-collection,
57 The forked Main-thread algorithm, in SysMan, is as follows.
59 \item disconnects from PVM.
60 \item sets a flag in argv to indicate that it is the main thread.
61 \item `exec's a copy of the pvm-executable (i.e. the program being run)
64 The pvm-executable run by each PE-task, is initialised as follows.
66 \item Registers with PVM, obtaining a task-id.
67 \item Joins the barrier synchronisation awaiting the other PEs.
68 \item Receives and records the task-id of SysMan, for future use.
69 \item If the PE is the main thread it sends its task-id to SysMan.
70 \item Receives and records the array of task-ids of the other PEs.
71 \item Begins execution
76 #define NON_POSIX_SOURCE /* so says Solaris */
82 The following definitions included so that SysMan can be linked with
83 Low Level Communications module (LLComms). They are not used in
87 GLOBAL_TASK_ID mytid, SysManTask;
88 rtsBool IAmMainThread;
93 static GLOBAL_TASK_ID gtids[MAX_PES], StatsTask = 0;
94 static long PEbuffer[MAX_PES];
96 static GLOBAL_TASK_ID sysman_id, sender_id, mainThread_id;
97 static unsigned PEsTerminated = 0;
98 static rtsBool Finishing = rtsFalse;
102 #define checkerr(c) do {if((c)<0) { pvm_perror("Sysman"); EXIT(EXIT_FAILURE); }} while(0)
105 This Function not yet implemented for GUM
109 DoGlobalGC(STG_NO_ARGS)
113 HandleException(STG_NO_ARGS)
119 main(int argc, char **argv)
125 int spawn_flag = PvmTaskDefault;
128 char *petask, *pvmExecutable;
130 setbuf(stdout, NULL);
131 setbuf(stderr, NULL);
134 if (*argv[1] == '-') {
135 spawn_flag = PvmTaskDebug;
139 sysman_id = pvm_mytid();/* This must be the first PVM call */
143 Get the full path and filename of the pvm executable (stashed in some
146 pvmExecutable = argv[1];
148 nPEs = atoi(argv[2]);
150 if ((petask = getenv(PETASK)) == NULL)
154 fprintf(stderr, "nPEs (%s) = %d\n", petask, nPEs);
157 /* Check that we can create the number of PE and IMU tasks requested */
158 if (nPEs > MAX_PES) {
159 fprintf(stderr, "No more than %d PEs allowed (%d requested)\n", MAX_PES, nPEs);
164 Now create the PE Tasks. We spawn (nPEs-1) pvm threads: the Main Thread
165 (which starts execution and performs IO) is created by forking SysMan
169 /* Initialise the PE task arguments from Sysman's arguments */
172 fprintf(stderr, "Spawning %d PEs(%s) ...\n", nPEs, petask);
173 fprintf(stderr, " args: ");
174 for (i = 0; pargv[i]; ++i)
175 fprintf(stderr, "%s, ", pargv[i]);
176 fprintf(stderr, "\n");
178 checkerr(pvm_spawn(petask, pargv, spawn_flag, "", nPEs, gtids));
180 * Stash the task-ids of the PEs away in a buffer, once we know
181 * the Main Thread's task-id, we'll broadcast them all.
183 for (i = 0; i < nPEs; i++)
184 PEbuffer[i+1] = (long) gtids[i];
186 fprintf(stderr, "Spawned /* PWT */\n");
191 Create the MainThread PE by forking SysMan. This arcane coding
192 is required to allow MainThread to read stdin and write to stdout.
195 nPEs++; /* Record that the number of PEs is increasing */
197 checkerr(cc); /* Parent continues as SysMan */
199 fprintf(stderr, "SysMan Task is [t%x]\n", sysman_id);
202 SysMan joins PECTLGROUP, so that it can wait (at the
203 barrier sysnchronisation a few instructions later) for the
204 other PE-tasks to start.
206 The manager group (MGRGROUP) is vestigial at the moment. It
207 may eventually include a statistics manager, and a (global)
208 garbage collector manager.
210 checkerr(pvm_joingroup(PECTLGROUP));
212 fprintf(stderr, "Joined PECTLGROUP /* PWT */\n");
214 /* Wait for all the PEs to arrive */
215 checkerr(pvm_barrier(PECTLGROUP, nPEs + 1));
217 fprintf(stderr, "PECTLGROUP barrier passed /* HWL */\n");
219 /* Broadcast SysMan's ID, so Main Thread PE knows it */
220 pvm_initsend(PvmDataDefault);
221 pvm_bcast(PEGROUP, PP_SYSMAN_TID);
223 /* Wait for Main Thread to identify itself*/
224 addr = WaitForPEOp(PP_MAIN_TASK, ANY_GLOBAL_TASK);
225 pvm_bufinfo(addr, &nbytes, &opcode, &mainThread_id );
226 PEbuffer[0] = mainThread_id;
228 fprintf(stderr,"SysMan received Main Task = %x\n",mainThread_id);
230 /* Now that we have them all, broadcast Global Task Ids of all PEs */
231 pvm_initsend(PvmDataDefault);
232 PutArgs(PEbuffer, nPEs);
233 pvm_bcast(PEGROUP, PP_PETIDS);
235 fprintf(stderr, "Sysman successfully initialized!\n");
237 /* Process incoming messages */
239 if ((rbufid = pvm_recv(ANY_TASK, ANY_OPCODE)) < 0)
240 pvm_perror("Sysman: Receiving Message");
242 pvm_bufinfo(rbufid, &nbytes, &opcode, &sender_id);
244 fprintf(stderr, "HWL-DBG(SysMan; main loop): rbufid=%x, nbytes = %d, opcode = %x, sender_id = %x\n",
245 rbufid, nbytes, opcode, sender_id);
249 /* This Function not yet implemented for GUM */
250 fprintf(stderr, "Global GC from %x Not yet implemented for GUM!\n", sender_id);
251 sync(PECTLGROUP, PP_FULL_SYSTEM);
252 broadcast(PEGROUP, PP_GC_INIT);
254 /* broadcast(PEGROUP, PP_INIT); */
259 /* This Function not yet implemented for GUM */
263 fprintf(stderr, "Finish from %x\n", sender_id);
265 long buf = (long) StatsTask;
267 pvm_initsend(PvmDataDefault);
268 pvm_pklong(&buf, 1, 1);
269 pvm_bcast(PEGROUP, PP_FINISH);
273 if (PEsTerminated >= nPEs) {
274 broadcast(PEGROUP, PP_FINISH);
275 broadcast(MGRGROUP, PP_FINISH);
276 pvm_lvgroup(PECTLGROUP);
277 pvm_lvgroup(MGRGROUP);
284 fprintf(stderr, "Fail from %x\n", sender_id);
287 broadcast(PEGROUP, PP_FAIL);
293 /* char *opname = GetOpName(opcode);
294 fprintf(stderr,"Sysman: Unrecognised opcode %s (%x)\n",
296 fprintf(stderr, "Sysman: Unrecognised opcode (%x)\n",
303 } /* forked Sysman Process */
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 */
314 @myexit@ for the system manager.