Split part of the Task struct into a separate struct InCall
[ghc.git] / rts / Task.h
1 /* -----------------------------------------------------------------------------
2 *
3 * (c) The GHC Team 2001-2005
4 *
5 * Tasks
6 *
7 * -------------------------------------------------------------------------*/
8
9 #ifndef TASK_H
10 #define TASK_H
11
12 #include "GetTime.h"
13
14 BEGIN_RTS_PRIVATE
15
16 /*
17 Definition of a Task
18 --------------------
19
20 A task is an OSThread that runs Haskell code. Every OSThread that
21 runs inside the RTS, whether as a worker created by the RTS or via
22 an in-call from C to Haskell, has an associated Task. The first
23 time an OS thread calls into Haskell it is allocated a Task, which
24 remains until the RTS is shut down.
25
26 There is a one-to-one relationship between OSThreads and Tasks.
27 The Task for an OSThread is kept in thread-local storage, and can
28 be retrieved at any time using myTask().
29
30 In the THREADED_RTS build, multiple Tasks may all be running
31 Haskell code simultaneously. A task relinquishes its Capability
32 when it is asked to evaluate an external (C) call.
33
34 Ownership of Task
35 -----------------
36
37 The OS thread named in the Task structure has exclusive access to
38 the structure, as long as it is the running_task of its Capability.
39 That is, if (task->cap->running_task == task), then task->id owns
40 the Task. Otherwise the Task is owned by the owner of the parent
41 data structure on which it is sleeping; for example, if the task is
42 sleeping on spare_workers field of a Capability, then the owner of the
43 Capability has access to the Task.
44
45 When a task is migrated from sleeping on one Capability to another,
46 its task->cap field must be modified. When the task wakes up, it
47 will read the new value of task->cap to find out which Capability
48 it belongs to. Hence some synchronisation is required on
49 task->cap, and this is why we have task->lock.
50
51 If the Task is not currently owned by task->id, then the thread is
52 either
53
54 (a) waiting on the condition task->cond. The Task is either
55 (1) a bound Task, the TSO will be on a queue somewhere
56 (2) a worker task, on the spare_workers queue of task->cap.
57
58 (b) making a foreign call. The InCall will be on the
59 suspended_ccalls list.
60
61 We re-establish ownership in each case by respectively
62
63 (a) the task is currently blocked in yieldCapability().
64 This call will return when we have ownership of the Task and
65 a Capability. The Capability we get might not be the same
66 as the one we had when we called yieldCapability().
67
68 (b) we must call resumeThread(task), which will safely establish
69 ownership of the Task and a Capability.
70 */
71
72 // The InCall structure represents either a single in-call from C to
73 // Haskell, or a worker thread.
74 typedef struct InCall_ {
75 StgTSO * tso; // the bound TSO (or NULL for a worker)
76
77 StgTSO * suspended_tso; // the TSO is stashed here when we
78 // make a foreign call (NULL otherwise);
79
80 Capability *suspended_cap; // The capability that the
81 // suspended_tso is on, because
82 // we can't read this from the TSO
83 // without owning a Capability in the
84 // first place.
85
86 struct Task_ *task;
87
88 // When a Haskell thread makes a foreign call that re-enters
89 // Haskell, we end up with another Task associated with the
90 // current thread. We have to remember the whole stack of InCalls
91 // associated with the current Task so that we can correctly
92 // save & restore the InCall on entry to and exit from Haskell.
93 struct InCall_ *prev_stack;
94
95 // Links InCalls onto suspended_ccalls, spare_incalls
96 struct InCall_ *prev;
97 struct InCall_ *next;
98 } InCall;
99
100 typedef struct Task_ {
101 #if defined(THREADED_RTS)
102 OSThreadId id; // The OS Thread ID of this task
103
104 Condition cond; // used for sleeping & waking up this task
105 Mutex lock; // lock for the condition variable
106
107 // this flag tells the task whether it should wait on task->cond
108 // or just continue immediately. It's a workaround for the fact
109 // that signalling a condition variable doesn't do anything if the
110 // thread is already running, but we want it to be sticky.
111 rtsBool wakeup;
112 #endif
113
114 // This points to the Capability that the Task "belongs" to. If
115 // the Task owns a Capability, then task->cap points to it. If
116 // the task does not own a Capability, then either (a) if the task
117 // is a worker, then task->cap points to the Capability it belongs
118 // to, or (b) it is returning from a foreign call, then task->cap
119 // points to the Capability with the returning_worker queue that this
120 // this Task is on.
121 //
122 // When a task goes to sleep, it may be migrated to a different
123 // Capability. Hence, we always check task->cap on wakeup. To
124 // syncrhonise between the migrater and the migratee, task->lock
125 // must be held when modifying task->cap.
126 struct Capability_ *cap;
127
128 // The current top-of-stack InCall
129 struct InCall_ *incall;
130
131 nat n_spare_incalls;
132 struct InCall_ *spare_incalls;
133
134 rtsBool worker; // == rtsTrue if this is a worker Task
135 rtsBool stopped; // this task has stopped or exited Haskell
136
137 SchedulerStatus stat; // return status
138 StgClosure ** ret; // return value
139
140 // Stats that we collect about this task
141 // ToDo: we probably want to put this in a separate TaskStats
142 // structure, so we can share it between multiple Tasks. We don't
143 // really want separate stats for each call in a nested chain of
144 // foreign->haskell->foreign->haskell calls, but we'll get a
145 // separate Task for each of the haskell calls.
146 Ticks elapsedtimestart;
147 Ticks muttimestart;
148 Ticks mut_time;
149 Ticks mut_etime;
150 Ticks gc_time;
151 Ticks gc_etime;
152
153 // Links tasks on the returning_tasks queue of a Capability, and
154 // on spare_workers.
155 struct Task_ *next;
156
157 // Links tasks on the all_tasks list
158 struct Task_ *all_link;
159
160 } Task;
161
162 INLINE_HEADER rtsBool
163 isBoundTask (Task *task)
164 {
165 return (task->incall->tso != NULL);
166 }
167
168
169 // Linked list of all tasks.
170 //
171 extern Task *all_tasks;
172
173 // Start and stop the task manager.
174 // Requires: sched_mutex.
175 //
176 void initTaskManager (void);
177 nat freeTaskManager (void);
178
179 // Create a new Task for a bound thread
180 // Requires: sched_mutex.
181 //
182 Task *newBoundTask (void);
183
184 // The current task is a bound task that is exiting.
185 // Requires: sched_mutex.
186 //
187 void boundTaskExiting (Task *task);
188
189 // Notify the task manager that a task has stopped. This is used
190 // mainly for stats-gathering purposes.
191 // Requires: sched_mutex.
192 //
193 #if defined(THREADED_RTS)
194 // In the non-threaded RTS, tasks never stop.
195 void workerTaskStop (Task *task);
196 #endif
197
198 // Record the time spent in this Task.
199 // This is called by workerTaskStop() but not by boundTaskExiting(),
200 // because it would impose an extra overhead on call-in.
201 //
202 void taskTimeStamp (Task *task);
203
204 // Put the task back on the free list, mark it stopped. Used by
205 // forkProcess().
206 //
207 void discardTasksExcept (Task *keep);
208
209 // Get the Task associated with the current OS thread (or NULL if none).
210 //
211 INLINE_HEADER Task *myTask (void);
212
213 #if defined(THREADED_RTS)
214
215 // Workers are attached to the supplied Capability. This Capability
216 // should not currently have a running_task, because the new task
217 // will become the running_task for that Capability.
218 // Requires: sched_mutex.
219 //
220 void startWorkerTask (Capability *cap);
221
222 #endif /* THREADED_RTS */
223
224 // -----------------------------------------------------------------------------
225 // INLINE functions... private from here on down:
226
227 // A thread-local-storage key that we can use to get access to the
228 // current thread's Task structure.
229 #if defined(THREADED_RTS)
230 #if defined(linux_HOST_OS) && \
231 (defined(i386_HOST_ARCH) || defined(x86_64_HOST_ARCH))
232 #define MYTASK_USE_TLV
233 extern __thread Task *my_task;
234 #else
235 extern ThreadLocalKey currentTaskKey;
236 #endif
237 #else
238 extern Task *my_task;
239 #endif
240
241 //
242 // myTask() uses thread-local storage to find the Task associated with
243 // the current OS thread. If the current OS thread has multiple
244 // Tasks, because it has re-entered the RTS, then the task->prev_stack
245 // field is used to store the previous Task.
246 //
247 INLINE_HEADER Task *
248 myTask (void)
249 {
250 #if defined(THREADED_RTS) && !defined(MYTASK_USE_TLV)
251 return getThreadLocalVar(&currentTaskKey);
252 #else
253 return my_task;
254 #endif
255 }
256
257 INLINE_HEADER void
258 setMyTask (Task *task)
259 {
260 #if defined(THREADED_RTS) && !defined(MYTASK_USE_TLV)
261 setThreadLocalVar(&currentTaskKey,task);
262 #else
263 my_task = task;
264 #endif
265 }
266
267 END_RTS_PRIVATE
268
269 #endif /* TASK_H */