csharp/aeroson/lego-game-and-Unity-like-engine/MyEngine/bepuphysics/Threading/SimpleLooper.cs

SimpleLooper.cs
using System;
using System.Collections.Generic;
using System.Threading;

namespace BEPUphysics.Threading
{
    /// 
    /// Manages the engine's threads.
    /// 
    /// 
    /// Uses a simple round-robin threadpool.
    /// It is recommended that other thread managers are used instead of this one;
    /// it is kept for compatibility and a fallback in case of problems.
    /// 
    public clast SimpleLooper : IParallelLooper
    {
        private readonly ManualResetEvent allThreadsIdleNotifier = new ManualResetEvent(false);
        private readonly object disposedLocker = new object();
        private readonly Action doLoopSectionDelegate;
        private readonly List taskInfos = new List();

        private readonly List workers = new List();

        /// 
        /// Index into the thread loop lists, incremented after each task allocation.
        /// 
        private int currentTaskAllocationIndex;

        private bool disposed;
        private int loopTasksPerThread;

        private int tasksRemaining = 1;

        /// 
        /// Constructs the thread manager.
        /// 
        public SimpleLooper()
        {
            LoopTasksPerThread = 1;
            doLoopSectionDelegate = new Action(DoLoopSection);
        }

        /// 
        /// Releases resources used by the object.
        /// 
        ~SimpleLooper()
        {
            Dispose();
        }

        /// 
        /// Gets or sets the number of tasks to create per thread when doing forLoops.
        /// 
        public int LoopTasksPerThread
        {
            get { return loopTasksPerThread; }
            set
            {
                loopTasksPerThread = value;
                RemakeLoopSections();
            }
        }
        
        /// 
        /// Gets the number of threads currently handled by the manager.
        /// 
        public int ThreadCount
        {
            get { return workers.Count; }
        }


        /// 
        /// Blocks the current thread until all tasks have been completed.
        /// 
        public void WaitForTaskCompletion()
        {
            if (Interlocked.Decrement(ref tasksRemaining) == 0)
            {
                allThreadsIdleNotifier.Set();
            }
            allThreadsIdleNotifier.WaitOne();

            //When it gets here, it means things are successfully idle'd.
            tasksRemaining = 1;
            allThreadsIdleNotifier.Reset();
        }


        /// 
        /// Adds a thread to the manager.
        /// 
        public void AddThread()
        {
            AddThread(null, null);
        }

        /// 
        /// Adds a thread to the manager.
        /// 
        /// A function to run to perform any initialization on the new thread.
        /// Data to give the ParameterizedThreadStart for initialization.
        public void AddThread(Action initialization, object initializationInformation)
        {
            lock (workers)
            {
                var worker = new WorkerThread(this, initialization, initializationInformation);
                workers.Add(worker);
                RemakeLoopSections();
            }
        }


        /// 
        /// Removes a thread and blocks until success.
        /// 
        public void RemoveThread()
        {
            EnqueueTask(null, null);
            WaitForTaskCompletion();
            RemakeLoopSections();
        }


        /// 
        /// Gives the thread manager a new task to run.
        /// 
        /// Task to run.
        /// Information to be used by the task.
        public void EnqueueTask(Action task, object taskInformation)
        {
            lock (workers)
            {
                workers[currentTaskAllocationIndex].EnqueueTask(task, taskInformation);
                currentTaskAllocationIndex = (currentTaskAllocationIndex + 1) % workers.Count;
            }
        }


        /// 
        /// Loops from the starting index (inclusive) to the ending index (exclusive), calling the loopBody at each iteration.
        /// The forLoop function will not return until all iterations are complete.
        /// This is meant to be used in a 'fork-join' model; only a single thread should be running a forLoop
        /// at any time.
        /// 
        /// Inclusive starting index.
        /// Exclusive ending index.
        /// Function that handles an individual iteration of the loop.
        public void ForLoop(int startIndex, int endIndex, Action loopBody)
        {
            int subdivisions = workers.Count * loopTasksPerThread;
            int iterationCount = endIndex - startIndex;
            for (int b = 0; b < subdivisions; b++)
            {
                taskInfos[b].loopBody = loopBody;
                taskInfos[b].iterationCount = iterationCount;
                EnqueueTaskSequentially(doLoopSectionDelegate, taskInfos[b]);
            }
            WaitForTaskCompletion();
        }


        /// 
        /// Releases threads and resources used by the thread manager.
        /// 
        public void Dispose()
        {
            lock (disposedLocker)
            {
                if (!disposed)
                {
                    disposed = true;
                    ShutDown();
                    allThreadsIdleNotifier.Close();
                    GC.SuppressFinalize(this);
                }
            }
        }


        /// 
        /// Enqueues a task.
        /// This method also does not perform any locking; it should only be called when all worker threads of the thread pool are idle and all calls to this method are from the same thread.
        /// 
        /// Task to enqueue.
        /// Information for the task.
        public void EnqueueTaskSequentially(Action task, object taskInformation)
        {
            //enqueueTask(task, taskInformation);
            workers[currentTaskAllocationIndex].EnqueueTask(task, taskInformation);
            currentTaskAllocationIndex = (currentTaskAllocationIndex + 1) % workers.Count;
            //workers[currentTaskAllocationIndex].enqueueTaskSequentially(task, taskInformation);
            //currentTaskAllocationIndex = (currentTaskAllocationIndex + 1) % workers.Count;
        }

        /// 
        /// Tells every thread in the thread manager to shut down and waits until completion.
        /// 
        public void ShutDown()
        {
            var toJoin = new Queue();

            for (int i = workers.Count - 1; i >= 0; i--)
            {
                lock (workers)
                {
                    toJoin.Enqueue(workers[i].Thread);
                    workers[i].EnqueueTask(null, null);
                }
            }
            while (toJoin.Count > 0)
            {
                toJoin.Dequeue().Join();
            }
        }

        private static void DoLoopSection(object o)
        {
            var data = o as LoopSection;
            int finalIndex = (data.iterationCount * (data.Index + 1)) / data.Subdivisions;
            for (int i = (data.iterationCount * data.Index) / data.Subdivisions; i < finalIndex; i++)
            {
                //do stuff
                data.loopBody(i);
            }
        }

        private void RemakeLoopSections()
        {
            taskInfos.Clear();
            int workerCount = workers.Count;
            int subdivisions = workerCount * loopTasksPerThread;
            for (int i = 0; i < workerCount; i++)
            {
                for (int j = 0; j < loopTasksPerThread; j++)
                {
                    taskInfos.Add(new LoopSection(i * loopTasksPerThread + j, subdivisions));
                }
            }
        }

        private clast LoopSection
        {
            internal readonly int Index;
            internal readonly int Subdivisions;
            internal int iterationCount;
            internal Action loopBody;

            internal LoopSection(int index, int subdivisions)
            {
                Index = index;
                Subdivisions = subdivisions;
            }
        }

        private clast WorkerThread : IDisposable
        {
            private readonly object disposedLocker = new object();
            private readonly object initializationInformation;
            private readonly SimpleLooper manager;
            private readonly AutoResetEvent resetEvent = new AutoResetEvent(false);
            private readonly Queue taskInformationQueue;
            private readonly Queue taskQueue;
            internal readonly Thread Thread;
            private readonly Action threadStart;
            private bool disposed;

            internal WorkerThread(SimpleLooper manager, Action threadStart, object initializationInformation)
            {
                this.manager = manager;
                Thread = new Thread(ThreadExecutionLoop);
                Thread.IsBackground = true;
                taskQueue = new Queue();
                taskInformationQueue = new Queue();
                this.threadStart = threadStart;
                this.initializationInformation = initializationInformation;
                Thread.Start();
            }

            /// 
            /// Shuts down any still living threads.
            /// 
            ~WorkerThread()
            {
                Dispose();
            }

            #region IDisposable Members

            public void Dispose()
            {
                lock (disposedLocker)
                {
                    if (!disposed)
                    {
                        disposed = true;
                        ShutDownThread();
                        resetEvent.Close();
                        GC.SuppressFinalize(this);
                    }
                }
            }

            #endregion

            internal void EnqueueTask(Action task, object taskInformation)
            {
                lock (taskQueue)
                {
                    Interlocked.Increment(ref manager.tasksRemaining);
                    taskQueue.Enqueue(task);
                    taskInformationQueue.Enqueue(taskInformation);
                    resetEvent.Set();
                }
            }


            private void ShutDownThread()
            {
                //Let the manager know that it is done with its 'task'!
                if (Interlocked.Decrement(ref manager.tasksRemaining) == 0)
                {
                    if (!manager.disposed) //Don't mess with the handle if it's already disposed.
                        manager.allThreadsIdleNotifier.Set();
                }
                //Dump out any remaining tasks in the queue.
                for (int i = 0; i < taskQueue.Count; i++) //This is still safe since shutDownThread is called from within a lock(taskQueue) block.
                {
                    taskQueue.Dequeue();
                    if (Interlocked.Decrement(ref manager.tasksRemaining) == 0)
                    {
                        if (!manager.disposed) //Don't mess with the handle if it's already disposed.
                            manager.allThreadsIdleNotifier.Set();
                    }
                }

                lock (manager.workers)
                    manager.workers.Remove(this);
            }

            /// Thrown when the thread encounters an invalid state; generally propagated float.NaN's.
            private void ThreadExecutionLoop()
            {
                //Perform any initialization requested.
                if (threadStart != null)
                    threadStart(initializationInformation);
                object information = null;

                while (true)
                {
                    Action task = null;
                    lock (taskQueue)
                    {
                        if (taskQueue.Count > 0)
                        {
                            task = taskQueue.Dequeue();
                            if (task == null)
                            {
                                Dispose();
                                return;
                            }

                            information = taskInformationQueue.Dequeue();
                        }
                    }
                    if (task != null)
                    {
                        //Perform the task!
                        try
                        {
                            task(information);
                        }
                        catch (ArithmeticException arithmeticException)
                        {
                            throw new ArithmeticException(
                                "Some internal mulsathreaded arithmetic has encountered an invalid state.  Check for invalid ensaty momentums, velocities, and positions; propagating NaN's will generally trigger this exception in the getExtremePoint function.",
                                arithmeticException);
                        }
                        if (Interlocked.Decrement(ref manager.tasksRemaining) == 0)
                        {
                            manager.allThreadsIdleNotifier.Set();
                            resetEvent.WaitOne();
                        }
                    }
                    else
                        resetEvent.WaitOne();
                }
            }
        }
    }
}