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();
}
};