/* ------------ 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> 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); } } }
/// <summary> Enqueue task at base of DEQ. /// Called ONLY by current thread. /// This method is currently not called from class FJTask. It could be used /// as a faster way to do FJTask.start, but most users would /// find the semantics too confusing and unpredictable. /// /// </summary> protected internal void put(FJTask r) { lock (this) { for (; ;) { int b = base_Renamed - 1; if (top < b + deq.Length) { int newBase = b & (deq.Length - 1); deq[newBase].Put(r); base_Renamed = newBase; if (b != newBase) { // Adjust for index underflow int newTop = top & (deq.Length - 1); if (newTop < newBase) { newTop += deq.Length; } top = newTop; } return; } else { checkOverflow(); // ... and retry } } } }
/// <summary> /// Runs this instance. /// </summary> public override void Run() { for (int i = 0; i < tasks.Length; ++i) { FJTask.Invoke(tasks[i]); } }
/// <summary> Handle slow case for push /// /// </summary> protected internal virtual void slowPush(FJTask r) { lock (this) { checkOverflow(); push(r); // just recurse -- this one is sure to succeed. } }
/// <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> /// Specialized form of execute called only from within FJTasks /// </summary> public virtual void ExecuteTask(FJTask t) { try { entryQueue.Put(t); SignalNewTask(); } catch (System.Threading.ThreadInterruptedException) { ThreadClass.Current.Interrupt(); } }
/// <summary> /// <see cref="IRunnable"/> /// </summary> public override void Run() { try { if (wrapped is FJTask) { FJTask.Invoke((FJTask)(wrapped)); } else { wrapped.Run(); } } finally { SetTerminated(); } }
/// <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); } }
/* ------------ DEQ operations ------------------- */ /// <summary> Push a task onto DEQ. /// Called ONLY by current thread. /// /// </summary> protected internal void push(FJTask r) { int t = top; /* * This test catches both overflows and index wraps. It doesn't * really matter if base value is in the midst of changing in take. * As long as deq length is < 2^30, we are guaranteed to catch wrap in * time since base can only be incremented at most length times * between pushes (or puts). */ if (t < (base_Renamed & (deq.Length - 1)) + deq.Length) { deq[t & (deq.Length - 1)].Put(r); top = t + 1; } // isolate slow case to increase chances push is inlined else { slowPush(r); // check overflow and retry } }
/// <summary>Set the reference *</summary> internal void Put(FJTask r) { ref_ = r; }
/// <summary> Backup to handle noninlinable cases of coInvoke /// /// </summary> protected internal virtual void slowCoInvoke(FJTask w, FJTask v) { push(w); // let push deal with overflow FJTask.Invoke(v); taskJoin(w); }
/// <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(); } }
/// <summary> Enqueue task at base of DEQ. /// Called ONLY by current thread. /// This method is currently not called from class FJTask. It could be used /// as a faster way to do FJTask.start, but most users would /// find the semantics too confusing and unpredictable. /// /// </summary> protected internal void put(FJTask r) { lock (this) { for (; ; ) { int b = base_Renamed - 1; if (top < b + deq.Length) { int newBase = b & (deq.Length - 1); deq[newBase].Put(r); base_Renamed = newBase; if (b != newBase) { // Adjust for index underflow int newTop = top & (deq.Length - 1); if (newTop < newBase) newTop += deq.Length; top = newTop; } return ; } else { checkOverflow(); // ... and retry } } } }
/// <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); }
/// <summary> Construct and return a FJTask object that, when executed, will /// invoke the tasks in the tasks array in parallel using coInvoke /// /// </summary> public static FJTask NewPar(FJTask[] tasks, FJTaskRunner runner) { return new Par(tasks); }
/// <summary> /// Two-task constructor, for compatibility with previous release. /// </summary> public Seq(FJTask task1, FJTask task2) : this(new FJTask[] { task1, task2 }) { }
/// <summary> Fork both tasks and then wait for their completion. It behaves as: /// <pre> /// task1.fork(); task2.fork(); task2.join(); task1.join(); /// </pre> /// As a simple classic example, here is /// a class that computes the Fibonacci function: /// <pre> /// public class Fib extends FJTask { /// /// // Computes fibonacci(n) = fibonacci(n-1) + fibonacci(n-2); for n> 1 /// // fibonacci(0) = 0; /// // fibonacci(1) = 1. /// /// // Value to compute fibonacci function for. /// // It is replaced with the answer when computed. /// private volatile int number; /// /// public Fib(int n) { number = n; } /// /// public int getAnswer() { /// if (!isDone()) throw new Error("Not yet computed"); /// return number; /// } /// /// public void run() { /// int n = number; /// if (n > 1) { /// Fib f1 = new Fib(n - 1); /// Fib f2 = new Fib(n - 2); /// /// coInvoke(f1, f2); // run these in parallel /// /// // we know f1 and f2 are computed, so just directly access numbers /// number = f1.number + f2.number; /// } /// } /// /// public static void main(String[] args) { // sample driver /// try { /// int groupSize = 2; // 2 worker threads /// int num = 35; // compute fib(35) /// FJTaskRunnerGroup group = new FJTaskRunnerGroup(groupSize); /// Fib f = new Fib(num); /// group.invoke(f); /// int result = f.getAnswer(); /// System.out.println(" Answer: " + result); /// } /// catch (InterruptedException ex) { /// System.out.println("Interrupted"); /// } /// } /// } /// </pre> /// /// </summary> public void CoInvoke(FJTask task1, FJTask task2) { FJTaskRunner.coInvoke(task1, task2); }
/// <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); } }
/// <summary>Return the reference and clear it *</summary> internal FJTask Take() { FJTask r = ref_; ref_ = null; return(r); }
/// <summary>Return the reference and clear it *</summary> internal FJTask Take() { FJTask r = ref_; ref_ = null; return r; }
/// <summary> Construct and return a FJTask object that, when executed, will /// invoke task1 and task2, in order /// /// </summary> public static FJTask NewSeq(FJTask task1, FJTask task2, FJTaskRunner fjTaskRunner) { return new Seq2(task1, task2); }
/// <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> Fork all tasks in array, and await their completion. /// Behaviorally equivalent to: /// <pre> /// for (int i = 0; i < tasks.length; ++i) tasks[i].fork(); /// for (int i = 0; i < tasks.length; ++i) tasks[i].join(); /// </pre> /// /// </summary> public void CoInvoke(FJTask[] tasks) { FJTaskRunner.coInvoke(tasks); }
/* ------------ DEQ operations ------------------- */ /// <summary> Push a task onto DEQ. /// Called ONLY by current thread. /// /// </summary> protected internal void push(FJTask r) { int t = top; /* This test catches both overflows and index wraps. It doesn't really matter if base value is in the midst of changing in take. As long as deq length is < 2^30, we are guaranteed to catch wrap in time since base can only be incremented at most length times between pushes (or puts). */ if (t < (base_Renamed & (deq.Length - 1)) + deq.Length) { deq[t & (deq.Length - 1)].Put(r); top = t + 1; } // isolate slow case to increase chances push is inlined else slowPush(r); // check overflow and retry }
/// <summary> Two-task constructor, for compatibility with previous release. /// /// </summary> public Par(FJTask task1, FJTask task2) { this.tasks = new FJTask[]{task1, task2}; }
/* ------------ 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> Construct a Seq that, when executed, will process each of the /// tasks in the tasks array in order /// /// </summary> public Seq(FJTask[] tasks) { this.tasks = tasks; }
/// <summary> Backup to handle atypical or noninlinable cases of coInvoke /// /// </summary> protected internal virtual void slowCoInvoke(FJTask[] tasks) { for (int i = 0; i < tasks.Length; ++i) push(tasks[i]); for (int i = 0; i < tasks.Length; ++i) taskJoin(tasks[i]); }
/// <summary> /// Creates a new <see cref="Seq2"/> instance. /// </summary> /// <param name="task1">Task1.</param> /// <param name="task2">Task2.</param> public Seq2(FJTask task1, FJTask task2) { fst = task1; snd = task2; }
/* ------------ 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> Construct and return a FJTask object that, when executed, will /// invoke the tasks in the tasks array in array order /// </summary> public static FJTask NewSeq(FJTask[] tasks, FJTaskRunner fjTaskRunner) { return new Seq(tasks); }
/// <summary> Two-task constructor, for compatibility with previous release. /// /// </summary> public Par(FJTask task1, FJTask task2) { this.tasks = new FJTask[] { task1, task2 }; }
/// <summary> Construct a Seq that, when executed, will process each of the /// tasks in the tasks array in parallel /// /// </summary> public Par(FJTask[] tasks) { this.tasks = tasks; }
/// <summary> /// Invoke first and second task /// </summary> public override void Run() { FJTask.Invoke(fst); FJTask.Invoke(snd); }
/// <summary> /// Creates a new <see cref="Par2"/> instance. /// </summary> /// <param name="task1">Task1.</param> /// <param name="task2">Task2.</param> public Par2(FJTask task1, FJTask task2) { fst = task1; snd = task2; }
/// <summary> /// Two-task constructor, for compatibility with previous release. /// </summary> public Seq(FJTask task1, FJTask task2) : this(new FJTask[]{task1, task2}) { }
/// <summary> Construct and return a FJTask object that, when executed, will /// invoke task1 and task2, in parallel /// /// </summary> public static FJTask NewPar(FJTask task1, FJTask task2, FJTaskRunner fjTaskRunner) { return(new Par2(task1, task2)); }
/// <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); } }