%/**************************************************************** %* * %* This is where everything starts * %* * %****************************************************************/ \begin{code} #if defined(PROFILING) || defined(PAR) || defined(CONCURRENT) #define NON_POSIX_SOURCE /* time things on Solaris -- sigh */ #endif #include "rtsdefs.h" #include #if defined(STDC_HEADERS) || defined(HAVE_STRING_H) # include /* An ANSI string.h and pre-ANSI memory.h might conflict. */ # if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H) # include # endif /* not STDC_HEADERS and HAVE_MEMORY_H */ #else /* not STDC_HEADERS and not HAVE_STRING_H */ # include /* memory.h and strings.h conflict on some systems. */ #endif /* not STDC_HEADERS and not HAVE_STRING_H */ #if defined(PROFILING) || defined(PAR) || defined(GRAN) /* need some "time" things */ /* ToDo: This is a mess! Improve ? */ # ifdef HAVE_SYS_TYPES_H # include # endif # ifdef HAVE_SYS_TIMES_H # include # endif # ifdef HAVE_SYS_TIME_H # include # endif #endif /* PROFILING || PAR */ #ifndef PAR STGRegisterTable MainRegTable; #endif /* fwd decls */ void shutdownHaskell(STG_NO_ARGS); EXTFUN(startStgWorld); extern void PrintTickyInfo(STG_NO_ARGS); extern void checkAStack(STG_NO_ARGS); /* a real nasty Global Variable */ /* moved to main/TopClosure(13)?.lc -- *one* of them will get linked in P_ TopClosure = GHCmain_mainPrimIO_closure; */ /* structure to carry around info about the storage manager */ smInfo StorageMgrInfo; #ifdef PAR extern I_ OkToGC, buckets; extern rtsBool TraceSparks, DelaySparks, DeferGlobalUpdates; void RunParallelSystem PROTO((P_)); void initParallelSystem(STG_NO_ARGS); void SynchroniseSystem(STG_NO_ARGS); void SetTrace PROTO((W_ address, I_ level/*?*/)); #endif void *stgAllocForGMP PROTO((size_t)); void *stgReallocForGMP PROTO ((void *, size_t, size_t)); void stgDeallocForGMP PROTO ((void *, size_t)); /* NeXTs can't just reach out and touch "end", to use in distinguishing things in static vs dynamic (malloc'd) memory. */ #if nextstep2_TARGET_OS || nextstep3_TARGET_OS /* ToDo: use END_BY_FUNNY_MEANS or something */ void *get_end_result; #endif int prog_argc; /* an "int" so as to match normal "argc" */ char **prog_argv; int rts_argc; /* ditto */ char *rts_argv[MAX_RTS_ARGS]; #ifndef PAR jmp_buf restart_main; /* For restarting after a signal */ #endif #if defined(PAR) int nPEs = 0; /* Number of PEs */ #endif int /* return type of "main" is defined by the C standard */ main(int argc, char *argv[]) { #ifdef GRAN int i; #endif \end{code} The very first thing we do is grab the start time...just in case we're collecting timing statistics. \begin{code} start_time(); \end{code} The parallel system needs to be initialised and synchronised before the program is run. This is done {\em before} heap allocation, so we can grab all remaining heap without needing to consider the System Manager's requirements. \begin{code} #ifdef PAR if (*argv[0] == '-') { /* Look to see whether we're the Main Thread */ IAmMainThread = rtsTrue; argv++; argc--; /* Strip off flag argument */ /* fprintf(stderr, "I am Main Thread\n"); */ } /* * Grab the number of PEs out of the argument vector, and * eliminate it from further argument processing. */ nPEs = atoi(argv[1]); argv[1] = argv[0]; argv++; argc--; initEachPEHook(); /* HWL: hook to be execed on each PE */ SynchroniseSystem(); #endif #if defined(PROFILING) || defined(PAR) || defined(GRAN) /* setup string indicating time of run -- only used for profiling */ (void) time_str(); #endif #if nextstep2_TARGET_OS || nextstep3_TARGET_OS /* ToDo: use END_BY_FUNNY_MEANS or something */ get_end_result = get_end(); #endif /* divide the command-line args between pgm and RTS; figure out what statsfile to use (if any); [if so, write the whole cmd-line into it] This is unlikely to work well in parallel! KH. */ initRtsFlagsDefaults(); defaultsHook(); /* the one supplied does nothing; the user may have supplied a more interesting one. */ setupRtsFlags(&argc, argv, &rts_argc, rts_argv); prog_argc = argc; prog_argv = argv; #if defined(PAR) /* Initialise the parallel system -- before initHeap! */ initParallelSystem(); #endif /* PAR */ #if defined(PROFILING) || defined(PAR) if (init_cc_profiling(rts_argc, rts_argv, prog_argv) != 0) { fflush(stdout); fprintf(stderr, "init_cc_profiling failed!\n"); EXIT(EXIT_FAILURE); } #endif #if defined(GRAN) if (!RTSflags.GranFlags.granSimStats_suppressed) if (init_gr_simulation(rts_argc, rts_argv, prog_argc, prog_argv) != 0) { fprintf(stderr, "init_gr_simulation failed!\n"); EXIT(EXIT_FAILURE); } #endif #ifdef PAR if (RTSflags.ParFlags.granSimStats) init_gr_profiling(rts_argc, rts_argv, prog_argc, prog_argv); #endif /* initialize the storage manager */ initSM(); #ifndef PAR if (! initStacks( &StorageMgrInfo )) { fflush(stdout); fprintf(stderr, "initStacks failed!\n"); EXIT(EXIT_FAILURE); } #endif if (! initHeap( &StorageMgrInfo )) { fflush(stdout); fprintf(stderr, "initHeap failed!\n"); EXIT(EXIT_FAILURE); } #if defined(CONCURRENT) && !defined(GRAN) if (!initThreadPools()) { fflush(stdout); fprintf(stderr, "initThreadPools failed!\n"); EXIT(EXIT_FAILURE); } #endif #if defined(PROFILING) || defined(PAR) /* call cost centre registering routine (after heap allocated) */ cc_register(); #endif #if defined(TICKY_TICKY) max_SpA = MAIN_SpA; /* initial high-water marks */ max_SpB = MAIN_SpB; #endif /* Tell GNU multi-precision pkg about our custom alloc functions */ mp_set_memory_functions(stgAllocForGMP, stgReallocForGMP, stgDeallocForGMP); /* Record initialization times */ end_init(); #if defined(PROFILING) || defined(CONCURRENT) /* * Both the context-switcher and the cost-center profiler use * a virtual timer. */ if (install_vtalrm_handler()) { fflush(stdout); fprintf(stderr, "Can't install VTALRM handler.\n"); EXIT(EXIT_FAILURE); } #if (defined(CONCURRENT) && defined(PROFILING)) || defined(PAR) if (! time_profiling) RTSflags.CcFlags.msecsPerTick = RTSflags.ConcFlags.ctxtSwitchTime; else { if (RTSflags.ConcFlags.ctxtSwitchTime % (1000/TICK_FREQUENCY) == 0) RTSflags.CcFlags.msecsPerTick = TICK_MILLISECS; else RTSflags.CcFlags.msecsPerTick = CS_MIN_MILLISECS; RTSflags.CcFlags.ctxtSwitchTicks = RTSflags.ConcFlags.ctxtSwitchTime / RTSflags.CcFlags.msecsPerTick; RTSflags.CcFlags.profilerTicks = TICK_MILLISECS / RTSflags.CcFlags.msecsPerTick; } #endif #ifndef CONCURRENT START_TIME_PROFILER; #endif #endif /* PROFILING || CONCURRENT */ #ifndef PAR setjmp(restart_main); initUserSignals(); #endif #ifdef CONCURRENT AvailableStack = AvailableTSO = Prelude_Z91Z93_closure; # if defined(GRAN) /* HWL */ /* Moved in here from ScheduleThreads, to handle a restart_main (because of a signal) properly. */ for (i=0; i... But if you're playing with sockets, that "close fd 0" might suggest to the daemon that all is over, only to be presented with more stuff on "fd 1" at the flush. The fflush avoids this sad possibility. */ } \end{code} Sets up and returns a string indicating the date/time of the run. Successive calls simply return the same string again. Initially called by @main.lc@ to initialise the string at the start of the run. Only used for profiling. \begin{code} #if defined(PROFILING) || defined(CONCURRENT) || defined(GRAN) # include char * time_str(STG_NO_ARGS) { static time_t now = 0; static char nowstr[26]; if (now == 0) { time(&now); strcpy(nowstr, ctime(&now)); strcpy(nowstr+16,nowstr+19); nowstr[21] = '\0'; } return nowstr; } #endif /* profiling */ \end{code} ToDo: Will this work under threads? \begin{code} StgStablePtr errorHandler = -1; /* NB: prone to magic-value-ery (WDP 95/12) */ StgInt getErrorHandler(STG_NO_ARGS) { return (StgInt) errorHandler; } #if !defined(PAR) void raiseError( handler ) StgStablePtr handler; { if (handler == -1) { /* beautiful magic value... (WDP 95/12) */ shutdownHaskell(); EXIT(EXIT_FAILURE); } else { TopClosure = deRefStablePointer( handler ); longjmp(restart_main,1); } } \end{code} \begin{code} StgInt catchError( newErrorHandler ) StgStablePtr newErrorHandler; { StgStablePtr oldErrorHandler = errorHandler; errorHandler = newErrorHandler; return oldErrorHandler; } #endif \end{code} If we have installed an error handler, we might want to indicate that we have successfully recovered from an error by decrementing the counter. \begin{code} void decrementErrorCount() { ErrorIO_call_count-=1; } \end{code}