1 /* -----------------------------------------------------------------------------
2 *
3 * (c) The GHC Team 2001-2005
4 *
5 * Tasks
6 *
7 * For details on the high-level design, see
8 * https://gitlab.haskell.org/ghc/ghc/wikis/commentary/rts/scheduler
9 *
10 * -------------------------------------------------------------------------*/
11
12 #pragma once
13
14 #include "GetTime.h"
15
16 #include "BeginPrivate.h"
17
18 /*
19 Note [Definition of a Task]
20 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
21
22 A task is an OSThread that runs Haskell code. Every OSThread that
23 runs inside the RTS, whether as a worker created by the RTS or via
24 an in-call from C to Haskell, has an associated Task. The first
25 time an OS thread calls into Haskell it is allocated a Task, which
26 remains until the RTS is shut down.
27
28 There is a one-to-one relationship between OSThreads and Tasks.
29 The Task for an OSThread is kept in thread-local storage, and can
30 be retrieved at any time using myTask().
31
32 In the THREADED_RTS build, multiple Tasks may all be running
33 Haskell code simultaneously. A task relinquishes its Capability
34 when it is asked to evaluate an external (C) call.
35
36 Note [Ownership of Task]
37 ~~~~~~~~~~~~~~~~~~~~~~~~
38
39 Task ownership is a little tricky. The default situation is that
40 the Task is an OS-thread-local structure that is owned by the OS
41 thread named in task->id. An OS thread not currently executing
42 Haskell code might call newBoundTask() at any time, which assumes
43 that it has access to the Task for the current OS thread.
44
45 The all_next and all_prev fields of a Task are owned by
46 all_tasks_mutex, which must also be taken if we want to create or
47 free a Task.
48
49 For an OS thread in Haskell, if (task->cap->running_task != task),
50 then the Task is owned by the owner of the parent data structure on
51 which it is sleeping; for example, if the task is sleeping on
52 spare_workers field of a Capability, then the owner of the
53 Capability has access to the Task.
54
55 When a task is migrated from sleeping on one Capability to another,
56 its task->cap field must be modified. When the task wakes up, it
57 will read the new value of task->cap to find out which Capability
58 it belongs to. Hence some synchronisation is required on
59 task->cap, and this is why we have task->lock.
60
61 If the Task is not currently owned by task->id, then the thread is
62 either
63
64 (a) waiting on the condition task->cond. The Task is either
65 (1) a bound Task, the TSO will be on a queue somewhere
66 (2) a worker task, on the spare_workers queue of task->cap.
67
68 (b) making a foreign call. The InCall will be on the
69 suspended_ccalls list.
70
71 We re-establish ownership in each case by respectively
72
73 (a) the task is currently blocked in yieldCapability().
74 This call will return when we have ownership of the Task and
75 a Capability. The Capability we get might not be the same
76 as the one we had when we called yieldCapability().
77
78 (b) we must call resumeThread(task), which will safely establish
79 ownership of the Task and a Capability.
80 */
81
82 // The InCall structure represents either a single in-call from C to
83 // Haskell, or a worker thread.
84 typedef struct InCall_ {
85 StgTSO * tso; // the bound TSO (or NULL for a worker)
86
87 StgTSO * suspended_tso; // the TSO is stashed here when we
88 // make a foreign call (NULL otherwise);
89
90 Capability *suspended_cap; // The capability that the
91 // suspended_tso is on, because
92 // we can't read this from the TSO
93 // without owning a Capability in the
94 // first place.
95
96 SchedulerStatus rstat; // return status
97 StgClosure ** ret; // return value
98
99 struct Task_ *task;
100
101 // When a Haskell thread makes a foreign call that re-enters
102 // Haskell, we end up with another Task associated with the
103 // current thread. We have to remember the whole stack of InCalls
104 // associated with the current Task so that we can correctly
105 // save & restore the InCall on entry to and exit from Haskell.
106 struct InCall_ *prev_stack;
107
108 // Links InCalls onto suspended_ccalls, spare_incalls
109 struct InCall_ *prev;
110 struct InCall_ *next;
111 } InCall;
112
113 typedef struct Task_ {
114 #if defined(THREADED_RTS)
115 OSThreadId id; // The OS Thread ID of this task
116
117 // The NUMA node this Task belongs to. If this is a worker thread, then the
118 // OS thread will be bound to this node (see workerStart()). If this is an
119 // external thread calling into Haskell, it can be bound to a node using
120 // rts_setInCallCapability().
121 uint32_t node;
122
123 Condition cond; // used for sleeping & waking up this task
124 Mutex lock; // lock for the condition variable
125
126 // this flag tells the task whether it should wait on task->cond
127 // or just continue immediately. It's a workaround for the fact
128 // that signalling a condition variable doesn't do anything if the
129 // thread is already running, but we want it to be sticky.
130 bool wakeup;
131 #endif
132
133 // If the task owns a Capability, task->cap points to it. (occasionally a
134 // task may own multiple capabilities, in which case task->cap may point to
135 // any of them. We must be careful to set task->cap to the appropriate one
136 // when using Capability APIs.)
137 //
138 // If the task is a worker, task->cap points to the Capability on which it
139 // is queued.
140 //
141 // If the task is in an unsafe foreign call, then task->cap can be used to
142 // retrieve the capability (see rts_unsafeGetMyCapability()).
143 struct Capability_ *cap;
144
145 // The current top-of-stack InCall
146 struct InCall_ *incall;
147
148 uint32_t n_spare_incalls;
149 struct InCall_ *spare_incalls;
150
151 bool worker; // == true if this is a worker Task
152 bool stopped; // == true between newBoundTask and
153 // boundTaskExiting, or in a worker Task.
154
155 // So that we can detect when a finalizer illegally calls back into Haskell
156 bool running_finalizers;
157
158 // if >= 0, this Capability will be used for in-calls
159 int preferred_capability;
160
161 // Links tasks on the returning_tasks queue of a Capability, and
162 // on spare_workers.
163 struct Task_ *next;
164
165 // Links tasks on the all_tasks list; need ACQUIRE_LOCK(&all_tasks_mutex)
166 struct Task_ *all_next;
167 struct Task_ *all_prev;
168
169 } Task;
170
171 INLINE_HEADER bool
isBoundTask(Task * task)172 isBoundTask (Task *task)
173 {
174 return (task->incall->tso != NULL);
175 }
176
177 // A Task is currently a worker if
178 // (a) it was created as a worker (task->worker), and
179 // (b) it has not left and re-entered Haskell, in which case
180 // task->incall->prev_stack would be non-NULL.
181 //
182 INLINE_HEADER bool
isWorker(Task * task)183 isWorker (Task *task)
184 {
185 return (task->worker && task->incall->prev_stack == NULL);
186 }
187
188 // Linked list of all tasks.
189 //
190 extern Task *all_tasks;
191
192 // The all_tasks list is protected by the all_tasks_mutex
193 #if defined(THREADED_RTS)
194 extern Mutex all_tasks_mutex;
195 #endif
196
197 // Start and stop the task manager.
198 // Requires: sched_mutex.
199 //
200 void initTaskManager (void);
201 uint32_t freeTaskManager (void);
202
203 // Create a new Task for a bound thread. This Task must be released
204 // by calling boundTaskExiting. The Task is cached in
205 // thread-local storage and will remain even after boundTaskExiting()
206 // has been called; to free the memory, see freeMyTask().
207 //
208 Task* newBoundTask (void);
209
210 // Return the current OS thread's Task, which is created if it doesn't already
211 // exist. After you have finished using RTS APIs, you should call freeMyTask()
212 // to release this thread's Task.
213 Task* getTask (void);
214
215 // The current task is a bound task that is exiting.
216 //
217 void boundTaskExiting (Task *task);
218
219 // Free a Task if one was previously allocated by newBoundTask().
220 // This is not necessary unless the thread that called newBoundTask()
221 // will be exiting, or if this thread has finished calling Haskell
222 // functions.
223 //
224 void freeMyTask(void);
225
226 // Notify the task manager that a task has stopped. This is used
227 // mainly for stats-gathering purposes.
228 // Requires: sched_mutex.
229 //
230 #if defined(THREADED_RTS)
231 // In the non-threaded RTS, tasks never stop.
232 void workerTaskStop (Task *task);
233 #endif
234
235 // Put the task back on the free list, mark it stopped. Used by
236 // forkProcess().
237 //
238 void discardTasksExcept (Task *keep);
239
240 // Get the Task associated with the current OS thread (or NULL if none).
241 //
242 INLINE_HEADER Task *myTask (void);
243
244 #if defined(THREADED_RTS)
245
246 // Workers are attached to the supplied Capability. This Capability
247 // should not currently have a running_task, because the new task
248 // will become the running_task for that Capability.
249 // Requires: sched_mutex.
250 //
251 void startWorkerTask (Capability *cap);
252
253 // Interrupts a worker task that is performing an FFI call. The thread
254 // should not be destroyed.
255 //
256 void interruptWorkerTask (Task *task);
257
258 #endif /* THREADED_RTS */
259
260 // For stats
261 extern uint32_t taskCount;
262 extern uint32_t workerCount;
263 extern uint32_t peakWorkerCount;
264
265 // -----------------------------------------------------------------------------
266 // INLINE functions... private from here on down:
267
268 // A thread-local-storage key that we can use to get access to the
269 // current thread's Task structure.
270 #if defined(THREADED_RTS)
271 #if ((defined(linux_HOST_OS) && \
272 (defined(i386_HOST_ARCH) || defined(x86_64_HOST_ARCH))) || \
273 (defined(mingw32_HOST_OS) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 4)) && \
274 (!defined(CC_LLVM_BACKEND))
275 #define MYTASK_USE_TLV
276 extern __thread Task *my_task;
277 #else
278 extern ThreadLocalKey currentTaskKey;
279 #endif
280 #else
281 extern Task *my_task;
282 #endif
283
284 //
285 // myTask() uses thread-local storage to find the Task associated with
286 // the current OS thread. If the current OS thread has multiple
287 // Tasks, because it has re-entered the RTS, then the task->prev_stack
288 // field is used to store the previous Task.
289 //
290 INLINE_HEADER Task *
myTask(void)291 myTask (void)
292 {
293 #if defined(THREADED_RTS) && !defined(MYTASK_USE_TLV)
294 return getThreadLocalVar(¤tTaskKey);
295 #else
296 return my_task;
297 #endif
298 }
299
300 INLINE_HEADER void
setMyTask(Task * task)301 setMyTask (Task *task)
302 {
303 #if defined(THREADED_RTS) && !defined(MYTASK_USE_TLV)
304 setThreadLocalVar(¤tTaskKey,task);
305 #else
306 my_task = task;
307 #endif
308 }
309
310 // Tasks are identified by their OS thread ID, which can be serialised
311 // to StgWord64, as defined below.
312 typedef StgWord64 TaskId;
313
314 // Get a unique serialisable representation for a task id.
315 //
316 // It's only unique within the process. For example if they are emitted in a
317 // log file then it is suitable to work out which log entries are releated.
318 //
319 // This is needed because OSThreadId is an opaque type
320 // and in practice on some platforms it is a pointer type.
321 //
322 #if defined(THREADED_RTS)
serialiseTaskId(OSThreadId taskID)323 INLINE_HEADER TaskId serialiseTaskId (OSThreadId taskID) {
324 #if defined(freebsd_HOST_OS) || defined(darwin_HOST_OS)
325 // Here OSThreadId is a pthread_t and pthread_t is a pointer, but within
326 // the process we can still use that pointer value as a unique id.
327 return (TaskId) (size_t) taskID;
328 #else
329 // On Windows, Linux and others it's an integral type to start with.
330 return (TaskId) taskID;
331 #endif
332 }
333 #endif
334
335 //
336 // Get a serialisable Id for the Task's OS thread
337 // Needed mainly for logging since the OSThreadId is an opaque type
338 INLINE_HEADER TaskId
serialisableTaskId(Task * task)339 serialisableTaskId (Task *task)
340 {
341 #if defined(THREADED_RTS)
342 return serialiseTaskId(task->id);
343 #else
344 return (TaskId) (size_t) task;
345 #endif
346 }
347
348 #include "EndPrivate.h"
349