예제 #1
0
            internal static void Release(ReuseableStringBuilder returning)
            {
#if DEBUG && ASSERT_BALANCE // Please define ASSERT_BALANCE if you need to analyze where we have cross thread competing usage of ReuseableStringBuilder
                int balance = Interlocked.Decrement(ref s_getVsReleaseBalance);
                Debug.Assert(balance == 0, "Unbalanced Get vs Release. Either forgotten Release or used from multiple threads concurrently.");
#endif
                FrameworkErrorUtilities.VerifyThrowInternalNull(returning._borrowedBuilder, nameof(returning._borrowedBuilder));

                StringBuilder returningBuilder = returning._borrowedBuilder !;
                int           returningLength  = returningBuilder.Length;

                // It's possible for someone to cause the builder to
                // enlarge to such an extent that this static field
                // would be a leak. To avoid that, only accept
                // the builder if it's no more than a certain size.
                //
                // If some code has a bug and forgets to return their builder
                // (or we refuse it here because it's too big) the next user will
                // get given a new one, and then return it soon after.
                // So the shared builder will be "replaced".
                if (returningBuilder.Capacity > MaxBuilderSizeCapacity)
                {
                    // In order to free memory usage by huge string builder, do not pool this one and let it be collected.
#if DEBUG
                    MSBuildEventSource.Log.ReusableStringBuilderFactoryStop(hash: returningBuilder.GetHashCode(), returningCapacity: returningBuilder.Capacity, returningLength: returningLength, type: "discard");
#endif
                }
                else
                {
                    if (returningBuilder.Capacity != returning._borrowedWithCapacity)
                    {
                        Debug.Assert(returningBuilder.Capacity > returning._borrowedWithCapacity, "Capacity can only increase");

                        // This builder used more than pre-allocated capacity bracket.
                        // Let this builder be collected and put new builder, with reflecting bracket capacity, into the pool.
                        // If we would just return this builder into pool as is, it would allocated new array[capacity] anyway (current implementation of returningBuilder.Clear() does it)
                        //   and that could lead to unpredictable amount of LOH allocations and eventual LOH fragmentation.
                        // Below implementation has predictable max Log2(MaxBuilderSizeBytes) string builder array re-allocations during whole process lifetime - unless MaxBuilderSizeCapacity is reached frequently.
                        int newCapacity = SelectBracketedCapacity(returningBuilder.Capacity);
                        returningBuilder = new StringBuilder(newCapacity);
                    }

                    returningBuilder.Clear(); // Clear before pooling

                    var oldSharedBuilder = Interlocked.Exchange(ref s_sharedBuilder, returningBuilder);
                    if (oldSharedBuilder != null)
                    {
                        // This can identify improper usage from multiple thread or bug in code - Get was reentered before Release.
                        // User of ReuseableStringBuilder has to make sure that calling method call stacks do not also use ReuseableStringBuilder.
                        // Look at stack traces of ETW events which contains reported string builder hashes.
                        MSBuildEventSource.Log.ReusableStringBuilderFactoryUnbalanced(oldHash: oldSharedBuilder.GetHashCode(), newHash: returningBuilder.GetHashCode());
                    }
#if DEBUG
                    MSBuildEventSource.Log.ReusableStringBuilderFactoryStop(hash: returningBuilder.GetHashCode(), returningCapacity: returningBuilder.Capacity, returningLength: returningLength, type: returning._borrowedBuilder != returningBuilder ? "return-new" : "return");
#endif
                }

                // Ensure ReuseableStringBuilder can no longer use _borrowedBuilder
                returning._borrowedBuilder = null;
            }
예제 #2
0
        private void LazyPrepare()
        {
            if (_borrowedBuilder == null)
            {
                FrameworkErrorUtilities.VerifyThrow(_capacity != -1, "Reusing after dispose");

                _borrowedBuilder      = ReuseableStringBuilderFactory.Get(_capacity);
                _borrowedWithCapacity = _borrowedBuilder.Capacity;
            }
        }