Fix numa001 failure with "too many NUMA nodes"
[ghc.git] / rts / Task.h
index f91872f..9323459 100644 (file)
@@ -5,7 +5,7 @@
  * Tasks
  *
  * For details on the high-level design, see
- *   http://hackage.haskell.org/trac/ghc/wiki/Commentary/Rts/Scheduler
+ *   http://ghc.haskell.org/trac/ghc/wiki/Commentary/Rts/Scheduler
  *
  * -------------------------------------------------------------------------*/
 
 
 #include "BeginPrivate.h"
 
-/* 
+/*
    Definition of a Task
    --------------------
+
    A task is an OSThread that runs Haskell code.  Every OSThread that
    runs inside the RTS, whether as a worker created by the RTS or via
    an in-call from C to Haskell, has an associated Task.  The first
@@ -29,7 +29,7 @@
    There is a one-to-one relationship between OSThreads and Tasks.
    The Task for an OSThread is kept in thread-local storage, and can
    be retrieved at any time using myTask().
-   
+
    In the THREADED_RTS build, multiple Tasks may all be running
    Haskell code simultaneously. A task relinquishes its Capability
    when it is asked to evaluate an external (C) call.
    Ownership of Task
    -----------------
 
-   The OS thread named in the Task structure has exclusive access to
-   the structure, as long as it is the running_task of its Capability.
-   That is, if (task->cap->running_task == task), then task->id owns
-   the Task.  Otherwise the Task is owned by the owner of the parent
-   data structure on which it is sleeping; for example, if the task is
-   sleeping on spare_workers field of a Capability, then the owner of the
+   Task ownership is a little tricky.  The default situation is that
+   the Task is an OS-thread-local structure that is owned by the OS
+   thread named in task->id.  An OS thread not currently executing
+   Haskell code might call newBoundTask() at any time, which assumes
+   that it has access to the Task for the current OS thread.
+
+   The all_next and all_prev fields of a Task are owned by
+   all_tasks_mutex, which must also be taken if we want to create or
+   free a Task.
+
+   For an OS thread in Haskell, if (task->cap->running_task != task),
+   then the Task is owned by the owner of the parent data structure on
+   which it is sleeping; for example, if the task is sleeping on
+   spare_workers field of a Capability, then the owner of the
    Capability has access to the Task.
 
    When a task is migrated from sleeping on one Capability to another,
@@ -56,7 +64,7 @@
 
       (a) waiting on the condition task->cond.  The Task is either
          (1) a bound Task, the TSO will be on a queue somewhere
-        (2) a worker task, on the spare_workers queue of task->cap.
+         (2) a worker task, on the spare_workers queue of task->cap.
 
      (b) making a foreign call.  The InCall will be on the
          suspended_ccalls list.
@@ -66,8 +74,8 @@
       (a) the task is currently blocked in yieldCapability().
           This call will return when we have ownership of the Task and
           a Capability.  The Capability we get might not be the same
-         as the one we had when we called yieldCapability().
-          
+          as the one we had when we called yieldCapability().
+
       (b) we must call resumeThread(task), which will safely establish
           ownership of the Task and a Capability.
 */
@@ -78,7 +86,7 @@ typedef struct InCall_ {
     StgTSO *   tso;             // the bound TSO (or NULL for a worker)
 
     StgTSO *   suspended_tso;   // the TSO is stashed here when we
-                               // make a foreign call (NULL otherwise);
+                                // make a foreign call (NULL otherwise);
 
     Capability *suspended_cap;  // The capability that the
                                 // suspended_tso is on, because
@@ -86,7 +94,7 @@ typedef struct InCall_ {
                                 // without owning a Capability in the
                                 // first place.
 
-    SchedulerStatus  stat;      // return status
+    SchedulerStatus  rstat;     // return status
     StgClosure **    ret;       // return value
 
     struct Task_ *task;
@@ -105,10 +113,16 @@ typedef struct InCall_ {
 
 typedef struct Task_ {
 #if defined(THREADED_RTS)
-    OSThreadId id;             // The OS Thread ID of this task
+    OSThreadId id;              // The OS Thread ID of this task
+
+    // The NUMA node this Task belongs to.  If this is a worker thread, then the
+    // OS thread will be bound to this node (see workerStart()).  If this is an
+    // external thread calling into Haskell, it can be bound to a node using
+    // rts_setInCallCapability().
+    uint32_t node;
 
     Condition cond;             // used for sleeping & waking up this task
-    Mutex lock;                        // lock for the condition variable
+    Mutex lock;                 // lock for the condition variable
 
     // this flag tells the task whether it should wait on task->cond
     // or just continue immediately.  It's a workaround for the fact
@@ -117,81 +131,99 @@ typedef struct Task_ {
     rtsBool wakeup;
 #endif
 
-    // This points to the Capability that the Task "belongs" to.  If
-    // the Task owns a Capability, then task->cap points to it.  If
-    // the task does not own a Capability, then either (a) if the task
-    // is a worker, then task->cap points to the Capability it belongs
-    // to, or (b) it is returning from a foreign call, then task->cap
-    // points to the Capability with the returning_worker queue that this
-    // this Task is on.
+    // If the task owns a Capability, task->cap points to it.  (occasionally a
+    // task may own multiple capabilities, in which case task->cap may point to
+    // any of them.  We must be careful to set task->cap to the appropriate one
+    // when using Capability APIs.)
     //
-    // When a task goes to sleep, it may be migrated to a different
-    // Capability.  Hence, we always check task->cap on wakeup.  To
-    // syncrhonise between the migrater and the migratee, task->lock
-    // must be held when modifying task->cap.
+    // If the task is a worker, task->cap points to the Capability on which it
+    // is queued.
+    //
+    // If the task is in an unsafe foreign call, then task->cap can be used to
+    // retrieve the capability (see rts_unsafeGetMyCapability()).
     struct Capability_ *cap;
 
     // The current top-of-stack InCall
     struct InCall_ *incall;
 
-    nat n_spare_incalls;
+    uint32_t n_spare_incalls;
     struct InCall_ *spare_incalls;
 
     rtsBool    worker;          // == rtsTrue if this is a worker Task
-    rtsBool    stopped;         // this task has stopped or exited Haskell
+    rtsBool    stopped;         // == rtsTrue between newBoundTask and
+                                // boundTaskExiting, or in a worker Task.
 
     // So that we can detect when a finalizer illegally calls back into Haskell
     rtsBool running_finalizers;
 
-    // Stats that we collect about this task
-    // ToDo: we probably want to put this in a separate TaskStats
-    // structure, so we can share it between multiple Tasks.  We don't
-    // really want separate stats for each call in a nested chain of
-    // foreign->haskell->foreign->haskell calls, but we'll get a
-    // separate Task for each of the haskell calls.
-    Ticks       elapsedtimestart;
-    Ticks       muttimestart;
-    Ticks       mut_time;
-    Ticks       mut_etime;
-    Ticks       gc_time;
-    Ticks       gc_etime;
+    // if >= 0, this Capability will be used for in-calls
+    int preferred_capability;
 
     // Links tasks on the returning_tasks queue of a Capability, and
     // on spare_workers.
     struct Task_ *next;
 
-    // Links tasks on the all_tasks list
-    struct Task_ *all_link;
+    // Links tasks on the all_tasks list; need ACQUIRE_LOCK(&all_tasks_mutex)
+    struct Task_ *all_next;
+    struct Task_ *all_prev;
 
 } Task;
 
 INLINE_HEADER rtsBool
-isBoundTask (Task *task) 
+isBoundTask (Task *task)
 {
     return (task->incall->tso != NULL);
 }
 
+// A Task is currently a worker if
+//  (a) it was created as a worker (task->worker), and
+//  (b) it has not left and re-entered Haskell, in which case
+//      task->incall->prev_stack would be non-NULL.
+//
+INLINE_HEADER rtsBool
+isWorker (Task *task)
+{
+    return (task->worker && task->incall->prev_stack == NULL);
+}
 
 // Linked list of all tasks.
 //
 extern Task *all_tasks;
 
+// The all_tasks list is protected by the all_tasks_mutex
+#if defined(THREADED_RTS)
+extern Mutex all_tasks_mutex;
+#endif
+
 // Start and stop the task manager.
 // Requires: sched_mutex.
 //
 void initTaskManager (void);
-nat  freeTaskManager (void);
+uint32_t  freeTaskManager (void);
 
-// Create a new Task for a bound thread
-// Requires: sched_mutex.
+// Create a new Task for a bound thread.  This Task must be released
+// by calling boundTaskExiting.  The Task is cached in
+// thread-local storage and will remain even after boundTaskExiting()
+// has been called; to free the memory, see freeMyTask().
 //
-Task *newBoundTask (void);
+Task* newBoundTask (void);
+
+// Return the current OS thread's Task, which is created if it doesn't already
+// exist.  After you have finished using RTS APIs, you should call freeMyTask()
+// to release this thread's Task.
+Task* getTask (void);
 
 // The current task is a bound task that is exiting.
-// Requires: sched_mutex.
 //
 void boundTaskExiting (Task *task);
 
+// Free a Task if one was previously allocated by newBoundTask().
+// This is not necessary unless the thread that called newBoundTask()
+// will be exiting, or if this thread has finished calling Haskell
+// functions.
+//
+void freeMyTask(void);
+
 // Notify the task manager that a task has stopped.  This is used
 // mainly for stats-gathering purposes.
 // Requires: sched_mutex.
@@ -201,12 +233,6 @@ void boundTaskExiting (Task *task);
 void workerTaskStop (Task *task);
 #endif
 
-// Record the time spent in this Task.
-// This is called by workerTaskStop() but not by boundTaskExiting(),
-// because it would impose an extra overhead on call-in.
-//
-void taskTimeStamp (Task *task);
-
 // Put the task back on the free list, mark it stopped.  Used by
 // forkProcess().
 //
@@ -225,16 +251,28 @@ INLINE_HEADER Task *myTask (void);
 //
 void startWorkerTask (Capability *cap);
 
+// Interrupts a worker task that is performing an FFI call.  The thread
+// should not be destroyed.
+//
+void interruptWorkerTask (Task *task);
+
 #endif /* THREADED_RTS */
 
+// For stats
+extern uint32_t taskCount;
+extern uint32_t workerCount;
+extern uint32_t peakWorkerCount;
+
 // -----------------------------------------------------------------------------
 // INLINE functions... private from here on down:
 
 // A thread-local-storage key that we can use to get access to the
 // current thread's Task structure.
 #if defined(THREADED_RTS)
-#if defined(linux_HOST_OS) && \
-    (defined(i386_HOST_ARCH) || defined(x86_64_HOST_ARCH))
+#if ((defined(linux_HOST_OS) && \
+     (defined(i386_HOST_ARCH) || defined(x86_64_HOST_ARCH))) || \
+    (defined(mingw32_HOST_OS) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 4)) && \
+    (!defined(llvm_CC_FLAVOR))
 #define MYTASK_USE_TLV
 extern __thread Task *my_task;
 #else
@@ -270,6 +308,44 @@ setMyTask (Task *task)
 #endif
 }
 
+// Tasks are identified by their OS thread ID, which can be serialised
+// to StgWord64, as defined below.
+typedef StgWord64 TaskId;
+
+// Get a unique serialisable representation for a task id.
+//
+// It's only unique within the process. For example if they are emitted in a
+// log file then it is suitable to work out which log entries are releated.
+//
+// This is needed because OSThreadId is an opaque type
+// and in practice on some platforms it is a pointer type.
+//
+#if defined(THREADED_RTS)
+INLINE_HEADER TaskId serialiseTaskId (OSThreadId taskID) {
+#if defined(freebsd_HOST_OS) || defined(darwin_HOST_OS)
+    // Here OSThreadId is a pthread_t and pthread_t is a pointer, but within
+    // the process we can still use that pointer value as a unique id.
+    return (TaskId) (size_t) taskID;
+#else
+    // On Windows, Linux and others it's an integral type to start with.
+    return (TaskId) taskID;
+#endif
+}
+#endif
+
+//
+// Get a serialisable Id for the Task's OS thread
+// Needed mainly for logging since the OSThreadId is an opaque type
+INLINE_HEADER TaskId
+serialisableTaskId (Task *task)
+{
+#if defined(THREADED_RTS)
+    return serialiseTaskId(task->id);
+#else
+    return (TaskId) (size_t) task;
+#endif
+}
+
 #include "EndPrivate.h"
 
 #endif /* TASK_H */