//******************************************************************************************************
// ManagedThread.cs - Gbtc
//
// Copyright © 2012, 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://www.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:
// ----------------------------------------------------------------------------------------------------
// 01/25/2008 - J. Ritchie Carroll
// Initial version of source generated.
// 09/11/2008 - J. Ritchie Carroll
// Converted to C#.
// 08/10/2009 - Josh L. Patterson
// Edited Comments.
// 09/14/2009 - Stephen C. Wills
// Added new header and license agreement.
// 09/22/2011 - J. Ritchie Carroll
// Added Mono implementation exception regions.
// 12/14/2012 - Starlynn Danyelle Gilliam
// Modified Header.
//
//******************************************************************************************************
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using GSF.Units;
namespace GSF.Threading
{
#region [ Enumerations ]
///
/// Managed Thread Types
///
public enum ThreadType
{
/// Standard thread created with public constructor
StandardThread,
/// Queued thread added into managed thread pool
QueuedThread
}
///
/// Managed Thread States
///
public enum ThreadStatus
{
/// Thread created, not started
Unstarted,
/// Thread queued for execution
Queued,
/// Thread start requested, execution pending
Started,
/// Thread executing
Executing,
/// Thread completed
Completed,
/// Thread aborted
Aborted
}
#endregion
///
/// Defines a managed thread
///
///
/// This class works like any normal thread but provides the benefit of automatic tracking
/// through the ManagedThreads collection, total thread runtime and the ability to run
/// the thread in an alternate execution context
///
public sealed class ManagedThread
{
#region [ Members ]
// Fields
private Thread m_thread;
private readonly ThreadType m_type;
private ThreadStatus m_status;
private string m_name;
private Ticks m_startTime;
private Ticks m_stopTime;
private readonly ContextCallback m_ctxCallback;
private readonly ThreadStart m_tsCallback;
private readonly ParameterizedThreadStart m_ptsCallback;
private readonly ExecutionContext m_ctx;
private object m_state;
private object m_tag;
#endregion
#region [ Constructors ]
///
/// Initializes a new instance of the ManagedThread class.
///
/// A object.
public ManagedThread(ThreadStart callback)
: this(ThreadType.StandardThread, callback, null, null)
{
m_thread = new Thread(HandleItem);
}
///
/// Initializes a new instance of the ManagedThread class, specifying a delegate that allows an object to be passed to the thread when the thread is started.
///
/// A object.
public ManagedThread(ParameterizedThreadStart callback)
: this(ThreadType.StandardThread, callback, null, null)
{
m_thread = new Thread(HandleItem);
}
#if !MONO
///
/// Initializes a new instance of the ManagedThread class, specifying a delegate that allows an object to be passed to the thread when the thread is started
/// and allowing the user to specify an alternate execution context for the thread.
///
/// A delgate to execute.
/// An .
public ManagedThread(ContextCallback callback, ExecutionContext ctx)
: this(ThreadType.StandardThread, callback, null, ctx)
{
m_thread = new Thread(HandleItem);
}
#endif
internal ManagedThread(ThreadType type, ThreadStart callback, object state, ExecutionContext ctx)
{
m_type = type;
m_status = (type == ThreadType.QueuedThread ? ThreadStatus.Queued : ThreadStatus.Unstarted);
m_tsCallback = callback;
m_state = state;
m_ctx = ctx;
}
internal ManagedThread(ThreadType type, ParameterizedThreadStart callback, object state, ExecutionContext ctx)
{
m_type = type;
m_status = (type == ThreadType.QueuedThread ? ThreadStatus.Queued : ThreadStatus.Unstarted);
m_ptsCallback = callback;
m_state = state;
m_ctx = ctx;
}
internal ManagedThread(ThreadType type, ContextCallback callback, object state, ExecutionContext ctx)
{
m_type = type;
m_status = (type == ThreadType.QueuedThread ? ThreadStatus.Queued : ThreadStatus.Unstarted);
m_ctxCallback = callback;
m_state = state;
m_ctx = ctx;
}
#endregion
#region [ Properties ]
///
/// An object containing data to be used by the thread's execution method.
///
public object State
{
get
{
return m_state;
}
set
{
m_state = value;
}
}
///
/// An object that allows additional user defined information to be tracked along with this thread.
///
public object Tag
{
get
{
return m_tag;
}
set
{
m_tag = value;
}
}
///
/// Returns the managed thread type (either StandardThread or QueuedThread)
///
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
public ThreadType Type
{
get
{
return m_type;
}
}
///
/// Gets a value containing the curretn status of the current thread.
///
public ThreadStatus Status
{
get
{
return m_status;
}
internal set
{
m_status = value;
}
}
///
/// Gets a value indicating the execution status of the current thread.
///
public bool IsAlive
{
get
{
return (m_status == ThreadStatus.Started || m_status == ThreadStatus.Executing);
}
}
///
/// Gets or sets the name of the thread.
///
public string Name
{
get
{
return m_name;
}
set
{
m_name = value;
if (m_type == ThreadType.StandardThread)
m_thread.Name = value;
}
}
///
/// Get the time, in ticks, that the thread started executing
///
public Ticks StartTime
{
get
{
return m_startTime;
}
}
///
/// Get the time, in ticks, that the thread finished executing
///
public Ticks StopTime
{
get
{
return m_stopTime;
}
}
///
/// Gets the total amount of time, in seconds, that the managed thread has been active.
///
public Time RunTime
{
get
{
Ticks processingTime = 0;
if (m_startTime > 0)
{
if (m_stopTime > 0)
processingTime = m_stopTime - m_startTime;
else
processingTime = DateTime.UtcNow.Ticks - m_startTime;
}
if (processingTime < 0)
processingTime = 0;
return processingTime.ToSeconds();
}
}
///
/// Gets or sets a value indicating the scheduling priority of a thread.
///
/// One of the ThreadPriority values. The default value is Normal.
///
/// Changing of this value is only available to standard threads - you can't change the priorty of queued threads since they are already
/// allocated and owned by the .NET thread pool.
///
public ThreadPriority Priority
{
get
{
if (m_type == ThreadType.QueuedThread)
{
return ThreadPriority.Normal;
}
else
{
return m_thread.Priority;
}
}
set
{
if (m_type == ThreadType.QueuedThread)
throw new InvalidOperationException("Cannot change priority of a thread that was queued into thread pool");
m_thread.Priority = value;
}
}
internal Thread Thread
{
get
{
return m_thread;
}
set
{
m_thread = value;
}
}
#endregion
#region [ Methods ]
///
/// Raises a ThreadAbortException in the thread on which it is invoked, to begin the process of terminating the thread. Calling this method usually terminates the thread.
///
public void Abort()
{
ManagedThreads.Cancel(this, true, null);
}
///
/// Raises a ThreadAbortException in the thread on which it is invoked, to begin the process of terminating the thread. Calling this method usually terminates the thread.
///
/// An object that contains application-specific information, such as state, which can be used by the thread being aborted.
public void Abort(object stateInfo)
{
ManagedThreads.Cancel(this, true, stateInfo);
}
///
/// Causes a thread to be scheduled for execution.
///
public void Start()
{
if (m_type == ThreadType.QueuedThread)
throw new InvalidOperationException("Cannot manually start a thread that was queued into thread pool");
ManagedThreads.Add(this);
m_thread.Start();
}
///
/// Causes a thread to be scheduled for execution.
///
/// An object that contains data to be used by the method the thread executes.
public void Start(object parameter)
{
if (m_type == ThreadType.QueuedThread)
throw new InvalidOperationException("Cannot manually start a thread that was queued into thread pool");
m_state = parameter;
ManagedThreads.Add(this);
m_thread.Start();
}
///
/// Blocks the calling thread until a thread terminates or the specified time elapses, while continuing to perform standard COM and SendMessage pumping.
///
///
/// This is only available for standard threads - queued threads don't have an associated thread until they are executing.
///
public void Join()
{
if (m_type == ThreadType.QueuedThread)
throw new InvalidOperationException("Cannot join a thread that was queued into thread pool");
if (!IsAlive)
throw new InvalidOperationException("Cannot join a thread that has not been started");
m_thread.Join();
}
///
/// Blocks the calling thread until a thread terminates or the specified time elapses, while continuing to perform standard COM and SendMessage pumping.
///
/// The number of milliseconds to wait for the thread to terminate.
/// true if the thread has terminated; false if the thread has not terminated after the amount of time specified by the millisecondsTimeout parameter has elapsed.
///
/// This is only available for standard threads - queued threads don't have an associated thread until they are executing.
///
public bool Join(int millisecondsTimeout)
{
if (m_type == ThreadType.QueuedThread)
throw new InvalidOperationException("Cannot join a thread that was queued into thread pool.");
if (!IsAlive)
throw new InvalidOperationException("Cannot join a thread that has not been started.");
return m_thread.Join(millisecondsTimeout);
}
///
/// Blocks the calling thread until a thread terminates or the specified time elapses, while continuing to perform standard COM and SendMessage pumping.
///
/// A TimeSpan set to the amount of time to wait for the thread to terminate.
/// true if the thread terminated; false if the thread has not terminated after the amount of time specified by the timeout parameter has elapsed.
///
/// This is only available for standard threads - queued threads don't have an associated thread until they are executing.
///
public bool Join(TimeSpan timeout)
{
return Join((int)timeout.TotalMilliseconds);
}
internal void HandleItem()
{
// Set start state
m_startTime = DateTime.UtcNow.Ticks;
m_status = ThreadStatus.Executing;
try
{
// Invoke the user's call back function
if ((object)m_ctx == null)
{
if ((object)m_tsCallback != null)
{
m_tsCallback.Invoke();
}
else if ((object)m_ptsCallback != null)
{
m_ptsCallback.Invoke(m_state);
}
else
{
m_ctxCallback.Invoke(m_state);
}
}
#if !MONO
else
{
// If user specified an alternate execution context, we invoke
// their delegate under that context
ExecutionContext.Run(m_ctx, m_ctxCallback, m_state);
}
#endif
}
finally
{
// Set finish state
if (m_status == ThreadStatus.Executing)
{
m_status = ThreadStatus.Completed;
}
m_stopTime = DateTime.UtcNow.Ticks;
ManagedThreads.Remove(this);
}
}
#endregion
}
}