343 lines · 12.0 KB · C#
RawDownload
//******************************************************************************************************//  LogicalThreadScheduler.cs - Gbtc////  Copyright © 2015, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  09/23/2015 - Stephen C. Wills//       Generated original version of source code.////****************************************************************************************************** using System;using System.Collections.Concurrent;using System.Diagnostics;using System.Linq;using System.Text;using System.Threading; namespace GSF.Threading{    /// <summary>    /// Manages synchronization of actions by dispatching actions    /// to logical threads to be processed synchronously.    /// </summary>    public class LogicalThreadScheduler    {        #region [ Members ]         // Events         /// <summary>        /// Triggered when an action managed by this        /// synchronization manager throws an exception.        /// </summary>        public event EventHandler<EventArgs<Exception>> UnhandledException;         // Fields        private ConcurrentQueue<LogicalThread> m_logicalThreads;        private int m_maxThreadCount;        private int m_threadCount;        private bool m_useBackgroundThreads;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new instance of the <see cref="LogicalThreadScheduler"/> class.        /// </summary>        public LogicalThreadScheduler()        {            m_maxThreadCount = Environment.ProcessorCount;            m_logicalThreads = new ConcurrentQueue<LogicalThread>();            m_useBackgroundThreads = true;        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets the target for the maximum number of physical        /// threads managed by this synchronization manager at any given time.        /// </summary>        public int MaxThreadCount        {            get            {                return Interlocked.CompareExchange(ref m_maxThreadCount, 0, 0);            }            set            {                int diff = value - m_maxThreadCount;                int inactiveThreads = Math.Min(diff, m_logicalThreads.Count);                 Interlocked.Exchange(ref m_maxThreadCount, value);                 for (int i = 0; i < inactiveThreads; i++)                    ActivatePhysicalThread();            }        }         /// <summary>        /// Gets or sets the flag that determines whether the threads in        /// the scheduler's thread pool should be background threads.        /// </summary>        public bool UseBackgroundThreads        {            get            {                return m_useBackgroundThreads;            }            set            {                m_useBackgroundThreads = value;            }        }         /// <summary>        /// Gets the current number of active physical threads.        /// </summary>        private int ThreadCount        {            get            {                return Interlocked.CompareExchange(ref m_threadCount, 0, 0);            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Creates a new logical thread whose        /// execution is managed by this scheduler.        /// </summary>        /// <returns>A new logical thread managed by this scheduler.</returns>        public LogicalThread CreateThread()        {            return CreateThread(1);        }         /// <summary>        /// Creates a new logical thread whose        /// execution is managed by this scheduler.        /// </summary>        /// <param name="priorityLevels">The number of levels of priority supported by the logical thread.</param>        /// <returns>A new logical thread managed by this scheduler.</returns>        public LogicalThread CreateThread(int priorityLevels)        {            return new LogicalThread(this, priorityLevels);        }         /// <summary>        /// Signals the manager when a logical        /// thread has new actions to be processed.        /// </summary>        /// <param name="thread">The thread with new actions to be processed.</param>        internal void SignalItemHandler(LogicalThread thread)        {            if (thread.TryActivate())            {                m_logicalThreads.Enqueue(thread);                ActivatePhysicalThread();            }        }         /// <summary>        /// Activates a new physical thread if the thread        /// count has not yet reached its maximum limit.        /// </summary>        private void ActivatePhysicalThread()        {            int threadCount;            int newThreadCount;             threadCount = ThreadCount;             while (threadCount < Interlocked.CompareExchange(ref m_maxThreadCount, 0, 0))            {                newThreadCount = Interlocked.CompareExchange(ref m_threadCount, threadCount + 1, threadCount);                 if (newThreadCount == threadCount)                {                    StartNewPhysicalThread();                    break;                }                 threadCount = newThreadCount;            }        }         /// <summary>        /// Starts a new physical thread to        /// process actions from logical threads.        /// </summary>        private void StartNewPhysicalThread()        {            Thread thread = new Thread(ProcessLogicalThreads);            thread.IsBackground = m_useBackgroundThreads;            thread.Start();        }         /// <summary>        /// Processes the next available task from the least recently        /// processed logical thread that is available for processing.        /// </summary>        private void ProcessLogicalThreads()        {            Stopwatch stopwatch;            LogicalThread thread;            Action action;             stopwatch = new Stopwatch();             while (ThreadCount <= MaxThreadCount && m_logicalThreads.TryDequeue(out thread))            {                action = thread.Pull();                 if ((object)action != null)                {                    LogicalThread.CurrentThread = thread;                     stopwatch.Restart();                    TryExecute(action);                    stopwatch.Stop();                     LogicalThread.CurrentThread = null;                    thread.UpdateStatistics(stopwatch.Elapsed);                }                 if (thread.HasAction)                    m_logicalThreads.Enqueue(thread);                else                    thread.Deactivate();            }             DeactivatePhysicalThread();             if (!m_logicalThreads.IsEmpty)                ActivatePhysicalThread();        }         /// <summary>        /// Attempts to execute the given action.        /// </summary>        /// <param name="action">The action to be executed.</param>        private void TryExecute(Action action)        {            try            {                action();            }            catch (Exception ex)            {                if (!TryHandleException(ex))                    throw;            }        }         /// <summary>        /// Decrements the thread count upon deactivation of a physical thread.        /// </summary>        private void DeactivatePhysicalThread()        {            Interlocked.Decrement(ref m_threadCount);        }         /// <summary>        /// Attempts to handle the exception via either the logical thread's        /// exception handler or the scheduler's exception handler.        /// </summary>        /// <param name="unhandledException">The unhandled exception thrown by an action on the logical thread.</param>        /// <returns>True if the exception could be handled; false otherwise.</returns>        private bool TryHandleException(Exception unhandledException)        {            AggregateException aggregateException;            StringBuilder message;            bool handled;             message = new StringBuilder();            message.AppendFormat("Logical thread action threw an exception of type {0}: {1}", unhandledException.GetType().FullName, unhandledException.Message);            aggregateException = new AggregateException(message.ToString(), unhandledException);             try            {                // Attempt to handle the exception via the logical thread's exception handler                handled = LogicalThread.CurrentThread.OnUnhandledException(unhandledException);            }            catch (Exception handlerException)            {                // If the handler throws an exception,                // make a note of it in the exception's exception message                message.AppendLine();                message.AppendFormat("Logical thread exception handler threw an exception of type {0}: {1}", handlerException.GetType().FullName, handlerException.Message);                aggregateException = new AggregateException(message.ToString(), aggregateException.InnerExceptions.Concat(new Exception[] { handlerException }));                handled = false;            }             try            {                // If the logical thread's exception handler was not able to handle the exception,                // attempt to handle the exception via the thread scheduler's exception handler                Exception ex = (aggregateException.InnerExceptions.Count > 1) ? aggregateException : unhandledException;                handled = handled || OnUnhandledException(ex);            }            catch (Exception handlerException)            {                // If the handler throws an exception,                // make a note of it in the exception's exception message                message.AppendLine();                message.AppendFormat("Scheduler exception handler threw an exception of type {0}: {1}", handlerException.GetType().FullName, handlerException.Message);                aggregateException = new AggregateException(message.ToString(), aggregateException.InnerExceptions.Concat(new Exception[] { handlerException }));                handled = false;            }             if (!handled)            {                // If the exception could not be handled by either the                // logical thread's exception handler or the scheduler's                // exception handler, throw it as an unhandled exception                if (aggregateException.InnerExceptions.Count > 1)                    throw aggregateException;                 return false;            }             return true;        }         /// <summary>        /// Raises the <see cref="UnhandledException"/> event.        /// </summary>        /// <param name="ex">The unhandled exception.</param>        /// <returns>True if there are any handlers attached to this event; false otherwise.</returns>        private bool OnUnhandledException(Exception ex)        {            EventHandler<EventArgs<Exception>> unhandledException = UnhandledException;             if ((object)unhandledException == null)                return false;             unhandledException(this, new EventArgs<Exception>(ex));             return true;        }         #endregion    }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t