/// <summary> /// After insert, checks whether a split should be initiated and whether there are buckets which need to be split /// </summary> private void PerformPostInsertSplitOperations(ref Accessors accessors, int count) { // If necessary, trigger a split. if (Buckets.GrowIfNecessary(ref m_buckets, count)) { return; } int splitBucketNo; int targetBucketNo; if (m_buckets.TryGetBucketToSplit(out splitBucketNo, out targetBucketNo)) { // Only need to get lock for split bucket since target bucket // will always use the same lock int splitLockNo = GetLockNo(splitBucketNo); using (var writeLock = m_locks.AcquireWriteLock(splitLockNo, allowReads: true)) { var nodeIndex = m_buckets[splitBucketNo]; if (nodeIndex >= 0) { // We only need to exclude reads if the bucket actually has nodes which may need to be split. writeLock.ExcludeReads(); } int lastSplitNodeIndex = -3; int lastTargetNodeIndex = -3; while (nodeIndex >= 0) { var node = accessors.Nodes[nodeIndex]; var nextNodeIndex = node.Next; int bucketNo = m_buckets.GetBucketNo(node.Hashcode); Node newNode; if (bucketNo == splitBucketNo) { newNode = new Node(node.Hashcode, lastSplitNodeIndex); lastSplitNodeIndex = nodeIndex; } else { newNode = new Node(node.Hashcode, lastTargetNodeIndex); lastTargetNodeIndex = nodeIndex; } accessors.Nodes[nodeIndex] = newNode; nodeIndex = nextNodeIndex; } m_buckets.SetBucketHeadNodeIndexDirect(splitBucketNo, lastSplitNodeIndex); m_buckets.SetBucketHeadNodeIndexDirect(targetBucketNo, lastTargetNodeIndex); m_buckets.EndBucketSplit(); } } }