Esempio n. 1
0
        /// <summary>
        /// Get the reference value for the provided baseline value.
        /// </summary>
        /// <param name="baseline"></param>
        /// <returns></returns>
        public static string GetReference(string baseline)
        {
            if (s_DisableCache)
            {
                return(baseline);
            }

            if (baseline == null)
            {
                return(null);  //can't do a reference to null anyway
            }
            if (baseline.Length == 0)
            {
                return(string.Empty);//this is a stock intered string.
            }

            string officialString = baseline; // We'll replace this with the official copy if there is one.

            try
            {
                object outerLock = s_Lock;                         // We need a copy we can null out when released.
                WeakStringCollection hashCodeCollisionNode = null; // Collects strings with the same hash code.

                try
                {
                    System.Threading.Monitor.Enter(outerLock); // Usually gets released in our finally block.

                    int baselineHashCode = baseline.GetHashCode();
                    if (s_StringReferences.TryGetValue(baselineHashCode, out hashCodeCollisionNode))
                    {
                        // The lookup by hash code gets us close, now have the little collection check for it.
                        if (hashCodeCollisionNode != null)
                        {
                            System.Threading.Monitor.Enter(hashCodeCollisionNode); // Lock the row so we can release the outer.
                            System.Threading.Monitor.Exit(outerLock);              // Release outer lock early to reduce contention.
                            outerLock = null;                                      // Mark the outer lock released so we don't release it again!

                            // This call will actually replace our tempString with any official copy if one is found.
                            hashCodeCollisionNode.PackAndOrAdd(ref officialString);
                            // Row lock will be released in the finally block.
                        }
                        else
                        {
                            // This case shouldn't happen, but we can replace the null entry with a new little collection.
                            s_StringReferences[baselineHashCode] = new WeakStringCollection(baseline);
                        }
                    }
                    else
                    {
                        // Didn't find anything for that hash code, so make a new little collection to hold the given string.
                        s_StringReferences.Add(baselineHashCode, new WeakStringCollection(baseline));
                        s_PeakReferenceSize = Math.Max(s_StringReferences.Count, s_PeakReferenceSize);
                    }
                }
                finally
                {
                    if (outerLock != null)
                    {
                        System.Threading.Monitor.Exit(outerLock);
                    }

                    if (hashCodeCollisionNode != null)
                    {
                        System.Threading.Monitor.Exit(hashCodeCollisionNode);
                    }
                }
            }
            catch (Exception ex)
            {
#if DEBUG
                Debug.WriteLine(string.Format("While trying to get the string reference for \"{0}\" an exception was thrown: {1}", baseline, ex.Message));
#endif
                GC.KeepAlive(ex); //just here to avoid a compiler warn in release mode
            }

            return(officialString);
        }
Esempio n. 2
0
        /// <summary>
        /// Check the cache for garbage collected values.
        /// </summary>
        public static void Pack()
        {
            if (s_DisableCache)
            {
                return;
            }

            //Our caller has a right to expect to never get an exception from this.
            try
            {
#if DEBUG
                Stopwatch packTimer = Stopwatch.StartNew();
                long      elapsedMilliseconds;
                int       deadNodesCount;
                int       singletonNodesCount = 0;
                int       multipleNodesCount  = 0;
                int       collapsedNodesCount = 0; // Nodes that had multiple but reduced to singleton.
#endif
                int startNodesCount;               // Declared here for use in DEBUG after the lock exits.
                KeyValuePair <int, WeakStringCollection>[] allNodes;
                List <int> deadNodes;
                lock (s_Lock)
                {
                    startNodesCount = s_StringReferences.Count;
                    if (startNodesCount == 0)
                    {
                        return;                                            //nothing here, nothing to collect.
                    }
                    deadNodes = new List <int>((startNodesCount / 4) + 1); //assume we could wipe out 25% every time.

                    // Make a snapshot of the little collection nodes in our table, so we can reduce contention while we pack.
                    allNodes = new KeyValuePair <int, WeakStringCollection> [s_StringReferences.Count];
                    int currentIndex = 0;
                    foreach (KeyValuePair <int, WeakStringCollection> node in s_StringReferences)
                    {
                        allNodes[currentIndex++] = node;
                    }
                }

                foreach (KeyValuePair <int, WeakStringCollection> keyValuePair in allNodes)
                {
                    WeakStringCollection currentReferencesList = keyValuePair.Value;
                    if (currentReferencesList == null)
                    {
                        deadNodes.Add(keyValuePair.Key); // Hmmm, shouldn't be here.  Let's remove the null node.
                        continue;                        // Try the next node.
                    }

                    lock (currentReferencesList)
                    {
#if DEBUG
                        bool singletonNode = (currentReferencesList.Count == 1);
                        if (singletonNode)
                        {
                            singletonNodesCount++;
                        }
                        else
                        {
                            multipleNodesCount++;
                        }
#endif

                        if (currentReferencesList.Pack() <= 0)
                        {
                            deadNodes.Add(keyValuePair.Key);
                        }
#if DEBUG
                        else
                        {
                            if (singletonNode == false && currentReferencesList.Count == 1)
                            {
                                collapsedNodesCount++;
                            }
                        }
#endif
                    }
                }

                lock (s_Lock) // Get the outer lock again so we can remove the dead nodes.
                {
                    //and now kill off our dead nodes.
                    foreach (int deadNodeKey in deadNodes)
                    {
                        WeakStringCollection currentNode;
                        if (s_StringReferences.TryGetValue(deadNodeKey, out currentNode))
                        {
                            if (currentNode != null)
                            {
                                lock (currentNode)
                                {
                                    if (currentNode.Count <= 0) // Check that it's still dead!
                                    {
                                        s_StringReferences.Remove(deadNodeKey);
                                    }
                                }
                            }
                            else
                            {
                                // This case shouldn't happen, but if it does there's nothing to lock or check.  Just remove it.
                                s_StringReferences.Remove(deadNodeKey);
                            }
                        }
                    }

                    //Finally, if we have killed a good percentage off we really need to shrink the dictionary itself.
                    if (s_PeakReferenceSize > MinimumRebuildSize)
                    {
                        double fillRatio = (s_StringReferences.Count / (double)((s_PeakReferenceSize == 0) ? 1 : s_PeakReferenceSize));
                        if (fillRatio < FreeSpaceRebuildRatio)
                        {
                            //it's bad enough we want to free the dictionary & rebuild it to make it small again.
#if DEBUG
                            Debug.WriteLine(string.Format("StringReference:  Rebuilding collection because fill ratio is {0}", fillRatio));
#endif
                            Dictionary <int, WeakStringCollection> newCollection = new Dictionary <int, WeakStringCollection>(s_StringReferences);
                            s_StringReferences  = newCollection;
                            s_PeakReferenceSize = newCollection.Count;
                        }
                    }
#if DEBUG
                    packTimer.Stop();
                    elapsedMilliseconds = packTimer.ElapsedMilliseconds;
                    deadNodesCount      = deadNodes.Count;
#endif
                    System.Threading.Monitor.PulseAll(s_Lock);
                }
#if DEBUG
                Debug.WriteLine(string.Format("StringReference:  Pack took {0} ms.", elapsedMilliseconds));
                Debug.WriteLine(string.Format("StringReference:  Removed {0} of {1} nodes from the cache ({2:F2} %).",
                                              deadNodesCount, startNodesCount, (deadNodesCount * 100.0) / startNodesCount));
                Debug.WriteLine(string.Format("StringReference:  {0} singleton nodes vs {1} multiple nodes ({2} collapsed to singleton)",
                                              singletonNodesCount, multipleNodesCount, collapsedNodesCount));
#endif
            }
            catch (Exception ex)
            {
                GC.KeepAlive(ex); //just here to avoid a compiler warn in release mode
            }
        }