X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=rts%2FTask.h;h=38e4763b5a350d8533b42a13133e4869c4abbc60;hp=9b5f0253cd41082c77230991a75a696e25caccdc;hb=83d563cb9ede0ba792836e529b1e2929db926355;hpb=9a9803e8dc80ba41bd3e2d31228e64fa6b61060e diff --git a/rts/Task.h b/rts/Task.h index 9b5f025..38e4763 100644 --- a/rts/Task.h +++ b/rts/Task.h @@ -4,6 +4,9 @@ * * Tasks * + * For details on the high-level design, see + * http://hackage.haskell.org/trac/ghc/wiki/Commentary/Rts/Scheduler + * * -------------------------------------------------------------------------*/ #ifndef TASK_H @@ -11,30 +14,26 @@ #include "GetTime.h" -BEGIN_RTS_PRIVATE +#include "BeginPrivate.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. + A task is an OSThread that runs Haskell code. Every OSThread that + runs inside the RTS, whether as a worker created by the RTS or via + an in-call from C to Haskell, has an associated Task. The first + time an OS thread calls into Haskell it is allocated a Task, which + remains until the RTS is shut down. + + There is a one-to-one relationship between OSThreads and Tasks. + The Task for an OSThread is kept in thread-local storage, and can + be retrieved at any time using myTask(). In the THREADED_RTS build, 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 associated with a given OS - thread. A second Task is created when one Task makes a foreign - call from Haskell, and subsequently calls back in to Haskell, - creating 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 ----------------- @@ -59,8 +58,8 @@ BEGIN_RTS_PRIVATE (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. + (b) making a foreign call. The InCall will be on the + suspended_ccalls list. We re-establish ownership in each case by respectively @@ -73,9 +72,49 @@ BEGIN_RTS_PRIVATE ownership of the Task and a Capability. */ +// The InCall structure represents either a single in-call from C to +// Haskell, or a worker thread. +typedef struct InCall_ { + StgTSO * tso; // the bound TSO (or NULL for a worker) + + StgTSO * suspended_tso; // the TSO is stashed here when we + // make a foreign call (NULL otherwise); + + Capability *suspended_cap; // The capability that the + // suspended_tso is on, because + // we can't read this from the TSO + // without owning a Capability in the + // first place. + + SchedulerStatus stat; // return status + StgClosure ** ret; // return value + + struct Task_ *task; + + // 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 InCalls + // associated with the current Task so that we can correctly + // save & restore the InCall on entry to and exit from Haskell. + struct InCall_ *prev_stack; + + // Links InCalls onto suspended_ccalls, spare_incalls + struct InCall_ *prev; + struct InCall_ *next; +} InCall; + typedef struct Task_ { #if defined(THREADED_RTS) OSThreadId id; // The OS Thread ID of this task + + 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 // This points to the Capability that the Task "belongs" to. If @@ -92,25 +131,17 @@ typedef struct Task_ { // 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 current top-of-stack InCall + struct InCall_ *incall; - // The following 3 fields are used by bound threads: - StgTSO * tso; // the bound TSO (or NULL) - SchedulerStatus stat; // return status - StgClosure ** ret; // return value + nat n_spare_incalls; + struct InCall_ *spare_incalls; -#if defined(THREADED_RTS) - Condition cond; // used for sleeping & waking up this task - Mutex lock; // lock for the condition variable + rtsBool worker; // == rtsTrue if this is a worker Task + rtsBool stopped; // this task has stopped or exited Haskell - // 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 + // So that we can detect when a finalizer illegally calls back into Haskell + rtsBool running_finalizers; // Stats that we collect about this task // ToDo: we probably want to put this in a separate TaskStats @@ -125,29 +156,19 @@ typedef struct Task_ { Ticks gc_time; Ticks gc_etime; - // Links tasks onto various lists. (ToDo: do we need double - // linking now?) - struct Task_ *prev; + // Links tasks on the returning_tasks queue of a Capability, and + // on spare_workers. 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); + return (task->incall->tso != NULL); } @@ -171,11 +192,6 @@ Task *newBoundTask (void); // 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. @@ -194,7 +210,7 @@ void taskTimeStamp (Task *task); // Put the task back on the free list, mark it stopped. Used by // forkProcess(). // -void discardTask (Task *task); +void discardTasksExcept (Task *keep); // Get the Task associated with the current OS thread (or NULL if none). // @@ -207,8 +223,12 @@ INLINE_HEADER Task *myTask (void); // will become the running_task for that Capability. // Requires: sched_mutex. // -void startWorkerTask (struct Capability_ *cap, - void OSThreadProcAttr (*taskStart)(Task *task)); +void startWorkerTask (Capability *cap); + +// Interrupts a worker task that is performing an FFI call. The thread +// should not be destroyed. +// +void interruptWorkerTask (Task *task); #endif /* THREADED_RTS */ @@ -218,7 +238,14 @@ void startWorkerTask (struct Capability_ *cap, // A thread-local-storage key that we can use to get access to the // current thread's Task structure. #if defined(THREADED_RTS) +#if (defined(linux_HOST_OS) && \ + (defined(i386_HOST_ARCH) || defined(x86_64_HOST_ARCH))) || \ + (defined(mingw32_HOST_OS) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 4) +#define MYTASK_USE_TLV +extern __thread Task *my_task; +#else extern ThreadLocalKey currentTaskKey; +#endif #else extern Task *my_task; #endif @@ -232,7 +259,7 @@ extern Task *my_task; INLINE_HEADER Task * myTask (void) { -#if defined(THREADED_RTS) +#if defined(THREADED_RTS) && !defined(MYTASK_USE_TLV) return getThreadLocalVar(¤tTaskKey); #else return my_task; @@ -242,25 +269,13 @@ myTask (void) INLINE_HEADER void setMyTask (Task *task) { -#if defined(THREADED_RTS) +#if defined(THREADED_RTS) && !defined(MYTASK_USE_TLV) 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); -} - -END_RTS_PRIVATE +#include "EndPrivate.h" #endif /* TASK_H */