summaryrefslogtreecommitdiff
path: root/rts/Task.h
diff options
context:
space:
mode:
authorSimon Marlow <simonmar@microsoft.com>2006-04-07 02:05:11 +0000
committerSimon Marlow <simonmar@microsoft.com>2006-04-07 02:05:11 +0000
commit0065d5ab628975892cea1ec7303f968c3338cbe1 (patch)
tree8e2afe0ab48ee33cf95009809d67c9649573ef92 /rts/Task.h
parent28a464a75e14cece5db40f2765a29348273ff2d2 (diff)
downloadhaskell-0065d5ab628975892cea1ec7303f968c3338cbe1.tar.gz
Reorganisation of the source tree
Most of the other users of the fptools build system have migrated to Cabal, and with the move to darcs we can now flatten the source tree without losing history, so here goes. The main change is that the ghc/ subdir is gone, and most of what it contained is now at the top level. The build system now makes no pretense at being multi-project, it is just the GHC build system. No doubt this will break many things, and there will be a period of instability while we fix the dependencies. A straightforward build should work, but I haven't yet fixed binary/source distributions. Changes to the Building Guide will follow, too.
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 */