/// <summary> Process tasks until w is done. /// Equivalent to <code>while(!w.isDone()) taskYield(); </code> /// /// </summary> protected internal void taskJoin(FJTask w) { while (!w.Done) { FJTask task = pop(); if (task != null) { if (!task.Done) { if (CollectStats) { ++runs; } task.Run(); task.SetDone(); if (task == w) { return; // fast exit if we just ran w } } } else { scan(w); } } }
/* ------------ composite operations ------------------- */ /// <summary> Main runloop /// /// </summary> private void DoStart() { try { while (!Interrupted) { FJTask task = pop(); if (task != null) { if (!task.Done) { // inline FJTask.invoke if (CollectStats) { ++runs; } task.Run(); task.SetDone(); } } else { scanWhileIdling(); } } } finally { group_.Inactive = this; } }
/// <summary> Immediately execute task t by calling its run method. Has no /// effect if t has already been run or has been cancelled. /// It is equivalent to calling t.run except that it /// deals with completion status, so should always be used /// instead of directly calling run. /// The method can be useful /// when a computation has been packaged as a FJTask, but you just need to /// directly execute its body from within some other task. /// /// </summary> public static void Invoke(FJTask t) { if (!t.Done) { t.Run(); t.SetDone(); } }
/// <summary> A specialized expansion of /// <code> w.fork(); invoke(v); w.join(); </code> /// /// </summary> protected internal void coInvoke(FJTask w, FJTask v) { // inline push int t = top; if (t < (base_Renamed & (deq.Length - 1)) + deq.Length) { deq[t & (deq.Length - 1)].Put(w); top = t + 1; // inline invoke if (!v.Done) { if (CollectStats) { ++runs; } v.Run(); v.SetDone(); } // inline taskJoin while (!w.Done) { FJTask task = pop(); if (task != null) { if (!task.Done) { if (CollectStats) { ++runs; } task.Run(); task.SetDone(); if (task == w) { return; // fast exit if we just ran w } } } else { scan(w); } } } // handle non-inlinable cases else { slowCoInvoke(w, v); } }
/// <summary> Execute a task in this thread. Generally called when current task /// cannot otherwise continue. /// /// </summary> protected internal void taskYield() { FJTask task = pop(); if (task != null) { if (!task.Done) { if (CollectStats) { ++runs; } task.Run(); task.SetDone(); } } else { scan(null); } }
/// <summary> A specialized expansion of /// <code> w.fork(); invoke(v); w.join(); </code> /// /// </summary> protected internal void coInvoke(FJTask w, FJTask v) { // inline push int t = top; if (t < (base_Renamed & (deq.Length - 1)) + deq.Length) { deq[t & (deq.Length - 1)].Put(w); top = t + 1; // inline invoke if (!v.Done) { if (CollectStats) ++runs; v.Run(); v.SetDone(); } // inline taskJoin while (!w.Done) { FJTask task = pop(); if (task != null) { if (!task.Done) { if (CollectStats) ++runs; task.Run(); task.SetDone(); if (task == w) return ; // fast exit if we just ran w } } else scan(w); } } // handle non-inlinable cases else slowCoInvoke(w, v); }
/// <summary> Same as scan, but called when current thread is idling. /// It repeatedly scans other threads for tasks, /// sleeping while none are available. /// <p> /// This differs from scan mainly in that /// since there is no reason to return to recheck any /// condition, we iterate until a task is found, backing /// off via sleeps if necessary. /// </p> /// /// </summary> protected internal virtual void scanWhileIdling() { FJTask task = null; bool lowered = false; long iters = 0; FJTaskRunner[] ts = group_.Array; int idx = victimRNG.Next(ts.Length); do { for (int i = 0; i < ts.Length; ++i) { FJTaskRunner t = ts[idx]; if (++idx >= ts.Length) { idx = 0; // circularly traverse } if (t != null && t != this) { if (CollectStats) { ++scans; } task = t.take(); if (task != null) { if (CollectStats) { ++steals; } if (lowered) { Priority = (ThreadPriority)runPriority_; } group_.SetActive(this); break; } } } if (task == null) { if (Interrupted) { return; } if (CollectStats) { ++scans; } task = group_.PollEntryQueue(this); if (task != null) { if (CollectStats) { ++steals; } if (lowered) { Priority = (ThreadPriority)runPriority_; } group_.SetActive(this); } else { ++iters; // Check here for yield vs sleep to avoid entering group synch lock if (iters >= FJTaskRunnerGroup.ScansPerSleep) { group_.CheckActive(this, iters); if (Interrupted) { return; } } else if (!lowered) { lowered = true; Priority = (ThreadPriority)scanPriority_; } else { Thread.Sleep(0); } } } }while (task == null); if (!task.Done) { if (CollectStats) { ++runs; } task.Run(); task.SetDone(); } }
/* ------------ Scheduling ------------------- */ /// <summary> Do all but the pop() part of yield or join, by /// traversing all DEQs in our group looking for a task to /// steal. If none, it checks the entry queue. /// <p> /// Since there are no good, portable alternatives, /// we rely here on a mixture of Thread.yield and priorities /// to reduce wasted spinning, even though these are /// not well defined. We are hoping here that the JVM /// does something sensible. /// </p> /// </summary> /// <param name="waitingFor">if non-null, the current task being joined /// /// </param> protected internal virtual void scan(FJTask waitingFor) { FJTask task = null; // to delay lowering priority until first failure to steal bool lowered = false; /* * Circularly traverse from a random start index. * * This differs slightly from cilk version that uses a random index * for each attempted steal. * Exhaustive scanning might impede analytic tractablity of * the scheduling policy, but makes it much easier to deal with * startup and shutdown. */ FJTaskRunner[] ts = group_.Array; int idx = victimRNG.Next(ts.Length); for (int i = 0; i < ts.Length; ++i) { FJTaskRunner t = ts[idx]; if (++idx >= ts.Length) { idx = 0; // circularly traverse } if (t != null && t != this) { if (waitingFor != null && waitingFor.Done) { break; } else { if (CollectStats) { ++scans; } task = t.take(); if (task != null) { if (CollectStats) { ++steals; } break; } else { if (Interrupted) { break; } else if (!lowered) { // if this is first fail, lower priority lowered = true; Priority = (ThreadPriority)scanPriority_; } else { // otherwise we are at low priority; just yield Thread.Sleep(0); } } } } } if (task == null) { if (CollectStats) { ++scans; } task = group_.PollEntryQueue(this); if (CollectStats) { if (task != null) { ++steals; } } } if (lowered) { Priority = (ThreadPriority)runPriority_; } if (task != null && !task.Done) { if (CollectStats) { ++runs; } task.Run(); task.SetDone(); } }
/// <summary> Array-based version of coInvoke /// /// </summary> protected internal void coInvoke(FJTask[] tasks) { int nforks = tasks.Length - 1; // inline bulk push of all but one task int t = top; if (nforks >= 0 && t + nforks < (base_Renamed & (deq.Length - 1)) + deq.Length) { for (int i = 0; i < nforks; ++i) { deq[t++ & (deq.Length - 1)].Put(tasks[i]); top = t; } // inline invoke of one task FJTask v = tasks[nforks]; if (!v.Done) { if (CollectStats) { ++runs; } v.Run(); v.SetDone(); } // inline taskJoins for (int i = 0; i < nforks; ++i) { FJTask w = tasks[i]; while (!w.Done) { FJTask task = pop(); if (task != null) { if (!task.Done) { if (CollectStats) { ++runs; } task.Run(); task.SetDone(); } } else { scan(w); } } } } // handle non-inlinable cases else { slowCoInvoke(tasks); } }