286 lines
12 KiB
C#
286 lines
12 KiB
C#
// ==++==
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// ==--==
|
|
//=============================================================================
|
|
//
|
|
// Class: LocalDataStoreMgr
|
|
//
|
|
// Purpose: Class that manages stores of local data. This class is used in
|
|
// cooperation with the LocalDataStore class.
|
|
//
|
|
//=============================================================================
|
|
namespace System
|
|
{
|
|
|
|
using System;
|
|
using System.Collections;
|
|
|
|
// This is a cheesy internal helper class that is used to make sure memory
|
|
// is actually being accessed and not some cached copy of a field in a
|
|
// register.
|
|
// WARNING: If we every do type analysis to eliminate virtual functions,
|
|
// this will break.
|
|
internal class LdsSyncHelper
|
|
{
|
|
internal virtual int Get(ref int slot)
|
|
{
|
|
return slot;
|
|
}
|
|
}
|
|
|
|
// This class is an encapsulation of a slot so that it is managed in a secure fashion.
|
|
// It is constructed by the LocalDataStoreManager, holds the slot and the manager
|
|
// and cleans up when it is finalized.
|
|
//| <include path='docs/doc[@for="LocalDataStoreSlot"]/*' />
|
|
public sealed class LocalDataStoreSlot
|
|
{
|
|
private static LdsSyncHelper m_helper = new LdsSyncHelper();
|
|
|
|
private LocalDataStoreMgr m_mgr;
|
|
private int m_slot;
|
|
|
|
// Construct the object to encapsulate the slot.
|
|
internal LocalDataStoreSlot(LocalDataStoreMgr mgr, int slot)
|
|
{
|
|
m_mgr = mgr;
|
|
m_slot = slot;
|
|
}
|
|
|
|
// Accessors for the two fields of this class.
|
|
internal LocalDataStoreMgr Manager
|
|
{
|
|
get
|
|
{
|
|
return m_mgr;
|
|
}
|
|
}
|
|
internal int Slot
|
|
{
|
|
get
|
|
{
|
|
return m_slot;
|
|
}
|
|
}
|
|
|
|
// This is used to make sure we are actually reading and writing to
|
|
// memory to fetch the slot (rather than possibly using a value
|
|
// cached in a register).
|
|
internal bool IsValid()
|
|
{
|
|
return m_helper.Get(ref m_slot) != -1;
|
|
}
|
|
|
|
// Release the slot reserved by this object when this object goes away.
|
|
// There is a race condition that can happen in the face of
|
|
// resurrection where another thread is fetching values or assigning
|
|
// while the finalizer thread is here. We are counting on the fact
|
|
// that code that fetches values calls IsValid after fetching a value
|
|
// and before giving it to anyone. See LocalDataStore for the other
|
|
// half of this. We are also counting on code that sets values locks
|
|
// the manager.
|
|
//| <include path='docs/doc[@for="LocalDataStoreSlot.Finalize"]/*' />
|
|
~LocalDataStoreSlot()
|
|
{
|
|
int slot = m_slot;
|
|
|
|
// This lock fixes synchronization with the assignment of values.
|
|
lock (m_mgr) {
|
|
// Mark the slot as free.
|
|
m_slot = -1;
|
|
|
|
m_mgr.FreeDataSlot(slot);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class LocalDataStoreMgr
|
|
{
|
|
private const byte DataSlotOccupied = 0x01;
|
|
|
|
private const int InitialSlotTableSize = 64;
|
|
private const int SlotTableDoubleThreshold = 512;
|
|
private const int LargeSlotTableSizeIncrease = 128;
|
|
|
|
//=========================================================================
|
|
// Create a data store to be managed by this manager and add it to the
|
|
// list. The initial size of the new store matches the number of slots
|
|
// allocated in this manager.
|
|
//=========================================================================
|
|
public LocalDataStore CreateLocalDataStore()
|
|
{
|
|
// Create a new local data store.
|
|
LocalDataStore Store = new LocalDataStore(this, m_SlotInfoTable.Length);
|
|
|
|
lock (this) {
|
|
// Add the store to the array list and return it.
|
|
m_ManagedLocalDataStores.Add(Store);
|
|
}
|
|
return Store;
|
|
}
|
|
|
|
//=========================================================================
|
|
// Remove the specified store from the list of managed stores..
|
|
//=========================================================================
|
|
public void DeleteLocalDataStore(LocalDataStore store)
|
|
{
|
|
lock (this) {
|
|
// Remove the store to the array list and return it.
|
|
m_ManagedLocalDataStores.Remove(store);
|
|
}
|
|
}
|
|
|
|
//=========================================================================
|
|
// Allocates a data slot by finding an available index and wrapping it
|
|
// an object to prevent clients from manipulating it directly, allowing us
|
|
// to make assumptions its integrity.
|
|
//=========================================================================
|
|
public LocalDataStoreSlot AllocateDataSlot()
|
|
{
|
|
lock (this) {
|
|
int i;
|
|
LocalDataStoreSlot slot;
|
|
|
|
// Retrieve the current size of the table.
|
|
int SlotTableSize = m_SlotInfoTable.Length;
|
|
|
|
// Check if there are any slots left.
|
|
if (m_FirstAvailableSlot < SlotTableSize) {
|
|
// Save the first available slot.
|
|
slot = new LocalDataStoreSlot(this, m_FirstAvailableSlot);
|
|
m_SlotInfoTable[m_FirstAvailableSlot] = DataSlotOccupied;
|
|
|
|
// Find the next available slot.
|
|
for (i = m_FirstAvailableSlot + 1; i < SlotTableSize; ++i)
|
|
if (0 == (m_SlotInfoTable[i] & DataSlotOccupied))
|
|
break;
|
|
|
|
// Save the new "first available slot".
|
|
m_FirstAvailableSlot = i;
|
|
|
|
// Return the slot index.
|
|
return slot;
|
|
}
|
|
|
|
// The table is full so we need to increase its size.
|
|
int NewSlotTableSize;
|
|
if (SlotTableSize < SlotTableDoubleThreshold) {
|
|
// The table is still relatively small so double it.
|
|
NewSlotTableSize = SlotTableSize * 2;
|
|
}
|
|
else {
|
|
// The table is relatively large so simply increase its size by a given amount.
|
|
NewSlotTableSize = SlotTableSize + LargeSlotTableSizeIncrease;
|
|
}
|
|
|
|
// Allocate the new slot info table.
|
|
byte[] NewSlotInfoTable = new byte[NewSlotTableSize];
|
|
|
|
// Copy the old array into the new one.
|
|
Array.Copy(m_SlotInfoTable, NewSlotInfoTable, SlotTableSize);
|
|
m_SlotInfoTable = NewSlotInfoTable;
|
|
|
|
// SlotTableSize is the index of the first empty slot in the expanded table.
|
|
slot = new LocalDataStoreSlot(this, SlotTableSize);
|
|
m_SlotInfoTable[SlotTableSize] = DataSlotOccupied;
|
|
m_FirstAvailableSlot = SlotTableSize + 1;
|
|
|
|
// Return the selected slot
|
|
return slot;
|
|
}
|
|
}
|
|
|
|
//=========================================================================
|
|
// Allocate a slot and associate a name with it.
|
|
//=========================================================================
|
|
public LocalDataStoreSlot AllocateNamedDataSlot(String name)
|
|
{
|
|
lock (this) {
|
|
// Allocate a normal data slot.
|
|
LocalDataStoreSlot slot = AllocateDataSlot();
|
|
|
|
// Insert the association between the name and the data slot number
|
|
// in the hash table.
|
|
m_KeyToSlotMap.Add(name, slot);
|
|
return slot;
|
|
}
|
|
}
|
|
|
|
//=========================================================================
|
|
// Retrieve the slot associated with a name, allocating it if no such
|
|
// association has been defined.
|
|
//=========================================================================
|
|
public LocalDataStoreSlot GetNamedDataSlot(String name)
|
|
{
|
|
lock (this) {
|
|
// Lookup in the hashtable to try find a slot for the name.
|
|
LocalDataStoreSlot slot = (LocalDataStoreSlot) m_KeyToSlotMap[name];
|
|
|
|
// If the name is not yet in the hashtable then add it.
|
|
if (null == slot)
|
|
return AllocateNamedDataSlot(name);
|
|
|
|
// The name was in the hashtable so return the associated slot.
|
|
return slot;
|
|
}
|
|
}
|
|
|
|
//=========================================================================
|
|
// Eliminate the association of a name with a slot. The actual slot will
|
|
// be reclaimed when the finalizer for the slot object runs.
|
|
//=========================================================================
|
|
public void FreeNamedDataSlot(String name)
|
|
{
|
|
lock (this) {
|
|
// Remove the name slot association from the hashtable.
|
|
m_KeyToSlotMap.Remove(name);
|
|
}
|
|
}
|
|
|
|
//=========================================================================
|
|
// Free's a previously allocated data slot on ALL the managed data stores.
|
|
//=========================================================================
|
|
internal void FreeDataSlot(int slot)
|
|
{
|
|
lock (this) {
|
|
// Go thru all the managed stores and set the data on the specified slot to 0.
|
|
for (int i = 0; i < m_ManagedLocalDataStores.Count; i++) {
|
|
((LocalDataStore)m_ManagedLocalDataStores[i]).SetDataInternal(
|
|
slot,
|
|
null,
|
|
false);
|
|
}
|
|
|
|
// Mark the slot as being no longer occupied.
|
|
m_SlotInfoTable[slot] = 0;
|
|
if (slot < m_FirstAvailableSlot)
|
|
m_FirstAvailableSlot = slot;
|
|
}
|
|
}
|
|
|
|
//=========================================================================
|
|
// Return the number of allocated slots in this manager.
|
|
//=========================================================================
|
|
public void ValidateSlot(LocalDataStoreSlot slot)
|
|
{
|
|
// Make sure the slot was allocated for this store.
|
|
if (slot == null || slot.Manager != this)
|
|
throw new ArgumentException("Argument_ALSInvalidSlot");
|
|
}
|
|
|
|
//=========================================================================
|
|
// Return the number of allocated slots in this manager.
|
|
//=========================================================================
|
|
internal int GetSlotTableLength()
|
|
{
|
|
return m_SlotInfoTable.Length;
|
|
}
|
|
|
|
private byte[] m_SlotInfoTable = new byte[InitialSlotTableSize];
|
|
private int m_FirstAvailableSlot = 0;
|
|
private ArrayList m_ManagedLocalDataStores = new ArrayList();
|
|
private Hashtable m_KeyToSlotMap = new Hashtable();
|
|
}
|
|
}
|