summaryrefslogtreecommitdiff
path: root/rts/Task.h
diff options
context:
space:
mode:
Diffstat (limited to 'rts/Task.h')
-rw-r--r--rts/Task.h271
1 files changed, 271 insertions, 0 deletions
diff --git a/rts/Task.h b/rts/Task.h
new file mode 100644
index 0000000000..ca71d2809a
--- /dev/null
+++ b/rts/Task.h
@@ -0,0 +1,271 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team 2001-2005
+ *
+ * Tasks
+ *
+ * -------------------------------------------------------------------------*/
+
+#ifndef TASK_H
+#define TASK_H
+
+#include "GetTime.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(&currentTaskKey);
+#else
+ return my_task;
+#endif
+}
+
+INLINE_HEADER void
+setMyTask (Task *task)
+{
+#if defined(THREADED_RTS)
+ setThreadLocalVar(&currentTaskKey,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 */