[project @ 2005-03-14 12:21:36 by simonmar]
[ghc-hetmet.git] / ghc / rts / RtsStartup.c
1 /* -----------------------------------------------------------------------------
2  *
3  * (c) The GHC Team, 1998-2002
4  *
5  * Main function for a standalone Haskell program.
6  *
7  * ---------------------------------------------------------------------------*/
8
9 #include "PosixSource.h"
10 #include "Rts.h"
11 #include "RtsAPI.h"
12 #include "RtsUtils.h"
13 #include "RtsFlags.h"  
14 #include "Storage.h"    /* initStorage, exitStorage */
15 #include "Schedule.h"   /* initScheduler */
16 #include "Stats.h"      /* initStats */
17 #include "STM.h"        /* initSTM */
18 #include "Signals.h"
19 #include "Timer.h"      /* startTimer, stopTimer */
20 #include "Weak.h"
21 #include "Ticky.h"
22 #include "StgRun.h"
23 #include "Prelude.h"            /* fixupRTStoPreludeRefs */
24 #include "HsFFI.h"
25 #include "Linker.h"
26 #include "ThreadLabels.h"
27 #include "BlockAlloc.h"
28
29 #if defined(RTS_GTK_FRONTPANEL)
30 #include "FrontPanel.h"
31 #endif
32
33 #if defined(PROFILING) || defined(DEBUG)
34 # include "Profiling.h"
35 # include "ProfHeap.h"
36 # include "RetainerProfile.h"
37 #endif
38
39 #if defined(GRAN)
40 # include "GranSimRts.h"
41 #endif
42
43 #if defined(GRAN) || defined(PAR)
44 # include "ParallelRts.h"
45 #endif
46
47 #if defined(PAR)
48 # include "Parallel.h"
49 # include "LLC.h"
50 #endif
51
52 #if defined(mingw32_HOST_OS)
53 #include "win32/AsyncIO.h"
54 #endif
55
56 #include <stdlib.h>
57
58 #ifdef HAVE_TERMIOS_H
59 #include <termios.h>
60 #endif
61 #ifdef HAVE_SIGNAL_H
62 #include <signal.h>
63 #endif
64
65 // Count of how many outstanding hs_init()s there have been.
66 static int hs_init_count = 0;
67
68 // Here we save the terminal settings on the standard file
69 // descriptors, if we need to change them (eg. to support NoBuffering
70 // input).
71 static void *saved_termios[3] = {NULL,NULL,NULL};
72
73 void*
74 __hscore_get_saved_termios(int fd)
75 {
76   return (0 <= fd && fd < (int)(sizeof(saved_termios) / sizeof(*saved_termios))) ?
77     saved_termios[fd] : NULL;
78 }
79
80 void
81 __hscore_set_saved_termios(int fd, void* ts)
82 {
83   if (0 <= fd && fd < (int)(sizeof(saved_termios) / sizeof(*saved_termios))) {
84     saved_termios[fd] = ts;
85   }
86 }
87
88 /* -----------------------------------------------------------------------------
89    Initialise floating point unit on x86 (currently disabled. why?)
90    -------------------------------------------------------------------------- */
91
92 #define X86_INIT_FPU 0
93
94 #if X86_INIT_FPU
95 static void
96 x86_init_fpu ( void )
97 {
98   __volatile unsigned short int fpu_cw;
99
100   // Grab the control word
101   __asm __volatile ("fnstcw %0" : "=m" (fpu_cw));
102
103 #if 0
104   printf("fpu_cw: %x\n", fpu_cw);
105 #endif
106
107   // Set bits 8-9 to 10 (64-bit precision).
108   fpu_cw = (fpu_cw & 0xfcff) | 0x0200;
109
110   // Store the new control word back
111   __asm __volatile ("fldcw %0" : : "m" (fpu_cw));
112 }
113 #endif
114
115 /* -----------------------------------------------------------------------------
116    Starting up the RTS
117    -------------------------------------------------------------------------- */
118
119 void
120 hs_init(int *argc, char **argv[])
121 {
122     hs_init_count++;
123     if (hs_init_count > 1) {
124         // second and subsequent inits are ignored
125         return;
126     }
127
128     /* The very first thing we do is grab the start time...just in case we're
129      * collecting timing statistics.
130      */
131     stat_startInit();
132
133 #ifdef PAR
134     /*
135      * The parallel system needs to be initialised and synchronised before
136      * the program is run.  
137      */ 
138     startupParallelSystem(argv);
139      
140     if (*argv[0] == '-') { /* Strip off mainPE flag argument */
141       argv++; 
142       argc--;                   
143     }
144
145     argv[1] = argv[0];   /* ignore the nPEs argument */
146     argv++; argc--;
147 #endif
148
149     /* Set the RTS flags to default values. */
150     initRtsFlagsDefaults();
151
152     /* Call the user hook to reset defaults, if present */
153     defaultsHook();
154
155     /* Parse the flags, separating the RTS flags from the programs args */
156     if (argc != NULL && argv != NULL) {
157         setupRtsFlags(argc, *argv, &rts_argc, rts_argv);
158         setProgArgv(*argc,*argv);
159     }
160
161 #if defined(PAR)
162     /* NB: this really must be done after processing the RTS flags */
163     IF_PAR_DEBUG(verbose,
164                  debugBelch("==== Synchronising system (%d PEs)\n", nPEs));
165     synchroniseSystem();             // calls initParallelSystem etc
166 #endif  /* PAR */
167
168     /* Perform initialisation of adjustor thunk layer. */
169     initAdjustor();
170
171     /* initialise scheduler data structures (needs to be done before
172      * initStorage()).
173      */
174     initScheduler();
175
176 #if defined(GRAN)
177     /* And start GranSim profiling if required: */
178     if (RtsFlags.GranFlags.GranSimStats.Full)
179       init_gr_simulation(rts_argc, rts_argv, prog_argc, prog_argv);
180 #elif defined(PAR)
181     /* And start GUM profiling if required: */
182     if (RtsFlags.ParFlags.ParStats.Full)
183       init_gr_simulation(rts_argc, rts_argv, prog_argc, prog_argv);
184 #endif  /* PAR || GRAN */
185
186     /* initialize the storage manager */
187     initStorage();
188
189     /* initialise the stable pointer table */
190     initStablePtrTable();
191
192 #if defined(DEBUG)
193     /* initialise thread label table (tso->char*) */
194     initThreadLabelTable();
195 #endif
196
197 #if defined(PROFILING) || defined(DEBUG)
198     initProfiling1();
199 #endif
200
201     /* start the virtual timer 'subsystem'. */
202     startTimer(TICK_MILLISECS);
203
204     /* Initialise the stats department */
205     initStats();
206
207 #if defined(RTS_USER_SIGNALS)
208     /* Initialise the user signal handler set */
209     initUserSignals();
210     /* Set up handler to run on SIGINT, etc. */
211     initDefaultHandlers();
212 #endif
213  
214 #if defined(mingw32_HOST_OS)
215     startupAsyncIO();
216 #endif
217
218     initSTM();
219
220 #ifdef RTS_GTK_FRONTPANEL
221     if (RtsFlags.GcFlags.frontpanel) {
222         initFrontPanel();
223     }
224 #endif
225
226 #if X86_INIT_FPU
227     x86_init_fpu();
228 #endif
229
230     /* Record initialization times */
231     stat_endInit();
232 }
233
234 // Compatibility interface
235 void
236 startupHaskell(int argc, char *argv[], void (*init_root)(void))
237 {
238     hs_init(&argc, &argv);
239     hs_add_root(init_root);
240 }
241
242
243 /* -----------------------------------------------------------------------------
244    Per-module initialisation
245
246    This process traverses all the compiled modules in the program
247    starting with "Main", and performing per-module initialisation for
248    each one.
249
250    So far, two things happen at initialisation time:
251
252       - we register stable names for each foreign-exported function
253         in that module.  This prevents foreign-exported entities, and
254         things they depend on, from being garbage collected.
255
256       - we supply a unique integer to each statically declared cost
257         centre and cost centre stack in the program.
258
259    The code generator inserts a small function "__stginit_<module>" in each
260    module and calls the registration functions in each of the modules it
261    imports.
262
263    The init* functions are compiled in the same way as STG code,
264    i.e. without normal C call/return conventions.  Hence we must use
265    StgRun to call this stuff.
266    -------------------------------------------------------------------------- */
267
268 /* The init functions use an explicit stack... 
269  */
270 #define INIT_STACK_BLOCKS  4
271 static F_ *init_stack = NULL;
272
273 void
274 hs_add_root(void (*init_root)(void))
275 {
276     bdescr *bd;
277 #ifdef SMP
278     Capability cap;
279 #else
280 #define cap MainCapability
281 #endif
282     nat init_sp;
283
284     if (hs_init_count <= 0) {
285         barf("hs_add_root() must be called after hs_init()");
286     }
287
288     /* The initialisation stack grows downward, with sp pointing 
289        to the last occupied word */
290     init_sp = INIT_STACK_BLOCKS*BLOCK_SIZE_W;
291     bd = allocGroup(INIT_STACK_BLOCKS);
292     init_stack = (F_ *)bd->start;
293     init_stack[--init_sp] = (F_)stg_init_finish;
294     if (init_root != NULL) {
295         init_stack[--init_sp] = (F_)init_root;
296     }
297     
298     cap.r.rSp = (P_)(init_stack + init_sp);
299     StgRun((StgFunPtr)stg_init, &cap.r);
300
301     freeGroup(bd);
302
303 #if defined(PROFILING) || defined(DEBUG)
304     // This must be done after module initialisation.
305     // ToDo: make this work in the presence of multiple hs_add_root()s.
306     initProfiling2();
307 #endif
308 }
309
310 /* -----------------------------------------------------------------------------
311    Shutting down the RTS
312    -------------------------------------------------------------------------- */
313
314 void
315 hs_exit(void)
316 {
317     if (hs_init_count <= 0) {
318         barf("too many hs_exit()s");
319     }
320     hs_init_count--;
321     if (hs_init_count > 0) {
322         // ignore until it's the last one
323         return;
324     }
325
326     /* start timing the shutdown */
327     stat_startExit();
328     
329     /* stop all running tasks */
330     exitScheduler();
331     
332 #if defined(GRAN)
333     /* end_gr_simulation prints global stats if requested -- HWL */
334     if (!RtsFlags.GranFlags.GranSimStats.Suppressed)
335         end_gr_simulation();
336 #endif
337     
338     /* stop the ticker */
339     stopTimer();
340     
341     /* reset the standard file descriptors to blocking mode */
342     resetNonBlockingFd(0);
343     resetNonBlockingFd(1);
344     resetNonBlockingFd(2);
345
346 #if HAVE_TERMIOS_H
347     // Reset the terminal settings on the standard file descriptors,
348     // if we changed them.  See System.Posix.Internals.tcSetAttr for
349     // more details, including the reason we termporarily disable
350     // SIGTTOU here.
351     { 
352         int fd;
353         sigset_t sigset, old_sigset;
354         sigemptyset(&sigset);
355         sigaddset(&sigset, SIGTTOU);
356         sigprocmask(SIG_BLOCK, &sigset, &old_sigset);
357         for (fd = 0; fd <= 2; fd++) {
358             struct termios* ts = (struct termios*)__hscore_get_saved_termios(fd);
359             if (ts != NULL) {
360                 tcsetattr(fd,TCSANOW,ts);
361             }
362         }
363         sigprocmask(SIG_SETMASK, &old_sigset, NULL);
364     }
365 #endif
366
367 #if defined(PAR)
368     /* controlled exit; good thread! */
369     shutdownParallelSystem(0);
370     
371     /* global statistics in parallel system */
372     PAR_TICKY_PAR_END();
373 #endif
374
375     /* stop timing the shutdown, we're about to print stats */
376     stat_endExit();
377     
378     /* clean up things from the storage manager's point of view.
379      * also outputs the stats (+RTS -s) info.
380      */
381     exitStorage();
382     
383 #ifdef RTS_GTK_FRONTPANEL
384     if (RtsFlags.GcFlags.frontpanel) {
385         stopFrontPanel();
386     }
387 #endif
388
389 #if defined(PROFILING) 
390     reportCCSProfiling();
391 #endif
392
393 #if defined(PROFILING) || defined(DEBUG)
394     endProfiling();
395 #endif
396
397 #ifdef PROFILING
398     // Originally, this was in report_ccs_profiling().  Now, retainer
399     // profiling might tack some extra stuff on to the end of this file
400     // during endProfiling().
401     fclose(prof_file);
402 #endif
403     
404 #if defined(TICKY_TICKY)
405     if (RtsFlags.TickyFlags.showTickyStats) PrintTickyInfo();
406 #endif
407
408 #if defined(mingw32_HOST_OS)
409     shutdownAsyncIO();
410 #endif
411 }
412
413 // Compatibility interfaces
414 void
415 shutdownHaskell(void)
416 {
417     hs_exit();
418 }
419
420 void
421 shutdownHaskellAndExit(int n)
422 {
423     if (hs_init_count == 1) {
424         OnExitHook();
425         hs_exit();
426 #if defined(PAR)
427         /* really exit (stg_exit() would call shutdownParallelSystem() again) */
428         exit(n);
429 #else
430         stg_exit(n);
431 #endif
432     }
433 }
434
435 /* 
436  * called from STG-land to exit the program
437  */
438
439 #ifdef PAR
440 static int exit_started=rtsFalse;
441 #endif
442
443 void  
444 stg_exit(int n)
445
446 #ifdef PAR
447   /* HACK: avoid a loop when exiting due to a stupid error */
448   if (exit_started) 
449     return;
450   exit_started=rtsTrue;
451
452   IF_PAR_DEBUG(verbose, debugBelch("==-- stg_exit %d on [%x]...", n, mytid));
453   shutdownParallelSystem(n);
454 #endif
455   exit(n);
456 }