*
* Tasks
*
+ * For details on the high-level design, see
+ * http://hackage.haskell.org/trac/ghc/wiki/Commentary/Rts/Scheduler
+ *
* -------------------------------------------------------------------------*/
#ifndef TASK_H
#include "GetTime.h"
+#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.
-
- 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.
+ 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().
- 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.
+ 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.
Ownership of Task
-----------------
(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
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
// 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
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);
}
// Requires: sched_mutex.
//
void initTaskManager (void);
-void stopTaskManager (void);
-void freeTaskManager (void);
+nat freeTaskManager (void);
// Create a new Task for a bound thread
// 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.
//
+#if defined(THREADED_RTS)
+// In the non-threaded RTS, tasks never stop.
void workerTaskStop (Task *task);
+#endif
// Record the time spent in this Task.
// This is called by workerTaskStop() but not by boundTaskExiting(),
//
void taskTimeStamp (Task *task);
+// The current Task has finished a GC, record the amount of time spent.
+void taskDoneGC (Task *task, Ticks cpu_time, Ticks elapsed_time);
+
// 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).
//
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
// 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 */
// 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
INLINE_HEADER Task *
myTask (void)
{
-#if defined(THREADED_RTS)
+#if defined(THREADED_RTS) && !defined(MYTASK_USE_TLV)
return getThreadLocalVar(¤tTaskKey);
#else
return my_task;
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);
-}
+#include "EndPrivate.h"
#endif /* TASK_H */