using System; using System.Diagnostics; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; public partial class LockFreeDictionary<K, V> : IDictionary<K, V> where K : IEquatable<K> where V : IEquatable<V> { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Obtain a chain head that doesn't contain the key and attempt to atomically insert the new Entry into it. /// If the atomic operation fails, start over. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool _try_add(K key, V value) { uint h = _check_key(key); Config.BucketList.BucketHead bh; EntryBlock.Entry e_new = new EntryBlock.Entry(0, h, key, value); while (true) { if (m_config.m_buckets.GetBucketHead(h, out bh)) { bool f_contains; if (bh.TryChainContainsKey(h, key, out f_contains)) { if (f_contains) return false; if (bh.TryInsertFirstItem(ref e_new)) break; } } } Config cfg_used = bh.Config; int c = Interlocked.Increment(ref cfg_used.m_count); if (c > cfg_used.m_buckets.BucketCount || bh.CapturedSlotCount > Config.BucketList.BucketHead.SlotsThreshold) { Config.BucketListPrimary blp = cfg_used.m_buckets as Config.BucketListPrimary; if (blp != null) blp.InitiateBucketResize(); } return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool _try_get_remove_update(Op op, K key, ref V in_value, out V out_value) { uint h = _check_key(key); sync_restart: Config.BucketList.BucketHead bh; if (!m_config.m_buckets.GetBucketHead(h, out bh)) goto sync_restart; Config cfg = bh.Config; int c = bh.CapturedSlotCount; int gx = bh.CapturedFirstSlotIndex; for (int i = 0; i < c; i++) { EntryBlock.Entry e; cfg.GetEntryLoose(gx, out e); if (bh.IsExpired) goto sync_restart; if (e.hash == h && e.key.Equals(key)) { if (op == Op.Update) { if ((cfg.dict.m_options & Options.CheckForVacuousValueUpdates) == 0 || !e.value.Equals(in_value)) cfg.SetEntryValueReadback(gx, ref in_value); out_value = in_value; } else { /// retrieve the value for the caller out_value = e.value; if (op == Op.Remove) { /// easy way #1: removing the first one: change the header link and the decrement the count if (i == 0) { if (!bh.TryUpdate(e.gx_next, c - 1)) goto sync_restart; cfg.ReleaseIndex(gx); } else if (i == c - 1) { /// easy way #2: removing the last one: just decrement the count if (!bh.TryUpdate(bh.CapturedFirstSlotIndex, c - 1)) goto sync_restart; cfg.ReleaseIndex(gx); } else { /// hard way: copy nodes from 0..i-1 int gx_head, gx_tail; if (!cfg.CopyChain(bh, i, out gx_head, out gx_tail)) goto sync_restart; cfg.SetEntryNext(gx_tail, e.gx_next); if (!bh.TryUpdate(gx_head, c - 1)) { cfg.ReleaseChain(gx_head, i); goto sync_restart; } /// success: release the old chain up to and including the entry being removed. The /// new chain is re-using the rest cfg.ReleaseChain(bh.CapturedFirstSlotIndex, i + 1); } Interlocked.Decrement(ref cfg.m_count); } } return true; } gx = e.gx_next; } out_value = default(V); return false; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool _try_update_atomic(K key, ref V in_value, ref V cmp_value) { uint h = _check_key(key); sync_restart: Config cfg = m_config.dict.m_config; Config.BucketList.BucketHead bh; if (!cfg.m_buckets.GetBucketHead(h, out bh)) goto sync_restart; int c = bh.CapturedSlotCount; int gx = bh.CapturedFirstSlotIndex; for (int i = 0; i < c; i++) { EntryBlock.Entry e; cfg.GetEntryLoose(gx, out e); if (bh.IsExpired) goto sync_restart; if (e.hash == h && e.key.Equals(key)) { /// value types cannot use compareexchange of course if (i == 0) { /// we make no ordering guarantees so it's ok to exit if (!e.value.Equals(cmp_value)) return false; if ((cfg.dict.m_options & Options.CheckForVacuousValueUpdates) > 0 && cmp_value.Equals(in_value)) return true; /// replace first one: easy case // e.value = in_value; // EntryBlock.SlotAddr sa_new = cfg.GetUnusedSlot(); // sa_new.StoreEntry(ref e); //if (!bh.TryUpdate()) //goto sync_restart; //cfg.ReleaseSlot(sa); } else { /// for all others, build new chain } return true; } gx = e.gx_next; } return false; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// uint _check_key(K key) { if (f_null_check && key == null) throw new ArgumentNullException("key"); return (uint)key.GetHashCode(); } };