X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=ghc%2Frts%2FTask.h;h=ca71d2809a46b9c0a2f75bf0d13ddb3c3b8cd549;hb=28a464a75e14cece5db40f2765a29348273ff2d2;hp=ee599876fd16474c9e7c81b66cbb8f58b81f521e;hpb=324e96d2ebfcb113cd97c43ef043d591ef87de71;p=ghc-hetmet.git diff --git a/ghc/rts/Task.h b/ghc/rts/Task.h index ee59987..ca71d28 100644 --- a/ghc/rts/Task.h +++ b/ghc/rts/Task.h @@ -1,36 +1,271 @@ /* ----------------------------------------------------------------------------- * - * (c) The GHC Team 2001- + * (c) The GHC Team 2001-2005 * - * Types + prototypes for functions in Task.c - * (RTS subsystem for handling tasks, agents thay may execute STG code). + * Tasks * * -------------------------------------------------------------------------*/ -#ifndef __TASK_H__ -#define __TASK_H__ -#if defined(RTS_SUPPORTS_THREADS) /* to the end */ + +#ifndef TASK_H +#define TASK_H + +#include "GetTime.h" /* - * Tasks evaluate STG code; the TaskInfo structure collects together - * misc metadata about a task. - * - */ -typedef struct _TaskInfo { - OSThreadId id; - double elapsedtimestart; - double mut_time; - double mut_etime; - double gc_time; - double gc_etime; -} TaskInfo; - -extern TaskInfo *taskIds; - -extern void startTaskManager ( nat maxTasks, void (*taskStart)(void) ); -extern void stopTaskManager ( void ); -void resetTaskManagerAfterFork (); - -extern void startTask ( void (*taskStart)(void) ); - -#endif /* RTS_SUPPORTS_THREADS */ -#endif /* __TASK_H__ */ + Definition of a Task + -------------------- + + A task is an OSThread that runs Haskell code. Every OSThread + created by the RTS for the purposes of running Haskell code is a + Task, and OS threads that enter the Haskell RTS for the purposes of + making a call-in are also Tasks. + + The relationship between the number of tasks and capabilities, and + the runtime build (-threaded, -smp etc.) is summarised by the + following table: + + build Tasks Capabilities + --------------------------------- + normal 1 1 + -threaded N N + + The non-threaded build has a single Task and a single global + Capability. + + The THREADED_RTS build allows multiple tasks and mulitple Capabilities. + Multiple Tasks may all be running Haskell code simultaneously. A task + relinquishes its Capability when it is asked to evaluate an external + (C) call. + + In general, there may be multiple Tasks for an OS thread. This + happens if one Task makes a foreign call from Haskell, and + subsequently calls back in to create a new bound thread. + + A particular Task structure can belong to more than one OS thread + over its lifetime. This is to avoid creating an unbounded number + of Task structures. The stats just accumulate. + + Ownership of Task + ----------------- + + The OS thread named in the Task structure has exclusive access to + the structure, as long as it is the running_task of its Capability. + That is, if (task->cap->running_task == task), then task->id owns + the Task. Otherwise the Task is owned by the owner of the parent + data structure on which it is sleeping; for example, if the task is + sleeping on spare_workers field of a Capability, then the owner of the + Capability has access to the Task. + + When a task is migrated from sleeping on one Capability to another, + its task->cap field must be modified. When the task wakes up, it + will read the new value of task->cap to find out which Capability + it belongs to. Hence some synchronisation is required on + task->cap, and this is why we have task->lock. + + If the Task is not currently owned by task->id, then the thread is + either + + (a) waiting on the condition task->cond. The Task is either + (1) a bound Task, the TSO will be on a queue somewhere + (2) a worker task, on the spare_workers queue of task->cap. + + (b) making a foreign call. The Task will be on the + suspended_ccalling_tasks list. + + We re-establish ownership in each case by respectively + + (a) the task is currently blocked in yieldCapability(). + This call will return when we have ownership of the Task and + a Capability. The Capability we get might not be the same + as the one we had when we called yieldCapability(). + + (b) we must call resumeThread(task), which will safely establish + ownership of the Task and a Capability. +*/ + +typedef struct Task_ { +#if defined(THREADED_RTS) + OSThreadId id; // The OS Thread ID of this task +#endif + + // This points to the Capability that the Task "belongs" to. If + // the Task owns a Capability, then task->cap points to it. If + // the task does not own a Capability, then either (a) if the task + // is a worker, then task->cap points to the Capability it belongs + // to, or (b) it is returning from a foreign call, then task->cap + // points to the Capability with the returning_worker queue that this + // this Task is on. + // + // When a task goes to sleep, it may be migrated to a different + // Capability. Hence, we always check task->cap on wakeup. To + // syncrhonise between the migrater and the migratee, task->lock + // must be held when modifying task->cap. + struct Capability_ *cap; + + rtsBool stopped; // this task has stopped or exited Haskell + StgTSO * suspended_tso; // the TSO is stashed here when we + // make a foreign call (NULL otherwise); + + // The following 3 fields are used by bound threads: + StgTSO * tso; // the bound TSO (or NULL) + SchedulerStatus stat; // return status + StgClosure ** ret; // return value + +#if defined(THREADED_RTS) + Condition cond; // used for sleeping & waking up this task + Mutex lock; // lock for the condition variable + + // this flag tells the task whether it should wait on task->cond + // or just continue immediately. It's a workaround for the fact + // that signalling a condition variable doesn't do anything if the + // thread is already running, but we want it to be sticky. + rtsBool wakeup; +#endif + + // Stats that we collect about this task + // ToDo: we probably want to put this in a separate TaskStats + // structure, so we can share it between multiple Tasks. We don't + // really want separate stats for each call in a nested chain of + // foreign->haskell->foreign->haskell calls, but we'll get a + // separate Task for each of the haskell calls. + Ticks elapsedtimestart; + Ticks muttimestart; + Ticks mut_time; + Ticks mut_etime; + Ticks gc_time; + Ticks gc_etime; + + // Links tasks onto various lists. (ToDo: do we need double + // linking now?) + struct Task_ *prev; + struct Task_ *next; + + // Links tasks on the returning_tasks queue of a Capability. + struct Task_ *return_link; + + // Links tasks on the all_tasks list + struct Task_ *all_link; + + // When a Haskell thread makes a foreign call that re-enters + // Haskell, we end up with another Task associated with the + // current thread. We have to remember the whole stack of Tasks + // associated with the current thread so that we can correctly + // save & restore the thread-local current task pointer. + struct Task_ *prev_stack; +} Task; + +INLINE_HEADER rtsBool +isBoundTask (Task *task) +{ + return (task->tso != NULL); +} + + +// Linked list of all tasks. +// +extern Task *all_tasks; + +// Start and stop the task manager. +// Requires: sched_mutex. +// +void initTaskManager (void); +void stopTaskManager (void); + +// Create a new Task for a bound thread +// Requires: sched_mutex. +// +Task *newBoundTask (void); + +// The current task is a bound task that is exiting. +// Requires: sched_mutex. +// +void boundTaskExiting (Task *task); + +// This must be called when a new Task is associated with the current +// thread. It sets up the thread-local current task pointer so that +// myTask() can work. +INLINE_HEADER void taskEnter (Task *task); + +// Notify the task manager that a task has stopped. This is used +// mainly for stats-gathering purposes. +// Requires: sched_mutex. +// +void taskStop (Task *task); + +// Put the task back on the free list, mark it stopped. Used by +// forkProcess(). +// +void discardTask (Task *task); + +// Get the Task associated with the current OS thread (or NULL if none). +// +INLINE_HEADER Task *myTask (void); + +// After a fork, the tasks are not carried into the child process, so +// we must tell the task manager. +// Requires: sched_mutex. +// +void resetTaskManagerAfterFork (void); + +#if defined(THREADED_RTS) + +// Workers are attached to the supplied Capability. This Capability +// should not currently have a running_task, because the new task +// will become the running_task for that Capability. +// Requires: sched_mutex. +// +void startWorkerTask (struct Capability_ *cap, + void OSThreadProcAttr (*taskStart)(Task *task)); + +#endif /* THREADED_RTS */ + +// ----------------------------------------------------------------------------- +// INLINE functions... private from here on down: + +// A thread-local-storage key that we can use to get access to the +// current thread's Task structure. +#if defined(THREADED_RTS) +extern ThreadLocalKey currentTaskKey; +#else +extern Task *my_task; +#endif + +// +// myTask() uses thread-local storage to find the Task associated with +// the current OS thread. If the current OS thread has multiple +// Tasks, because it has re-entered the RTS, then the task->prev_stack +// field is used to store the previous Task. +// +INLINE_HEADER Task * +myTask (void) +{ +#if defined(THREADED_RTS) + return getThreadLocalVar(¤tTaskKey); +#else + return my_task; +#endif +} + +INLINE_HEADER void +setMyTask (Task *task) +{ +#if defined(THREADED_RTS) + setThreadLocalVar(¤tTaskKey,task); +#else + my_task = task; +#endif +} + +// This must be called when a new Task is associated with the current +// thread. It sets up the thread-local current task pointer so that +// myTask() can work. +INLINE_HEADER void +taskEnter (Task *task) +{ + // save the current value, just in case this Task has been created + // as a result of re-entering the RTS (defaults to NULL): + task->prev_stack = myTask(); + setMyTask(task); +} + +#endif /* TASK_H */