//******************************************************************************************************
// LogicalThreadLocal.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/24/2015 - Stephen C. Wills
// Generated original version of source code.
//
//******************************************************************************************************
using System;
using System.Threading;
namespace GSF.Threading
{
///
/// Represents a slot in the thread local memory space of each logical thread.
///
public sealed class LogicalThreadLocal : IDisposable
{
#region [ Members ]
// Nested Types
private class Slot
{
private T m_value;
public T Value
{
get
{
return m_value;
}
set
{
m_value = value;
}
}
}
// Events
private event EventHandler Disposed;
// Fields
private Guid m_id;
private Func m_newObjectFactory;
private ThreadLocal m_threadLocal;
private int m_accessCount;
private int m_disposed;
#endregion
#region [ Constructors ]
///
/// Creates a new instance of the class.
///
public LogicalThreadLocal()
: this(() => default(T))
{
}
///
/// Creates a new instance of the class.
///
/// Factory to produce the initial value when accessing uninitialized values.
public LogicalThreadLocal(Func newObjectFactory)
{
m_id = Guid.NewGuid();
m_newObjectFactory = newObjectFactory;
m_threadLocal = new ThreadLocal(newObjectFactory);
}
///
/// Releases the unmanaged resources before the object is reclaimed by .
///
~LogicalThreadLocal()
{
Dispose(false);
}
#endregion
#region [ Properties ]
///
/// Gets or sets the thread local object
/// associated with the current logical thread.
///
public T Value
{
get
{
Slot slot;
return Access(() =>
{
if (TryGetSlot(out slot))
return slot.Value;
if (!IsDisposed)
return m_threadLocal.Value;
return default(T);
});
}
set
{
Slot slot;
Access(() =>
{
if (TryGetSlot(out slot))
slot.Value = value;
else if (!IsDisposed)
m_threadLocal.Value = value;
});
}
}
///
/// Gets a flag that indicates whether this object has been disposed.
///
private bool IsDisposed
{
get
{
return Interlocked.CompareExchange(ref m_disposed, 0, 0) != 0;
}
}
#endregion
#region [ Methods ]
///
/// Releases all the resources used by the object.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Executes the given action in a protected code block that ensures all
/// unmanaged resources get cleaned up after a call to .
///
/// The action to be executed.
private void Access(Action action)
{
try
{
Interlocked.Increment(ref m_accessCount);
if (IsDisposed)
return;
action();
}
finally
{
Interlocked.Decrement(ref m_accessCount);
if (IsDisposed && Interlocked.CompareExchange(ref m_accessCount, 0, 0) == 0)
OnDisposed(true);
}
}
///
/// Executes the given function in a protected code block that ensures all
/// unmanaged resources get cleaned up after a call to .
///
/// The function to be executed.
private TResult Access(Func func)
{
try
{
Interlocked.Increment(ref m_accessCount);
return !IsDisposed ? func() : default(TResult);
}
finally
{
Interlocked.Decrement(ref m_accessCount);
if (IsDisposed && Interlocked.CompareExchange(ref m_accessCount, 0, 0) == 0)
OnDisposed(true);
}
}
///
/// Attempts to get the instance of this slot from the current logical thread.
///
/// An instance of this slot.
/// True if an instance of this slot is successfully retrieved from the current logical thread.
private bool TryGetSlot(out Slot slot)
{
LogicalThread currentThread;
if (IsDisposed)
{
slot = null;
return false;
}
currentThread = LogicalThread.CurrentThread;
if ((object)currentThread == null)
{
slot = null;
return false;
}
slot = currentThread.GetThreadLocal(m_id) as Slot;
if ((object)slot == null)
{
slot = new Slot();
slot.Value = m_newObjectFactory();
currentThread.SetThreadLocal(m_id, slot);
AttachToDisposed();
}
return true;
}
///
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
private void Dispose(bool disposing)
{
Interlocked.CompareExchange(ref m_disposed, -1, 0);
if (Interlocked.CompareExchange(ref m_accessCount, 0, 0) == 0)
OnDisposed(disposing);
}
///
/// Attaches logic to dispose of the slot in
/// the currently executing logical thread.
///
private void AttachToDisposed()
{
WeakReference threadRef = new WeakReference(LogicalThread.CurrentThread);
Disposed += (sender, args) =>
{
LogicalThread thread;
if (threadRef.TryGetTarget(out thread))
thread.SetThreadLocal(m_id, null);
};
}
///
/// Invokes the event handler.
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
private void OnDisposed(bool disposing)
{
if (Interlocked.CompareExchange(ref m_disposed, 1, -1) == -1)
{
EventHandler disposed = Disposed;
if ((object)disposed != null)
disposed(this, EventArgs.Empty);
if (disposing)
m_threadLocal.Dispose();
}
}
#endregion
}
}