| 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. |
|---|
| 77 | typedef 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 | |
|---|
| 106 | typedef 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 | |
|---|
| 156 | INLINE_HEADER rtsBool |
|---|
| 157 | isBoundTask (Task *task) |
|---|
| 158 | { |
|---|
| 159 | return (task->incall->tso != NULL); |
|---|
| 160 | } |
|---|
| 161 | |
|---|
| 162 | |
|---|
| 163 | // Linked list of all tasks. |
|---|
| 164 | // |
|---|
| 165 | extern Task *all_tasks; |
|---|
| 166 | |
|---|
| 167 | // Start and stop the task manager. |
|---|
| 168 | // Requires: sched_mutex. |
|---|
| 169 | // |
|---|
| 170 | void initTaskManager (void); |
|---|
| 171 | nat freeTaskManager (void); |
|---|
| 172 | |
|---|
| 173 | // Create a new Task for a bound thread |
|---|
| 174 | // Requires: sched_mutex. |
|---|
| 175 | // |
|---|
| 176 | Task *newBoundTask (void); |
|---|
| 177 | |
|---|
| 178 | // The current task is a bound task that is exiting. |
|---|
| 179 | // Requires: sched_mutex. |
|---|
| 180 | // |
|---|
| 181 | void 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. |
|---|
| 189 | void workerTaskStop (Task *task); |
|---|
| 190 | #endif |
|---|
| 191 | |
|---|
| 192 | // Put the task back on the free list, mark it stopped. Used by |
|---|
| 193 | // forkProcess(). |
|---|
| 194 | // |
|---|
| 195 | void discardTasksExcept (Task *keep); |
|---|
| 196 | |
|---|
| 197 | // Get the Task associated with the current OS thread (or NULL if none). |
|---|
| 198 | // |
|---|
| 199 | INLINE_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 | // |
|---|
| 208 | void startWorkerTask (Capability *cap); |
|---|
| 209 | |
|---|
| 210 | // Interrupts a worker task that is performing an FFI call. The thread |
|---|
| 211 | // should not be destroyed. |
|---|
| 212 | // |
|---|
| 213 | void 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 | // |
|---|
| 220 | void updateCapabilityRefs (void); |
|---|
| 221 | |
|---|
| 222 | // For stats |
|---|
| 223 | extern nat taskCount; |
|---|
| 224 | extern nat workerCount; |
|---|
| 225 | extern 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 |
|---|
| 238 | extern __thread Task *my_task; |
|---|
| 239 | #else |
|---|
| 240 | extern 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) |
|---|
| 246 | extern ThreadLocalKey gctKey; |
|---|
| 247 | #endif |
|---|
| 248 | #else |
|---|
| 249 | extern 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 | // |
|---|
| 258 | INLINE_HEADER Task * |
|---|
| 259 | myTask (void) |
|---|
| 260 | { |
|---|
| 261 | #if defined(THREADED_RTS) && !defined(MYTASK_USE_TLV) |
|---|
| 262 | return getThreadLocalVar(¤tTaskKey); |
|---|
| 263 | #else |
|---|
| 264 | return my_task; |
|---|
| 265 | #endif |
|---|
| 266 | } |
|---|
| 267 | |
|---|
| 268 | INLINE_HEADER void |
|---|
| 269 | setMyTask (Task *task) |
|---|
| 270 | { |
|---|
| 271 | #if defined(THREADED_RTS) && !defined(MYTASK_USE_TLV) |
|---|
| 272 | setThreadLocalVar(¤tTaskKey,task); |
|---|
| 273 | #else |
|---|
| 274 | my_task = task; |
|---|
| 275 | #endif |
|---|
| 276 | } |
|---|
| 277 | |
|---|
| 278 | #include "EndPrivate.h" |
|---|
| 279 | |
|---|
| 280 | #endif /* TASK_H */ |
|---|