/// <summary> /// Resizes a table to a certain length (or larger). /// </summary> private void GrowTable(ref LinkedSlotVolatile[] table, int minLength) { Contract.Assert(table.Length < minLength); // Determine the size of the new table and allocate it. int newLen = GetNewTableSize(minLength); LinkedSlotVolatile[] newTable = new LinkedSlotVolatile[newLen]; // // The lock is necessary to avoid a race with ThreadLocal.Dispose. GrowTable has to point all // LinkedSlot instances referenced in the old table to reference the new table. Without locking, // Dispose could use a stale SlotArray reference and clear out a slot in the old array only, while // the value continues to be referenced from the new (larger) array. // lock (s_idManager) { for (int i = 0; i < table.Length; i++) { LinkedSlot linkedSlot = table[i].Value; if (linkedSlot != null && linkedSlot.SlotArray != null) { linkedSlot.SlotArray = newTable; newTable[i] = table[i]; } } } table = newTable; }
public override void OnSubInspectorGUI() { LinkedSlot s = target as LinkedSlot; s.SourceSlot = EditorGUILayout.ObjectField("Source Slot", s.SourceSlot, typeof(PGISlot), true) as PGISlot; SlotInspector(); }
/// <summary> /// Creates a LinkedSlot and inserts it into the linked list for this ThreadLocal instance. /// </summary> private void CreateLinkedSlot(LinkedSlotVolatile[] slotArray, int id, T value) { // Create a LinkedSlot var linkedSlot = new LinkedSlot(slotArray); // Insert the LinkedSlot into the linked list maintained by this ThreadLocal<> instance and into the slot array lock (s_idManager) { // Check that the instance has not been disposed. It is important to check this under a lock, since // Dispose also executes under a lock. if (!_initialized) { throw new ObjectDisposedException(SR.ThreadLocal_Disposed); } LinkedSlot firstRealNode = _linkedSlot._next; // Insert linkedSlot between nodes m_linkedSlot and firstRealNode. // (_linkedSlot is the dummy head node that should always be in the front.) linkedSlot._next = firstRealNode; linkedSlot._previous = _linkedSlot; linkedSlot._value = value; if (firstRealNode != null) { firstRealNode._previous = linkedSlot; } _linkedSlot._next = linkedSlot; // Assigning the slot under a lock prevents a race condition with Dispose (dispose also acquires the lock). // Otherwise, it would be possible that the ThreadLocal instance is disposed, another one gets created // with the same ID, and the write would go to the wrong instance. slotArray[id].Value = linkedSlot; } }
private void SetValueSlow(T value, LinkedSlotVolatile[] slotArray) { int id = ~m_idComplement; // If the object has been disposed, id will be -1. if (id < 0) { throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed")); } // If a slot array has not been created on this thread yet, create it. if (slotArray == null) { slotArray = new LinkedSlotVolatile[GetNewTableSize(id + 1)]; ts_finalizationHelper = new FinalizationHelper(slotArray, m_trackAllValues); ts_slotArray = slotArray; } // If the slot array is not big enough to hold this ID, increase the table size. if (id >= slotArray.Length) { GrowTable(ref slotArray, id + 1); ts_finalizationHelper.SlotArray = slotArray; ts_slotArray = slotArray; } // If we are using the slot in this table for the first time, create a new LinkedSlot and add it into // the linked list for this ThreadLocal instance. if (slotArray[id].Value == null) { CreateLinkedSlot(slotArray, id, value); } else { // Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read // that follows will not be reordered before the read of slotArray[id]. LinkedSlot slot = slotArray[id].Value; // It is important to verify that the ThreadLocal instance has not been disposed. The check must come // after capturing slotArray[id], but before assigning the value into the slot. This ensures that // if this ThreadLocal instance was disposed on another thread and another ThreadLocal instance was // created, we definitely won't assign the value into the wrong instance. if (!m_initialized) { throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed")); } slot.Value = value; } }
/// <summary>Gets all of the threads' values in a list.</summary> private List <T> GetValuesAsList() { List <T> valueList = new List <T>(); int id = ~m_idComplement; if (id == -1) { return(null); } // Walk over the linked list of slots and gather the values associated with this ThreadLocal instance. for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next) { // We can safely read linkedSlot.Value. Even if this ThreadLocal has been disposed in the meantime, the LinkedSlot // objects will never be assigned to another ThreadLocal instance. valueList.Add(linkedSlot.Value); } return(valueList); }
/// <summary> /// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance. /// </summary> /// <param name="disposing"> /// A Boolean value that indicates whether this method is being called due to a call to <see cref="Dispose()"/>. /// </param> /// <remarks> /// Unlike most of the members of <see cref="T:System.Threading.ThreadLocal{T}"/>, this method is not thread-safe. /// </remarks> protected virtual void Dispose(bool disposing) { int id; lock (s_idManager) { id = ~m_idComplement; m_idComplement = 0; if (id < 0 || !m_initialized) { Contract.Assert(id >= 0 || !m_initialized, "expected id >= 0 if initialized"); // Handle double Dispose calls or disposal of an instance whose constructor threw an exception. return; } m_initialized = false; for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next) { LinkedSlotVolatile[] slotArray = linkedSlot.SlotArray; if (slotArray == null) { // The thread that owns this slotArray has already finished. continue; } // Remove the reference from the LinkedSlot to the slot table. linkedSlot.SlotArray = null; // And clear the references from the slot table to the linked slot and the value so that // both can get garbage collected. slotArray[id].Value.Value = default(T); slotArray[id].Value = null; } } m_linkedSlot = null; s_idManager.ReturnId(id); }