Fix crash in nested callbacks (#4038)
[ghc-hetmet.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     SchedulerStatus  stat;      // return status
87     StgClosure **    ret;       // return value
88
89     struct Task_ *task;
90
91     // When a Haskell thread makes a foreign call that re-enters
92     // Haskell, we end up with another Task associated with the
93     // current thread.  We have to remember the whole stack of InCalls
94     // associated with the current Task so that we can correctly
95     // save & restore the InCall on entry to and exit from Haskell.
96     struct InCall_ *prev_stack;
97
98     // Links InCalls onto suspended_ccalls, spare_incalls
99     struct InCall_ *prev;
100     struct InCall_ *next;
101 } InCall;
102
103 typedef struct Task_ {
104 #if defined(THREADED_RTS)
105     OSThreadId id;              // The OS Thread ID of this task
106
107     Condition cond;             // used for sleeping & waking up this task
108     Mutex lock;                 // lock for the condition variable
109
110     // this flag tells the task whether it should wait on task->cond
111     // or just continue immediately.  It's a workaround for the fact
112     // that signalling a condition variable doesn't do anything if the
113     // thread is already running, but we want it to be sticky.
114     rtsBool wakeup;
115 #endif
116
117     // This points to the Capability that the Task "belongs" to.  If
118     // the Task owns a Capability, then task->cap points to it.  If
119     // the task does not own a Capability, then either (a) if the task
120     // is a worker, then task->cap points to the Capability it belongs
121     // to, or (b) it is returning from a foreign call, then task->cap
122     // points to the Capability with the returning_worker queue that this
123     // this Task is on.
124     //
125     // When a task goes to sleep, it may be migrated to a different
126     // Capability.  Hence, we always check task->cap on wakeup.  To
127     // syncrhonise between the migrater and the migratee, task->lock
128     // must be held when modifying task->cap.
129     struct Capability_ *cap;
130
131     // The current top-of-stack InCall
132     struct InCall_ *incall;
133
134     nat n_spare_incalls;
135     struct InCall_ *spare_incalls;
136
137     rtsBool    worker;          // == rtsTrue if this is a worker Task
138     rtsBool    stopped;         // this task has stopped or exited Haskell
139
140     // So that we can detect when a finalizer illegally calls back into Haskell
141     rtsBool running_finalizers;
142
143     // Stats that we collect about this task
144     // ToDo: we probably want to put this in a separate TaskStats
145     // structure, so we can share it between multiple Tasks.  We don't
146     // really want separate stats for each call in a nested chain of
147     // foreign->haskell->foreign->haskell calls, but we'll get a
148     // separate Task for each of the haskell calls.
149     Ticks       elapsedtimestart;
150     Ticks       muttimestart;
151     Ticks       mut_time;
152     Ticks       mut_etime;
153     Ticks       gc_time;
154     Ticks       gc_etime;
155
156     // Links tasks on the returning_tasks queue of a Capability, and
157     // on spare_workers.
158     struct Task_ *next;
159
160     // Links tasks on the all_tasks list
161     struct Task_ *all_link;
162
163 } Task;
164
165 INLINE_HEADER rtsBool
166 isBoundTask (Task *task) 
167 {
168     return (task->incall->tso != NULL);
169 }
170
171
172 // Linked list of all tasks.
173 //
174 extern Task *all_tasks;
175
176 // Start and stop the task manager.
177 // Requires: sched_mutex.
178 //
179 void initTaskManager (void);
180 nat  freeTaskManager (void);
181
182 // Create a new Task for a bound thread
183 // Requires: sched_mutex.
184 //
185 Task *newBoundTask (void);
186
187 // The current task is a bound task that is exiting.
188 // Requires: sched_mutex.
189 //
190 void boundTaskExiting (Task *task);
191
192 // Notify the task manager that a task has stopped.  This is used
193 // mainly for stats-gathering purposes.
194 // Requires: sched_mutex.
195 //
196 #if defined(THREADED_RTS)
197 // In the non-threaded RTS, tasks never stop.
198 void workerTaskStop (Task *task);
199 #endif
200
201 // Record the time spent in this Task.
202 // This is called by workerTaskStop() but not by boundTaskExiting(),
203 // because it would impose an extra overhead on call-in.
204 //
205 void taskTimeStamp (Task *task);
206
207 // Put the task back on the free list, mark it stopped.  Used by
208 // forkProcess().
209 //
210 void discardTasksExcept (Task *keep);
211
212 // Get the Task associated with the current OS thread (or NULL if none).
213 //
214 INLINE_HEADER Task *myTask (void);
215
216 #if defined(THREADED_RTS)
217
218 // Workers are attached to the supplied Capability.  This Capability
219 // should not currently have a running_task, because the new task
220 // will become the running_task for that Capability.
221 // Requires: sched_mutex.
222 //
223 void startWorkerTask (Capability *cap);
224
225 #endif /* THREADED_RTS */
226
227 // -----------------------------------------------------------------------------
228 // INLINE functions... private from here on down:
229
230 // A thread-local-storage key that we can use to get access to the
231 // current thread's Task structure.
232 #if defined(THREADED_RTS)
233 #if defined(linux_HOST_OS) && \
234     (defined(i386_HOST_ARCH) || defined(x86_64_HOST_ARCH))
235 #define MYTASK_USE_TLV
236 extern __thread Task *my_task;
237 #else
238 extern ThreadLocalKey currentTaskKey;
239 #endif
240 #else
241 extern Task *my_task;
242 #endif
243
244 //
245 // myTask() uses thread-local storage to find the Task associated with
246 // the current OS thread.  If the current OS thread has multiple
247 // Tasks, because it has re-entered the RTS, then the task->prev_stack
248 // field is used to store the previous Task.
249 //
250 INLINE_HEADER Task *
251 myTask (void)
252 {
253 #if defined(THREADED_RTS) && !defined(MYTASK_USE_TLV)
254     return getThreadLocalVar(&currentTaskKey);
255 #else
256     return my_task;
257 #endif
258 }
259
260 INLINE_HEADER void
261 setMyTask (Task *task)
262 {
263 #if defined(THREADED_RTS) && !defined(MYTASK_USE_TLV)
264     setThreadLocalVar(&currentTaskKey,task);
265 #else
266     my_task = task;
267 #endif
268 }
269
270 END_RTS_PRIVATE
271
272 #endif /* TASK_H */