root/rts/Task.h

Revision 085c7fe5d4ea6e7b59f944d46ecfeba3755a315b, 8.9 KB (checked in by Simon Marlow <marlowsd@…>, 3 months ago)

Drop the per-task timing stats, give a summary only (#5897)

We were keeping around the Task struct (216 bytes) for every worker we
ever created, even though we only keep a maximum of 6 workers per
Capability. These Task structs accumulate and cause a space leak in
programs that do lots of safe FFI calls; this patch frees the Task
struct as soon as a worker exits.

One reason we were keeping the Task structs around is because we print
out per-Task timing stats in +RTS -s, but that isn't terribly useful.
What is sometimes useful is knowing how *many* Tasks there were. So
now I'm printing a single-line summary, this is for the program in

TASKS: 2001 (1 bound, 31 peak workers (2000 total), using -N1)

So although we created 2k tasks overall, there were only 31 workers
active at any one time (which is exactly what we expect: the program
makes 30 safe FFI calls concurrently).

This also gives an indication of how many capabilities were being
used, which is handy if you use +RTS -N without an explicit number.

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