//****************************************************************************************************** // 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 } }