% % (c) The GRASP Project, Glasgow University, 1994-1995 % \section[Thread]{Thread support macros used in \tr{.hc} files} \begin{code} #ifndef THREADS_H #define THREADS_H \end{code} \begin{code} #if defined(GRAN) #define sparkq sparkq #define TYPE_OF_SPARK struct spark #define TYPE_OF_SPARK_PTR sparkq #define SIZE_OF_SPARK (sizeof(TYPE_OF_SPARK)) typedef struct spark { struct spark *prev, *next; P_ node; I_ name, global; I_ gran_info; } *sparkq; #endif #ifndef CONCURRENT #define OR_CONTEXT_SWITCH #else extern I_ do_gr_sim; /* Are we simulating granularity? */ extern FILE *gr_file; extern I_ do_qp_prof; /* Are we quasi-parallel profiling? */ extern FILE *qp_file; #ifdef PAR #define DO_QP_PROF 0 #else #define DO_QP_PROF do_qp_prof #endif extern I_ context_switch; /* Flag set by signal handler */ #define CS_MAX_FREQUENCY 100 /* context switches per second */ #define CS_MIN_MILLISECS (1000/CS_MAX_FREQUENCY)/* milliseconds per slice */ #ifdef __STG_GCC_REGS__ #define OR_CONTEXT_SWITCH || context_switch #else #define OR_CONTEXT_SWITCH /* in miniInterpret */ #endif #define REQUIRED_POOL 0 #define ADVISORY_POOL 1 #define SPARK_POOLS 2 #if !defined(GRAN) #define TYPE_OF_SPARK PP_ #define SIZE_OF_SPARK (sizeof(TYPE_OF_SPARK)) extern TYPE_OF_SPARK PendingSparksBase[SPARK_POOLS], PendingSparksLim[SPARK_POOLS]; extern TYPE_OF_SPARK PendingSparksHd[SPARK_POOLS], PendingSparksTl[SPARK_POOLS]; extern I_ SparkLimit[SPARK_POOLS]; extern P_ RunnableThreadsHd, RunnableThreadsTl; extern P_ WaitingThreadsHd, WaitingThreadsTl; extern I_ sparksIgnored; IF_RTS(extern void AwaitEvent(I_);) #else /* GRAN */ extern TYPE_OF_SPARK_PTR PendingSparksBase[][SPARK_POOLS], PendingSparksLim[][SPARK_POOLS]; extern TYPE_OF_SPARK_PTR PendingSparksHd[][SPARK_POOLS], PendingSparksTl[][SPARK_POOLS]; extern P_ RunnableThreadsHd[], RunnableThreadsTl[], WaitThreadsHd[], WaitThreadsTl[]; #define SparkQueueHd PendingSparksHd[CurrentProc][ADVISORY_POOL] #define SparkQueueTl PendingSparksTl[CurrentProc][ADVISORY_POOL] #define ThreadQueueHd RunnableThreadsHd[CurrentProc] #define ThreadQueueTl RunnableThreadsTl[CurrentProc] #define WaitingThreadsHd WaitThreadsHd[CurrentProc] #define WaitingThreadsTl WaitThreadsTl[CurrentProc] #endif /* GRAN */ IF_RTS(extern void PruneSparks(STG_NO_ARGS);) #if defined(GRAN) /* Codes that can be used as params for ReSchedule */ /* I distinguish them from the values 0/1 in the -UGRAN setup for security */ /* reasons */ #define FIND_THREAD 10 #define SAME_THREAD 11 #define NEW_THREAD SAME_THREAD #define CHANGE_THREAD 13 #define END_OF_WORLD 14 extern W_ SparksAvail, SurplusThreads; extern W_ CurrentTime[]; extern I_ OutstandingFetches[], OutstandingFishes[]; extern enum proc_status procStatus[]; # if defined(GRAN_CHECK) && defined(GRAN) /* Just for testing */ # define FETCH_MASK_TSO 0x08000000 /* only bits 0, 1, 2 should be used */ /* normally */ extern P_ BlockedOnFetch[]; # endif #endif /* GRAN */ extern P_ CurrentTSO; /* thread state object now in use */ extern P_ AvailableStack; extern P_ AvailableTSO; extern I_ threadId; void ScheduleThreads PROTO((P_ topClosure)); #if defined(GRAN) void ReSchedule PROTO((int what_next)) STG_NORETURN; void add_to_spark_queue PROTO((sparkq)); int set_sparkname PROTO((P_, int)); int reset_sparkname PROTO((P_)); I_ spark_queue_len PROTO((PROC, I_)); sparkq delete_from_spark_queue PROTO((sparkq, sparkq)); I_ thread_queue_len PROTO((PROC)); void DisposeSparkQ PROTO((sparkq)); #else /* !GRAN */ void ReSchedule PROTO((int again)) STG_NORETURN; #endif void EndThread(STG_NO_ARGS) STG_NORETURN; /* ToDo: Check if these are still needed -- HWL */ void QP_Event0 PROTO((I_, P_)); void QP_Event1 PROTO((char *, P_)); void QP_Event2 PROTO((char *, P_, P_)); long qp_elapsed_time(STG_NO_ARGS); \end{code} %************************************************************************ %* * \subsection[thread-heap-objs]{Special threads-only heap objects (`closures')} %* * %************************************************************************ %************************************************************************ %* * \subsubsection[TSO-closures]{@TSO@ (thread state object) heap objects} %* * %************************************************************************ We now enter the realm of the Deeply Magical. Reduction threads come and go, resume and suspend, etc., in the threaded world. Obviously, there must be a place to squirrel away state information when a thread is suspended. Hence these {\em thread state objects} (TSOs). Rather than manage TSOs' alloc/dealloc, etc., in some {\em ad hoc} way, we instead alloc/dealloc/etc them in the heap; then we can use all the standard garbage-collection/fetching/flushing/etc machinery on them. So that's why TSOs are ``heap objects,'' albeit very special ones. We use all the standard heap-object/closure jargon... (e.g., @SET_TSO_HDR@, fixed headers, variable-hdr size, ...). A TSO is a fixed-size object with (post-header) words arranged like the main register table, and enough slop so that the register table can be properly aligned. The last header word of the TSO is a pointer to the (internal) start of the interesting data. Note that the heap and stack pointers in the TSO are only valid while the thread is executing, and only if the corresponding values are not stored in machine registers (i.e. the TSO becomes the backing register table for those values). \begin{code} #define TSO_INFO_WORDS 10 #ifdef TICKY_TICKY #define TSO_REDN_WORDS 2 #else #define TSO_REDN_WORDS 0 #endif #if defined(GRAN) || defined(PAR) /* do we really need a whole statistics buffer in PAR setup? HWL*/ #define TSO_GRAN_WORDS 17 #else #define TSO_GRAN_WORDS 0 #endif #define TSO_VHS \ (GC_MUT_RESERVED_WORDS + TSO_INFO_WORDS + TSO_REDN_WORDS + TSO_GRAN_WORDS) #define TSO_HS (FIXED_HS + TSO_VHS) #define TSO_CTS_SIZE (BYTES_TO_STGWORDS(sizeof(STGRegisterTable) + sizeof(StgDouble))) #define TSO_PTRS (MAX_VANILLA_REG + 2) /* std start-filling-in macro: */ #define SET_TSO_HDR(closure,infolbl,cc) \ { SET_FIXED_HDR(closure,infolbl,cc); \ SET_MUT_RESERVED_WORDS(closure); \ } #define TSO_INFO_START (FIXED_HS + GC_MUT_RESERVED_WORDS) #define TSO_LINK_LOCN (TSO_INFO_START + 0) #define TSO_CCC_LOCN (TSO_INFO_START + 1) #define TSO_NAME_LOCN (TSO_INFO_START + 2) #define TSO_ID_LOCN (TSO_INFO_START + 3) #define TSO_TYPE_LOCN (TSO_INFO_START + 4) #define TSO_PC1_LOCN (TSO_INFO_START + 5) #define TSO_PC2_LOCN (TSO_INFO_START + 6) #define TSO_ARG1_LOCN (TSO_INFO_START + 7) #define TSO_EVENT_LOCN (TSO_INFO_START + 8) #define TSO_SWITCH_LOCN (TSO_INFO_START + 9) #define TSO_REDN_START (TSO_INFO_START + TSO_INFO_WORDS) #ifdef TICKY_TICKY #define TSO_AHWM_LOCN (TSO_REDN_START + 0) #define TSO_BHWM_LOCN (TSO_REDN_START + 1) #endif #define TSO_GRAN_START (TSO_REDN_START + TSO_REDN_WORDS) #if defined(GRAN) || defined(PAR) #define TSO_LOCKED_LOCN (TSO_GRAN_START + 0) #define TSO_SPARKNAME_LOCN (TSO_GRAN_START + 1) #define TSO_STARTEDAT_LOCN (TSO_GRAN_START + 2) #define TSO_EXPORTED_LOCN (TSO_GRAN_START + 3) #define TSO_BASICBLOCKS_LOCN (TSO_GRAN_START + 4) #define TSO_ALLOCS_LOCN (TSO_GRAN_START + 5) #define TSO_EXECTIME_LOCN (TSO_GRAN_START + 6) #define TSO_FETCHTIME_LOCN (TSO_GRAN_START + 7) #define TSO_FETCHCOUNT_LOCN (TSO_GRAN_START + 8) #define TSO_BLOCKTIME_LOCN (TSO_GRAN_START + 9) #define TSO_BLOCKCOUNT_LOCN (TSO_GRAN_START + 10) #define TSO_BLOCKEDAT_LOCN (TSO_GRAN_START + 11) #define TSO_GLOBALSPARKS_LOCN (TSO_GRAN_START + 12) #define TSO_LOCALSPARKS_LOCN (TSO_GRAN_START + 13) #define TSO_QUEUE_LOCN (TSO_GRAN_START + 14) #define TSO_PRI_LOCN (TSO_GRAN_START + 15) #define TSO_CLOCK_LOCN (TSO_GRAN_START + 16) #endif #define TSO_LINK(closure) (((PP_)closure)[TSO_LINK_LOCN]) #define TSO_CCC(closure) (((CostCentre *)closure)[TSO_CCC_LOCN]) #define TSO_NAME(closure) (((PP_)closure)[TSO_NAME_LOCN]) #define TSO_ID(closure) (((P_)closure)[TSO_ID_LOCN]) #define TSO_TYPE(closure) (((P_)closure)[TSO_TYPE_LOCN]) #define TSO_PC1(closure) (((FP_)closure)[TSO_PC1_LOCN]) #define TSO_PC2(closure) (((FP_)closure)[TSO_PC2_LOCN]) #define TSO_ARG1(closure) (((P_)closure)[TSO_ARG1_LOCN]) #define TSO_EVENT(closure) (((P_)closure)[TSO_EVENT_LOCN]) #define TSO_SWITCH(closure) (((FP_)closure)[TSO_SWITCH_LOCN]) #define TSO_AHWM(closure) (((I_ *)closure)[TSO_AHWM_LOCN]) #define TSO_BHWM(closure) (((I_ *)closure)[TSO_BHWM_LOCN]) #define TSO_LOCKED(closure) (((P_)closure)[TSO_LOCKED_LOCN]) #define TSO_SPARKNAME(closure) (((P_)closure)[TSO_SPARKNAME_LOCN]) #define TSO_STARTEDAT(closure) (((P_)closure)[TSO_STARTEDAT_LOCN]) #define TSO_EXPORTED(closure) (((P_)closure)[TSO_EXPORTED_LOCN]) #define TSO_BASICBLOCKS(closure) (((P_)closure)[TSO_BASICBLOCKS_LOCN]) #define TSO_ALLOCS(closure) (((P_)closure)[TSO_ALLOCS_LOCN]) #define TSO_EXECTIME(closure) (((P_)closure)[TSO_EXECTIME_LOCN]) #define TSO_FETCHTIME(closure) (((P_)closure)[TSO_FETCHTIME_LOCN]) #define TSO_FETCHCOUNT(closure) (((P_)closure)[TSO_FETCHCOUNT_LOCN]) #define TSO_BLOCKTIME(closure) (((P_)closure)[TSO_BLOCKTIME_LOCN]) #define TSO_BLOCKCOUNT(closure) (((P_)closure)[TSO_BLOCKCOUNT_LOCN]) #define TSO_BLOCKEDAT(closure) (((P_)closure)[TSO_BLOCKEDAT_LOCN]) #define TSO_GLOBALSPARKS(closure) (((P_)closure)[TSO_GLOBALSPARKS_LOCN]) #define TSO_LOCALSPARKS(closure) (((P_)closure)[TSO_LOCALSPARKS_LOCN]) #define TSO_QUEUE(closure) (((P_)closure)[TSO_QUEUE_LOCN]) #define TSO_PRI(closure) (((P_)closure)[TSO_PRI_LOCN]) /* TSO_CLOCK is only needed in GrAnSim-Light */ #define TSO_CLOCK(closure) (((P_)closure)[TSO_CLOCK_LOCN]) #define TSO_INTERNAL_PTR(closure) \ ((STGRegisterTable *)(((W_)(((P_)closure) \ + TSO_HS + BYTES_TO_STGWORDS(sizeof(StgDouble)))) & ~(sizeof(StgDouble) - 1))) #if defined(CONCURRENT) && defined(GRAN) /* HWL */ /* Per definitionem a tso is really awake if it has met a first */ /* GRAN_RESCHEDULE macro after having been rescheduled. */ #define REALLY_AWAKE(tso) (TSO_SWITCH(tso) != TSO_PC2(tso)) #define SET_AWAKE_FLAG(tso) TSO_SWITCH(tso) = NULL #define RESET_AWAKE_FLAG(tso) TSO_SWITCH(tso) = TSO_PC2(tso) #endif \end{code} The types of threads (TSO_TYPE): \begin{code} #define T_MAIN 0 /* Must be executed locally */ #define T_REQUIRED 1 /* A required thread -- may be exported */ #define T_ADVISORY 2 /* An advisory thread -- may be exported */ #define T_FAIL 3 /* A failure thread -- may be exported */ \end{code} The total space required to start a new thread (See NewThread in Threads.lc): \begin{code} #define THREAD_SPACE_REQUIRED (TSO_HS + TSO_CTS_SIZE + STKO_HS + RTSflags.ConcFlags.stkChunkSize) \end{code} Here are the various queues for GrAnSim-type events. \begin{code} #define Q_RUNNING 'G' #define Q_RUNNABLE 'A' #define Q_BLOCKED 'R' #define Q_FETCHING 'Y' #define Q_MIGRATING 'B' \end{code} %************************************************************************ %* * \subsubsection[spark-closures]{Pending Sparks} %* * %************************************************************************ \begin{code} #ifdef PAR P_ FindLocalSpark PROTO((rtsBool forexport)); void DisposeSpark PROTO((P_ spark)); rtsBool Spark PROTO((P_ closure, rtsBool required)); #endif /*PAR*/ #ifdef GRAN /* For GrAnSim sparks are currently mallocated -- HWL */ void DisposeSpark PROTO((sparkq spark)); sparkq NewSpark PROTO((P_,I_,I_,I_,I_,I_)); /* # define MAX_EVENTS 1000 */ /* For GC Roots Purposes */ # define MAX_SPARKS 0 /* i.e. infinite */ #if defined(GRAN_JSM_SPARKS) /* spark is a pointer into some sparkq (which is for JSM sparls just an array of (struct sparks) */ # define SPARK_PREV(spark) { fprintf(stderr,"Error: SPARK_PREV not supported for JSM sparks") \ EXIT(EXIT_FAILURE); } /* NB: SPARK_NEXT may only be used as a rhs but NOT as a lhs */ # define SPARK_NEXT(spark) (spark++) # define SPARK_NODE(spark) (P_)(spark->node) # define SPARK_NAME(spark) (spark->name) # define SPARK_GRAN_INFO(spark) (spark->gran_info) # define SPARK_GLOBAL(spark) (spark->global) # define SPARK_EXPORTED(spark) (SPARK_GLOBAL(spark) > 1) #else # define SPARK_PREV(spark) (spark->prev) # define SPARK_NEXT(spark) (sparkq)(spark->next) # define SPARK_NODE(spark) (spark->node) # define SPARK_NAME(spark) (spark->name) # define SPARK_GRAN_INFO(spark) (spark->gran_info) # define SPARK_GLOBAL(spark) (spark->global) # define SPARK_EXPORTED(spark) (SPARK_GLOBAL(spark) > 1) #endif #endif /* GRAN */ \end{code} %************************************************************************ %* * \subsubsection[STKO-closures]{@STKO@ (stack object) heap objects} %* * %************************************************************************ We linger in the Deeply Magical... Each reduction thread has to have its own stack space. As there may be many such threads, and as any given one may need quite a big stack, a naive give-'em-a-big-stack-and-let-'em-run approach will cost a {\em lot} of memory. Our approach is to give a thread a small stack space, and then link on/off extra ``chunks'' as the need arises. Again, this is a storage-management problem, and, yet again, we choose to graft the whole business onto the existing heap-management machinery. So stack objects will live in the heap, be garbage collected, etc., etc.. So, as with TSOs, we use the standard heap-object (`closure') jargon. Here is the picture of how a stack object is arranged: \begin{verbatim} <----- var hdr --------> v ---- FirstPtr --- v --------------------------------------------------------------------- ...|| SpB | SuB | SpA | SuA || B stk -> ... | ... <- A stk || PREV || --------------------------------------------------------------------- XX-> <-YY \end{verbatim} We keep the following state-of-stack info in the {\em variable-header} part of a STKO: \begin{tabular}{ll} SpB, SuB & their {\em offsets} from 1st non-hdr word (marked \tr{XX} above)\\ SpA, SuA & their {\em offsets} from the next-to-last word (marked \tr{YY} above)\\ ctr field??? & (GC\_GEN\_WHATNOT may serve instead)\\ \end{tabular} The stack-pointer offsets are from the points indicated and are {\em non-negative} for pointers to this chunk of the stack space. At the {\em end} of the stack object, we have a {\em link} to the previous part of the overall stack. The link is \tr{NULL} if this is the bottom of the overall stack. After the header, we have @STKO_CHUNK_SIZE-1@ words of actual stack stuff. The B-stack part begins at the lowest address and grows upwards; the A-stack parts begins at the highest address and grows downwards. From a storage-manager point of view, these are {\em very special} objects. \begin{code} #ifdef TICKY_TICKY #define STKO_VHS (GC_MUT_RESERVED_WORDS + 9) #else #define STKO_VHS (GC_MUT_RESERVED_WORDS + 7) #endif #define STKO_HS (FIXED_HS + STKO_VHS) #define MIN_STKO_CHUNK_SIZE 16 /* Rather arbitrary */ #define STKO_CLOSURE_SIZE(closure) STKO_SIZE(closure) #define STKO_CLOSURE_CTS_SIZE(closure) (STKO_CLOSURE_SIZE(closure) - STKO_VHS) #define STKO_CLOSURE_PTR(closure, no) (*STKO_CLOSURE_ADDR(closure, no)) #define STKO_CLOSURE_ADDR(s, n) (((P_)(s)) + STKO_HS + (n) - 1) #define STKO_CLOSURE_OFFSET(s, p) (((P_)(p) - (P_)(s)) - STKO_HS + 1) /* std start-filling-in macro: */ #define SET_STKO_HDR(s,infolbl,cc) \ { SET_FIXED_HDR(s,infolbl,cc); \ SET_MUT_RESERVED_WORDS(s); \ /* the other header words filled in some other way */ } /* now we have the STKO-specific stuff Note: The S[pu][AB] registers are put in this order so that they will appear in monotonically increasing order in the StkO...just as an aid to the poor wee soul who has to debug things. */ #ifdef TICKY_TICKY #define STKO_ADEP_LOCN (STKO_HS - 9) #define STKO_BDEP_LOCN (STKO_HS - 8) #endif #define STKO_SIZE_LOCN (STKO_HS - 7) #define STKO_RETURN_LOCN (STKO_HS - 6) #define STKO_LINK_LOCN (STKO_HS - 5) #define STKO_SuB_LOCN (STKO_HS - 4) #define STKO_SpB_LOCN (STKO_HS - 3) #define STKO_SpA_LOCN (STKO_HS - 2) #define STKO_SuA_LOCN (STKO_HS - 1) #define STKO_ADEP(s) (((I_ *)(s))[STKO_ADEP_LOCN]) #define STKO_BDEP(s) (((I_ *)(s))[STKO_BDEP_LOCN]) #define STKO_SIZE(s) (((P_)(s))[STKO_SIZE_LOCN]) #define STKO_RETURN(s) (((StgRetAddr *)(s))[STKO_RETURN_LOCN]) #define STKO_LINK(s) (((PP_)(s))[STKO_LINK_LOCN]) #define STKO_SpB(s) (((PP_)(s))[STKO_SpB_LOCN]) #define STKO_SuB(s) (((PP_)(s))[STKO_SuB_LOCN]) #define STKO_SpA(s) (((PP_ *)(s))[STKO_SpA_LOCN]) #define STKO_SuA(s) (((PP_ *)(s))[STKO_SuA_LOCN]) #define STKO_BSTK_OFFSET(closure) (STKO_HS) #define STKO_ASTK_OFFSET(closure) (FIXED_HS + STKO_CLOSURE_SIZE(closure) - 1) #define STKO_BSTK_BOT(closure) (((P_)(closure)) + STKO_BSTK_OFFSET(closure)) #define STKO_ASTK_BOT(closure) (((PP_)(closure)) + STKO_ASTK_OFFSET(closure)) \end{code} These are offsets into the stack object proper (starting at 1 for the first word after the header). \begin{code} #define STKO_SpA_OFFSET(s) (STKO_CLOSURE_OFFSET(s,STKO_SpA(s))) #define STKO_SuA_OFFSET(s) (STKO_CLOSURE_OFFSET(s,STKO_SuA(s))) #define STKO_SpB_OFFSET(s) (STKO_CLOSURE_OFFSET(s,STKO_SpB(s))) #define STKO_SuB_OFFSET(s) (STKO_CLOSURE_OFFSET(s,STKO_SuB(s))) \end{code} %************************************************************************ %* * \subsubsection[BQ-closures]{@BQ@ (blocking queue) heap objects (`closures')} %* * %************************************************************************ Blocking queues are built in the parallel system when a local thread enters a non-global node. They are similar to black holes, except that when they are updated, the blocking queue must be enlivened too. A blocking queue closure thus has the following structure. \begin{onlylatex} \begin{center} \end{onlylatex} \begin{tabular}{||l|l|l|l||}\hline GA & Info ptr. & $\ldots$ & Blocking Queue \\ \hline \end{tabular} \begin{onlylatex} \begin{center} \end{onlylatex} The blocking queue itself is a pointer to a list of blocking queue entries. The list is formed from TSO closures. For the generational garbage collectors, the BQ must have the same structure as an IND, with the blocking queue hanging off of the indirection pointer. (This has to do with treating the BQ as an old root if it gets updated while in the old generation.) \begin{code} #define BQ_VHS IND_VHS #define BQ_HS IND_HS #define BQ_CLOSURE_SIZE(closure) IND_CLOSURE_SIZE(closure) #define BQ_CLOSURE_NoPTRS(closure) IND_CLOSURE_NoPTRS(closure) #define BQ_CLOSURE_NoNONPTRS(closure) IND_CLOSURE_NoNONPTRS(closure) #define BQ_CLOSURE_PTR(closure, no) (((P_)(closure))[BQ_HS + (no) - 1]) \end{code} Blocking queues store a pointer to a list of blocking queue entries. \begin{code} #define BQ_ENTRIES(closure) IND_CLOSURE_PTR(closure) #define BQ_LINK(closure) IND_CLOSURE_LINK(closure) \end{code} We have only one kind of blocking queue closure, so we test the info pointer for a specific value rather than looking in the info table for a special bit. \begin{code} EXTDATA_RO(BQ_info); EXTFUN(BQ_entry); #define IS_BQ_CLOSURE(closure) (INFO_PTR(closure) == (W_) BQ_info) \end{code} %************************************************************************ %* * \subsubsection[TSO_ITBL]{@TSO_ITBL@} %* * %************************************************************************ The special info table used for thread state objects (TSOs). \begin{code} #define TSO_ITBL() \ CAT_DECLARE(TSO,INTERNAL_KIND,"TSO","") \ EXTFUN(TSO_entry); \ EXTDATA_RO(MK_REP_LBL(TSO,,)); \ const W_ TSO_info[] = { \ (W_) TSO_entry \ ,(W_) INFO_OTHER_TAG \ ,(W_) MK_REP_REF(TSO,,) \ INCLUDE_PROFILING_INFO(TSO) \ } #define TSO_RTBL() \ const W_ MK_REP_LBL(TSO,,)[] = { \ INCLUDE_TYPE_INFO(TSO) \ INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED) \ INCLUDE_PAR_INFO \ INCLUDE_COPYING_INFO(_Evacuate_TSO,_Scavenge_TSO) \ INCLUDE_COMPACTING_INFO(_ScanLink_TSO,_PRStart_TSO,_ScanMove_TSO,_PRIn_TSO) \ } \end{code} %************************************************************************ %* * \subsubsection[STKO_ITBL]{@STKO_ITBL@} %* * %************************************************************************ The special info table used for stack objects (STKOs). \begin{code} #define STKO_ITBL() \ CAT_DECLARE(StkO,INTERNAL_KIND,"STKO","") \ EXTFUN(StkO_entry); \ EXTDATA_RO(MK_REP_LBL(StkO,,)); \ const W_ StkO_info[] = { \ (W_) StkO_entry \ ,(W_) INFO_OTHER_TAG \ ,(W_) MK_REP_REF(StkO,,) \ INCLUDE_PROFILING_INFO(StkO) \ } #define STKO_RTBL() \ const W_ MK_REP_LBL(StkO,,)[] = { \ INCLUDE_TYPE_INFO(STKO_DYNAMIC) \ INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED) \ INCLUDE_PAR_INFO \ INCLUDE_COPYING_INFO(_Evacuate_StkO,_Scavenge_StkO) \ INCLUDE_COMPACTING_INFO(_ScanLink_StkO,_PRStart_StkO,_ScanMove_StkO,_PRIn_StkO) \ } #define STKO_STATIC_ITBL() \ CAT_DECLARE(StkO_static,INTERNAL_KIND,"STKO","") \ EXTFUN(StkO_static_entry); \ EXTDATA_RO(MK_REP_LBL(StkO_static,,)); \ const W_ StkO_static_info[] = { \ (W_) StkO_static_entry \ ,(W_) INFO_OTHER_TAG \ ,(W_) MK_REP_REF(StkO_static,,) \ INCLUDE_PROFILING_INFO(StkO_static) \ } #define STKO_STATIC_RTBL() \ const W_ MK_REP_LBL(StkO_static,,)[] = { \ INCLUDE_TYPE_INFO(STKO_STATIC) \ INCLUDE_SIZE_INFO(INFO_UNUSED,INFO_UNUSED) \ INCLUDE_PAR_INFO \ INCLUDE_COPYING_INFO(_Evacuate_Static,_Dummy_Static_entry) \ INCLUDE_COMPACTING_INFO(_Dummy_Static_entry,_PRStart_Static, \ _Dummy_Static_entry,_PRIn_Error) \ } \end{code} %************************************************************************ %* * \subsubsection[BQ_ITBL]{@BQ_ITBL@} %* * %************************************************************************ Special info-table for local blocking queues. \begin{code} #define BQ_ITBL() \ CAT_DECLARE(BQ,INTERNAL_KIND,"BQ","") \ EXTFUN(BQ_entry); \ EXTDATA_RO(MK_REP_LBL(BQ,,)); \ const W_ BQ_info[] = { \ (W_) BQ_entry \ ,(W_) INFO_OTHER_TAG \ ,(W_) MK_REP_REF(BQ,,) \ INCLUDE_PROFILING_INFO(BQ) \ } #define BQ_RTBL() \ const W_ MK_REP_LBL(BQ,,)[] = { \ INCLUDE_TYPE_INFO(BQ) \ INCLUDE_SIZE_INFO(MIN_UPD_SIZE,INFO_UNUSED) \ INCLUDE_PAR_INFO \ INCLUDE_COPYING_INFO(_Evacuate_BQ,_Scavenge_BQ) \ SPEC_COMPACTING_INFO(_ScanLink_BQ,_PRStart_BQ,_ScanMove_BQ,_PRIn_BQ) \ } \end{code} \begin{code} #endif /* CONCURRENT */ \end{code} Even the sequential system gets to play with SynchVars, though it really doesn't make too much sense (if any). Okay; maybe it makes some sense. (See the 1.3 I/O stuff.) %************************************************************************ %* * \subsubsection[SVar-closures]{@SynchVar@ heap objects} %* * %************************************************************************ \begin{code} #define SVAR_HS (MUTUPLE_HS) #define SVAR_CLOSURE_SIZE(closure) 3 #define SET_SVAR_HDR(closure,infolbl,cc) \ SET_MUTUPLE_HDR(closure,infolbl,cc,MUTUPLE_VHS+3,3) /* The value must come first, because we shrink the other two fields off when writing an IVar */ #define SVAR_VALUE_LOCN (SVAR_HS+0) #define SVAR_HEAD_LOCN (SVAR_HS+1) #define SVAR_TAIL_LOCN (SVAR_HS+2) #define SVAR_VALUE(closure) ((PP_)(closure))[SVAR_VALUE_LOCN] #define SVAR_HEAD(closure) ((PP_)(closure))[SVAR_HEAD_LOCN] #define SVAR_TAIL(closure) ((PP_)(closure))[SVAR_TAIL_LOCN] \end{code} End multi-slurp protection: \begin{code} #endif /* THREADS_H */ \end{code}