/// <summary> /// Add a value to the hash table, hashed based on a string key embedded in it. Return the added value (may be a different object than "value"). /// </summary> public TValue Add(TValue value) { TValue newValue; // Loop until value is in hash table while (true) { // Add new value // XHashtableState.TryAdd returns false if hash table is not big enough if (_state.TryAdd(value, out newValue)) { return(newValue); } // PUBLISH (state) // Hash table was not big enough, so resize it. // We only want one thread to perform a resize, as it is an expensive operation // First thread will perform resize; waiting threads will call Resize(), but should immediately // return since there will almost always be space in the hash table resized by the first thread. lock (this) { XHashtableState newState = _state.Resize(); // Use memory barrier to ensure that the resized XHashtableState object is fully constructed before it is assigned Thread.MemoryBarrier(); _state = newState; } } }
public TValue Add(TValue value) { TValue local; Label_0000: if (this.state.TryAdd(value, out local)) { return(local); } lock (((XHashtable <TValue>) this)) { XHashtableState <TValue> state = this.state.Resize(); Thread.MemoryBarrier(); this.state = state; goto Label_0000; } }
/// <summary> /// Add a value to the hash table, hashed based on a string key embedded in it. Return the added value (may be a different object than "value"). /// </summary> public TValue Add(TValue value) { TValue newValue; // Loop until value is in hash table while (true) { // Add new value // XHashtableState.TryAdd returns false if hash table is not big enough if (state.TryAdd(value, out newValue)) { return(newValue); } // PUBLISH (state) // Hash table was not big enough, so resize it. // We only want one thread to perform a resize, as it is an expensive operation // First thread will perform resize; waiting threads will call Resize(), but should immediately // return since there will almost always be space in the hash table resized by the first thread. lock (this) { XHashtableState newState = state.Resize(); // Use memory barrier to ensure that the resized XHashtableState object is fully constructed before it is assigned #if !SILVERLIGHT Thread.MemoryBarrier(); #else // SILVERLIGHT // According to this document "http://my/sites/juddhall/ThreadingFeatureCrew/Shared Documents/System.Threading - FX Audit Proposal.docx" // The MemoryBarrier method usage is busted (mostly - don't know about ours) and should be removed. // Replacing with Interlocked.CompareExchange for now (with no effect) // which will do a very similar thing to MemoryBarrier (it's just slower) System.Threading.Interlocked.CompareExchange <XHashtableState>(ref state, null, null); #endif // SILVERLIGHT state = newState; } } }
/// <summary> /// Construct a new XHashtable with the specified starting capacity. /// </summary> public XHashtable(ExtractKeyDelegate extractKey, int capacity) { _state = new XHashtableState(extractKey, capacity); }
/// <summary> /// If this table is not full, then just return "this". Otherwise, create and return a new table with /// additional capacity, and rehash all values in the table. /// </summary> public XHashtableState Resize() { // No need to resize if there are open entries if (_numEntries < _buckets.Length) { return(this); } int newSize = 0; // Determine capacity of resized hash table by first counting number of valid, non-orphaned entries // As this count proceeds, close all linked lists so that no additional entries can be added to them for (int bucketIdx = 0; bucketIdx < _buckets.Length; bucketIdx++) { int entryIdx = _buckets[bucketIdx]; if (entryIdx == EndOfList) { // Replace EndOfList with FullList, so that any threads still attempting to add will be forced to resize entryIdx = Interlocked.CompareExchange(ref _buckets[bucketIdx], FullList, EndOfList); } // Loop until we've guaranteed that the list has been counted and closed to further adds while (entryIdx > EndOfList) { // Count each valid entry if (_extractKey(_entries[entryIdx].Value) != null) { newSize++; } if (_entries[entryIdx].Next == EndOfList) { // Replace EndOfList with FullList, so that any threads still attempting to add will be forced to resize entryIdx = Interlocked.CompareExchange(ref _entries[entryIdx].Next, FullList, EndOfList); } else { // Move to next entry in the list entryIdx = _entries[entryIdx].Next; } } Debug.Assert(entryIdx == EndOfList, "Resize() should only be called by one thread"); } // Double number of valid entries; if result is less than current capacity, then use current capacity if (newSize < _buckets.Length / 2) { newSize = _buckets.Length; } else { newSize = _buckets.Length * 2; if (newSize < 0) { throw new OverflowException(); } } // Create new hash table with additional capacity XHashtableState newHashtable = new XHashtableState(_extractKey, newSize); // Rehash names (TryAdd will always succeed, since we won't fill the new table) // Do not simply walk over entries and add them to table, as that would add orphaned // entries. Instead, walk the linked lists and add each name. for (int bucketIdx = 0; bucketIdx < _buckets.Length; bucketIdx++) { int entryIdx = _buckets[bucketIdx]; TValue newValue; while (entryIdx > EndOfList) { newHashtable.TryAdd(_entries[entryIdx].Value, out newValue); entryIdx = _entries[entryIdx].Next; } Debug.Assert(entryIdx == FullList, "Linked list should have been closed when it was counted"); } return(newHashtable); }
public XHashtable(ExtractKeyDelegate <TValue> extractKey, int capacity) { this.state = new XHashtableState <TValue>(extractKey, capacity); }