diff options
Diffstat (limited to 'src/mscorlib/src/System/_LocalDataStore.cs')
-rw-r--r-- | src/mscorlib/src/System/_LocalDataStore.cs | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/_LocalDataStore.cs b/src/mscorlib/src/System/_LocalDataStore.cs new file mode 100644 index 0000000000..a3a312f104 --- /dev/null +++ b/src/mscorlib/src/System/_LocalDataStore.cs @@ -0,0 +1,244 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Class that stores local data. This class is used in cooperation +** with the _LocalDataStoreMgr class. +** +** +=============================================================================*/ + +namespace System { + + using System; + using System.Threading; + using System.Runtime.CompilerServices; + using System.Diagnostics.Contracts; + + // Helper class to aid removal of LocalDataStore from the LocalDataStoreMgr + // LocalDataStoreMgr does not holds references to LocalDataStoreHolder. It holds + // references to LocalDataStore only. LocalDataStoreHolder finalizer will run once + // the only outstanding reference to the store is in LocalDataStoreMgr. + sealed internal class LocalDataStoreHolder + { + private LocalDataStore m_Store; + + public LocalDataStoreHolder(LocalDataStore store) + { + m_Store = store; + } + + ~LocalDataStoreHolder() + { + LocalDataStore store = m_Store; + if (store == null) + return; + + store.Dispose(); + } + + public LocalDataStore Store + { + get + { + return m_Store; + } + } + } + + sealed internal class LocalDataStoreElement + { + private Object m_value; + private long m_cookie; // This is immutable cookie of the slot used to verify that + // the value is indeed indeed owned by the slot. Necessary + // to avoid resurection holes. + + public LocalDataStoreElement(long cookie) + { + m_cookie = cookie; + } + + public Object Value + { + get + { + return m_value; + } + set + { + m_value = value; + } + } + + public long Cookie + { + get + { + return m_cookie; + } + } + } + + // This class will not be marked serializable + sealed internal class LocalDataStore + { + private LocalDataStoreElement[] m_DataTable; + private LocalDataStoreMgr m_Manager; + + /*========================================================================= + ** Initialize the data store. + =========================================================================*/ + public LocalDataStore(LocalDataStoreMgr mgr, int InitialCapacity) + { + // Store the manager of the local data store. + m_Manager = mgr; + + // Allocate the array that will contain the data. + m_DataTable = new LocalDataStoreElement[InitialCapacity]; + } + + /*========================================================================= + ** Delete this store from its manager + =========================================================================*/ + internal void Dispose() + { + m_Manager.DeleteLocalDataStore(this); + } + + /*========================================================================= + ** Retrieves the value from the specified slot. + =========================================================================*/ + public Object GetData(LocalDataStoreSlot slot) + { + // Validate the slot. + m_Manager.ValidateSlot(slot); + + // Cache the slot index to avoid synchronization issues. + int slotIdx = slot.Slot; + + if (slotIdx >= 0) + { + // Delay expansion of m_DataTable if we can + if (slotIdx >= m_DataTable.Length) + return null; + + // Retrieve the data from the given slot. + LocalDataStoreElement element = m_DataTable[slotIdx]; + + //Initially we prepopulate the elements to be null. + if (element == null) + return null; + + // Check that the element is owned by this slot by comparing cookies. + // This is necesary to avoid resurection race conditions. + if (element.Cookie == slot.Cookie) + return element.Value; + + // Fall thru and throw exception + } + + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_SlotHasBeenFreed")); + } + + /*========================================================================= + ** Sets the data in the specified slot. + =========================================================================*/ + public void SetData(LocalDataStoreSlot slot, Object data) + { + // Validate the slot. + m_Manager.ValidateSlot(slot); + + // Cache the slot index to avoid synchronization issues. + int slotIdx = slot.Slot; + + if (slotIdx >= 0) + { + LocalDataStoreElement element = (slotIdx < m_DataTable.Length) ? m_DataTable[slotIdx] : null; + if (element == null) + { + element = PopulateElement(slot); + } + + // Check that the element is owned by this slot by comparing cookies. + // This is necesary to avoid resurection race conditions. + if (element.Cookie == slot.Cookie) + { + // Set the data on the given slot. + element.Value = data; + return; + } + + // Fall thru and throw exception + } + + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_SlotHasBeenFreed")); + } + + /*========================================================================= + ** This method does clears the unused slot. + * Assumes lock on m_Manager is taken + =========================================================================*/ + internal void FreeData(int slot, long cookie) + { + // We try to delay allocate the dataTable (in cases like the manager clearing a + // just-freed slot in all stores + if (slot >= m_DataTable.Length) + return; + + LocalDataStoreElement element = m_DataTable[slot]; + if (element != null && element.Cookie == cookie) + m_DataTable[slot] = null; + } + + /*========================================================================= + ** Method used to expand the capacity of the local data store. + =========================================================================*/ + [System.Security.SecuritySafeCritical] // auto-generated + private LocalDataStoreElement PopulateElement(LocalDataStoreSlot slot) + { + bool tookLock = false; + RuntimeHelpers.PrepareConstrainedRegions(); + try { + Monitor.Enter(m_Manager, ref tookLock); + + // Make sure that the slot was not freed in the meantime + int slotIdx = slot.Slot; + if (slotIdx < 0) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_SlotHasBeenFreed")); + + if (slotIdx >= m_DataTable.Length) + { + int capacity = m_Manager.GetSlotTableLength(); + + // Validate that the specified capacity is larger than the current one. + Contract.Assert(capacity >= m_DataTable.Length, "LocalDataStore corrupted: capacity >= m_DataTable.Length"); + + // Allocate the new data table. + LocalDataStoreElement[] NewDataTable = new LocalDataStoreElement[capacity]; + + // Copy all the objects into the new table. + Array.Copy(m_DataTable, NewDataTable, m_DataTable.Length); + + // Save the new table. + m_DataTable = NewDataTable; + } + + // Validate that there is enough space in the local data store now + Contract.Assert(slotIdx < m_DataTable.Length, "LocalDataStore corrupted: slotIdx < m_DataTable.Length"); + + if (m_DataTable[slotIdx] == null) + m_DataTable[slotIdx] = new LocalDataStoreElement(slot.Cookie); + + return m_DataTable[slotIdx]; + } + finally { + if (tookLock) + Monitor.Exit(m_Manager); + } + } + } +} |