diff options
Diffstat (limited to 'main/src')
9 files changed, 346 insertions, 402 deletions
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/AbstractGenerateAction.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/AbstractGenerateAction.cs index 8a7609ab5a..6a49c74505 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/AbstractGenerateAction.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/AbstractGenerateAction.cs @@ -121,12 +121,12 @@ namespace MonoDevelop.CodeGeneration static string AddIndent (string text, string indent) { var doc = TextEditorFactory.CreateNewReadonlyDocument (new StringTextSource (text), ""); - var result = StringBuilderCache.New (); + var result = StringBuilderCache.Allocate (); foreach (var line in doc.GetLines ()) { result.Append (indent); result.Append (doc.GetTextAt (line.SegmentIncludingDelimiter)); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public void GenerateCode (Gtk.TreeView treeView) @@ -145,7 +145,7 @@ namespace MonoDevelop.CodeGeneration includedMembers.Add (store.GetValue (iter, 3)); } } - var output = StringBuilderCache.New (); + var output = StringBuilderCache.Allocate (); string indent = options.Editor.GetVirtualIndentationString (options.Editor.CaretLine); foreach (string nodeText in GenerateCode (includedMembers)) { if (output.Length > 0) { @@ -159,7 +159,7 @@ namespace MonoDevelop.CodeGeneration var data = options.Editor; data.EnsureCaretIsNotVirtual (); int offset = data.CaretOffset; - var text = output.ToString ().TrimStart (); + var text = StringBuilderCache.ReturnAndFree (output).TrimStart (); using (var undo = data.OpenUndoGroup ()) { data.InsertAtCaret (text); OnTheFlyFormatter.Format (data, options.DocumentContext, offset, offset + text.Length); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Logging/AssertLoggingTraceListener.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Logging/AssertLoggingTraceListener.cs index f9f8bf53c7..3778df5720 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Logging/AssertLoggingTraceListener.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Logging/AssertLoggingTraceListener.cs @@ -45,7 +45,7 @@ namespace MonoDevelop.Core.Logging if (callerFrame == frames.Length - 1) callerFrame = 0; - var sb = StringBuilderCache.New(); + var sb = StringBuilderCache.Allocate(); if (IsRealMessage (message)) { if (!string.IsNullOrEmpty (detailMessage)) { sb.AppendFormat ("Failed assertion: {0} - {1}", message, detailMessage); @@ -61,7 +61,7 @@ namespace MonoDevelop.Core.Logging sb.Append ("\n"); FormatStackTrace (sb, frames, callerFrame); - LoggingService.LogError (sb.ToString ()); + LoggingService.LogError (StringBuilderCache.ReturnAndFree(sb)); } static bool IsRealMessage (string message) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj index 108da5d396..6285a560d1 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj @@ -757,6 +757,8 @@ <Compile Include="MonoDevelop.FSW\FileSystemWatcher.cs" /> <Compile Include="MonoDevelop.FSW\Mono\FileSystemWatcher.cs" /> <Compile Include="MonoDevelop.Core\StringBuilderCache.cs" /> + <Compile Include="MonoDevelop.Core\ObjectPool.cs" /> + <Compile Include="MonoDevelop.Core\SharedPools.cs" /> </ItemGroup> <ItemGroup> <None Include="Makefile.am" /> diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ObjectPool.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ObjectPool.cs new file mode 100644 index 0000000000..93b3464ff2 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ObjectPool.cs @@ -0,0 +1,269 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +// define TRACE_LEAKS to get additional diagnostics that can lead to the leak sources. note: it will +// make everything about 2-3x slower +// +// #define TRACE_LEAKS + +// define DETECT_LEAKS to detect possible leaks +// #if DEBUG +// #define DETECT_LEAKS //for now always enable DETECT_LEAKS in debug. +// #endif + +using System; +using System.Diagnostics; +using System.Threading; + +#if DETECT_LEAKS +using System.Runtime.CompilerServices; + +#endif + +namespace MonoDevelop.Core +{ + /// <summary> + /// Generic implementation of object pooling pattern with predefined pool size limit. The main + /// purpose is that limited number of frequently used objects can be kept in the pool for + /// further recycling. + /// + /// Notes: + /// 1) it is not the goal to keep all returned objects. Pool is not meant for storage. If there + /// is no space in the pool, extra returned objects will be dropped. + /// + /// 2) it is implied that if object was obtained from a pool, the caller will return it back in + /// a relatively short time. Keeping checked out objects for long durations is ok, but + /// reduces usefulness of pooling. Just new up your own. + /// + /// Not returning objects to the pool in not detrimental to the pool's work, but is a bad practice. + /// Rationale: + /// If there is no intent for reusing the object, do not use pool - just use "new". + /// </summary> + internal class ObjectPool<T> where T : class + { + [DebuggerDisplay ("{Value,nq}")] + private struct Element + { + internal T Value; + } + + /// <remarks> + /// Not using System.Func{T} because this file is linked into the (debugger) Formatter, + /// which does not have that type (since it compiles against .NET 2.0). + /// </remarks> + internal delegate T Factory (); + + // Storage for the pool objects. The first item is stored in a dedicated field because we + // expect to be able to satisfy most requests from it. + private T _firstItem; + private readonly Element [] _items; + + // factory is stored for the lifetime of the pool. We will call this only when pool needs to + // expand. compared to "new T()", Func gives more flexibility to implementers and faster + // than "new T()". + private readonly Factory _factory; + +#if DETECT_LEAKS + private static readonly ConditionalWeakTable<T, LeakTracker> leakTrackers = new ConditionalWeakTable<T, LeakTracker>(); + + private class LeakTracker : IDisposable + { + private volatile bool disposed; + +#if TRACE_LEAKS + internal volatile object Trace = null; +#endif + + public void Dispose() + { + disposed = true; + GC.SuppressFinalize(this); + } + + private string GetTrace() + { +#if TRACE_LEAKS + return Trace == null ? "" : Trace.ToString(); +#else + return "Leak tracing information is disabled. Define TRACE_LEAKS on ObjectPool`1.cs to get more info \n"; +#endif + } + + ~LeakTracker() + { + if (!this.disposed && !Environment.HasShutdownStarted) + { + var trace = GetTrace(); + + // If you are seeing this message it means that object has been allocated from the pool + // and has not been returned back. This is not critical, but turns pool into rather + // inefficient kind of "new". + Debug.WriteLine($"TRACEOBJECTPOOLLEAKS_BEGIN\nPool detected potential leaking of {typeof(T)}. \n Location of the leak: \n {GetTrace()} TRACEOBJECTPOOLLEAKS_END"); + } + } + } +#endif + + internal ObjectPool (Factory factory) + : this (factory, Environment.ProcessorCount * 2) + { } + + internal ObjectPool (Factory factory, int size) + { + Debug.Assert (size >= 1); + _factory = factory; + _items = new Element [size - 1]; + } + + private T CreateInstance () + { + var inst = _factory (); + return inst; + } + + /// <summary> + /// Produces an instance. + /// </summary> + /// <remarks> + /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. + /// Note that Free will try to store recycled objects close to the start thus statistically + /// reducing how far we will typically search. + /// </remarks> + internal T Allocate () + { + // PERF: Examine the first element. If that fails, AllocateSlow will look at the remaining elements. + // Note that the initial read is optimistically not synchronized. That is intentional. + // We will interlock only when we have a candidate. in a worst case we may miss some + // recently returned objects. Not a big deal. + T inst = _firstItem; + if (inst == null || inst != Interlocked.CompareExchange (ref _firstItem, null, inst)) { + inst = AllocateSlow (); + } + +#if DETECT_LEAKS + var tracker = new LeakTracker(); + leakTrackers.Add(inst, tracker); + +#if TRACE_LEAKS + var frame = CaptureStackTrace(); + tracker.Trace = frame; +#endif +#endif + return inst; + } + + private T AllocateSlow () + { + var items = _items; + + for (int i = 0; i < items.Length; i++) { + // Note that the initial read is optimistically not synchronized. That is intentional. + // We will interlock only when we have a candidate. in a worst case we may miss some + // recently returned objects. Not a big deal. + T inst = items [i].Value; + if (inst != null) { + if (inst == Interlocked.CompareExchange (ref items [i].Value, null, inst)) { + return inst; + } + } + } + + return CreateInstance (); + } + + /// <summary> + /// Returns objects to the pool. + /// </summary> + /// <remarks> + /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. + /// Note that Free will try to store recycled objects close to the start thus statistically + /// reducing how far we will typically search in Allocate. + /// </remarks> + internal void Free (T obj) + { + Validate (obj); + ForgetTrackedObject (obj); + + if (_firstItem == null) { + // Intentionally not using interlocked here. + // In a worst case scenario two objects may be stored into same slot. + // It is very unlikely to happen and will only mean that one of the objects will get collected. + _firstItem = obj; + } else { + FreeSlow (obj); + } + } + + private void FreeSlow (T obj) + { + var items = _items; + for (int i = 0; i < items.Length; i++) { + if (items [i].Value == null) { + // Intentionally not using interlocked here. + // In a worst case scenario two objects may be stored into same slot. + // It is very unlikely to happen and will only mean that one of the objects will get collected. + items [i].Value = obj; + break; + } + } + } + + /// <summary> + /// Removes an object from leak tracking. + /// + /// This is called when an object is returned to the pool. It may also be explicitly + /// called if an object allocated from the pool is intentionally not being returned + /// to the pool. This can be of use with pooled arrays if the consumer wants to + /// return a larger array to the pool than was originally allocated. + /// </summary> + [Conditional ("DEBUG")] + internal void ForgetTrackedObject (T old, T replacement = null) + { +#if DETECT_LEAKS + LeakTracker tracker; + if (leakTrackers.TryGetValue(old, out tracker)) + { + tracker.Dispose(); + leakTrackers.Remove(old); + } + else + { + var trace = CaptureStackTrace(); + Debug.WriteLine($"TRACEOBJECTPOOLLEAKS_BEGIN\nObject of type {typeof(T)} was freed, but was not from pool. \n Callstack: \n {trace} TRACEOBJECTPOOLLEAKS_END"); + } + + if (replacement != null) + { + tracker = new LeakTracker(); + leakTrackers.Add(replacement, tracker); + } +#endif + } + +#if DETECT_LEAKS + private static Lazy<Type> _stackTraceType = new Lazy<Type>(() => Type.GetType("System.Diagnostics.StackTrace")); + + private static object CaptureStackTrace() + { + return Activator.CreateInstance(_stackTraceType.Value); + } +#endif + + [Conditional ("DEBUG")] + private void Validate (object obj) + { + Debug.Assert (obj != null, "freeing null?"); + + Debug.Assert (_firstItem != obj, "freeing twice?"); + + var items = _items; + for (int i = 0; i < items.Length; i++) { + var value = items [i].Value; + if (value == null) { + } + return; + + Debug.Assert (value != obj, "freeing twice?"); + } + } + } +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/SharedPools.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/SharedPools.cs new file mode 100644 index 0000000000..b74dd4a279 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/SharedPools.cs @@ -0,0 +1,58 @@ +// +// StringBuilderCache.cs +// +// Author: +// Mike Krüger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +namespace MonoDevelop.Core +{ + static class SharedPools + { + /// <summary> + /// pool that uses default constructor with 100 elements pooled + /// </summary> + public static ObjectPool<T> BigDefault<T> () where T : class, new() + { + return DefaultBigPool<T>.Instance; + } + + /// <summary> + /// pool that uses default constructor with 20 elements pooled + /// </summary> + public static ObjectPool<T> Default<T> () where T : class, new() + { + return DefaultNormalPool<T>.Instance; + } + + static class DefaultBigPool<T> where T : class, new() + { + public static readonly ObjectPool<T> Instance = new ObjectPool<T> (() => new T (), 100); + } + + static class DefaultNormalPool<T> where T : class, new() + { + public static readonly ObjectPool<T> Instance = new ObjectPool<T> (() => new T (), 20); + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringBuilderCache.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringBuilderCache.cs index a184724f47..b1cde57805 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringBuilderCache.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringBuilderCache.cs @@ -24,12 +24,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -using System; -using System.Diagnostics; using System.Text; -using System.Threading; -using System.Collections.Immutable; -using System.Security.Cryptography; namespace MonoDevelop.Core { @@ -38,401 +33,21 @@ namespace MonoDevelop.Core /// </summary> public static class StringBuilderCache { - static ImmutableQueue<CachedStringBuilder> pool = ImmutableQueue<CachedStringBuilder>.Empty; - - public static CachedStringBuilder New () + public static StringBuilder Allocate () { - pool = pool.Dequeue (out CachedStringBuilder result); - if (result != null) { - result.Cached = false; - return result; - } - return new CachedStringBuilder (); + return SharedPools.Default<StringBuilder> ().Allocate (); } - internal static void Put (CachedStringBuilder sb) + public static void Free (StringBuilder sb) { sb.Clear (); - sb.Cached = true; - pool = pool.Enqueue (sb); - } - } - - /// <summary> - /// A string builder which puts itself into the object pool on a ToString() call. - /// </summary> - public class CachedStringBuilder - { - readonly StringBuilder sb = new StringBuilder (); - - internal bool Cached { get; set; } - - public int Capacity { get => sb.Capacity; set => sb.Capacity = value; } - public int Length { get => sb.Length; set => sb.Length = value; } - - public int MaxCapacity { get => sb.MaxCapacity; } - - public char this [int index] { get => sb [index]; set => sb [index] = value; } - - public CachedStringBuilder Append (long value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (ulong value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (uint value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (ushort value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (string value, int startIndex, int count) - { - sb.Append (value, startIndex, count); - return this; - } - - public CachedStringBuilder Append (string value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (float value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (sbyte value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (object value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (int value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (char [] value, int startIndex, int charCount) - { - sb.Append (value, startIndex, charCount); - return this; - } - - public CachedStringBuilder Append (double value) - { - sb.Append (value); - return this; + SharedPools.Default<StringBuilder> ().Free (sb); } - public CachedStringBuilder Append (decimal value) + public static string ReturnAndFree (StringBuilder sb) { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (char [] value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (char value, int repeatCount) - { - sb.Append (value, repeatCount); - return this; - } - - public unsafe CachedStringBuilder Append (char* value, int valueCount) - { - sb.Append (value, valueCount); - return this; - } - - public CachedStringBuilder Append (char value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (byte value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (bool value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder Append (short value) - { - sb.Append (value); - return this; - } - - public CachedStringBuilder AppendFormat (string format, object arg0, object arg1, object arg2) - { - sb.AppendFormat (format, arg0, arg1, arg2); - return this; - } - - public CachedStringBuilder AppendFormat (string format, params object [] args) - { - sb.AppendFormat (format, args); - return this; - } - - public CachedStringBuilder AppendFormat (string format, object arg0, object arg1) - { - sb.AppendFormat (format, arg0, arg1); - return this; - } - - public CachedStringBuilder AppendFormat (string format, object arg0) - { - sb.AppendFormat (format, arg0); - return this; - } - - public CachedStringBuilder AppendFormat (IFormatProvider provider, string format, object arg0, object arg1, object arg2) - { - sb.AppendFormat (provider, format, arg0, arg1, arg2); - return this; - } - - public CachedStringBuilder AppendFormat (IFormatProvider provider, string format, object arg0, object arg1) - { - sb.AppendFormat (provider, format, arg0, arg1); - return this; - } - - public CachedStringBuilder AppendFormat (IFormatProvider provider, string format, object arg0) - { - sb.AppendFormat (provider, format, arg0); - return this; - } - - public CachedStringBuilder AppendFormat (IFormatProvider provider, string format, params object [] args) - { - sb.AppendFormat (provider, format, args); - return this; - } - - public CachedStringBuilder AppendLine () - { - sb.AppendLine (); - return this; - } - - public CachedStringBuilder AppendLine (string value) - { - sb.AppendLine (value); - return this; - } - - public CachedStringBuilder Clear () - { - sb.Clear (); - return this; - } - - public void CopyTo (int sourceIndex, char [] destination, int destinationIndex, int count) - { - sb.CopyTo (sourceIndex, destination, destinationIndex, count); - } - - public int EnsureCapacity (int capacity) - { - return sb.EnsureCapacity(capacity); - } - - public bool Equals (StringBuilder sb) - { - return this.sb.Equals (sb); - } - - public bool Equals (CachedStringBuilder csb) - { - return this.sb.Equals (csb.sb); - } - - public CachedStringBuilder Insert (int index, uint value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, ushort value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, string value, int count) - { - sb.Insert (index, value, count); - return this; - } - - public CachedStringBuilder Insert (int index, string value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, float value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, sbyte value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, ulong value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, object value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, double value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, int value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, decimal value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, char [] value, int startIndex, int charCount) - { - sb.Insert (index, value, startIndex, charCount); - return this; - } - - public CachedStringBuilder Insert (int index, char [] value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, char value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, byte value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, bool value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, long value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Insert (int index, short value) - { - sb.Insert (index, value); - return this; - } - - public CachedStringBuilder Remove (int startIndex, int length) - { - sb.Remove (startIndex, length); - return this; - } - - public CachedStringBuilder Replace (string oldValue, string newValue, int startIndex, int count) - { - sb.Replace (oldValue, newValue, startIndex, count); - return this; - } - - public CachedStringBuilder Replace (char oldChar, char newChar) - { - sb.Replace (oldChar, newChar); - return this; - } - - public CachedStringBuilder Replace (char oldChar, char newChar, int startIndex, int count) - { - sb.Replace (oldChar, newChar, startIndex, count); - return this; - } - - public CachedStringBuilder Replace (string oldValue, string newValue) - { - sb.Replace (oldValue, newValue); - return this; - } - - public override string ToString () - { - if (Cached) - throw new InvalidOperationException ("Object is cached."); var result = sb.ToString (); - StringBuilderCache.Put (this); - return result; - } - - public string ToString (int startIndex, int length) - { - if (Cached) - throw new InvalidOperationException ("Object is cached."); - var result = sb.ToString (startIndex, length); - StringBuilderCache.Put (this); + Free (sb); return result; } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs index 3da90ec0c1..c6e6c03918 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs @@ -106,7 +106,7 @@ namespace MonoDevelop.Components.MainToolbar { var lane = StringMatcher.GetMatcher (toMatch, true).GetMatch (text);
var matchHexColor = selected ? selectedResultMatchTextColor : resultMatchTextColor; - var result = StringBuilderCache.New (); + var result = StringBuilderCache.Allocate (); if (lane != null) { int lastPos = 0; for (int n=0; n < lane.Length; n++) { @@ -125,7 +125,7 @@ namespace MonoDevelop.Components.MainToolbar } else { MarkupUtilities.AppendEscapedString (result, text, 0, text.Length); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public virtual bool CanActivate { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs index b48f83becd..5f50205a70 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs @@ -106,9 +106,9 @@ namespace MonoDevelop.Ide.TypeSystem if (String.IsNullOrEmpty (str)) return string.Empty; - var sb = StringBuilderCache.New (); + var sb = StringBuilderCache.Allocate (); MarkupUtilities.AppendEscapedString (sb, str, 0, str.Length); - return sb.ToString (); + return StringBuilderCache.ReturnAndFree(sb); } #region Documentation diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MarkupUtilities.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MarkupUtilities.cs index 5331637730..2cc64ee033 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MarkupUtilities.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MarkupUtilities.cs @@ -40,7 +40,7 @@ namespace MonoDevelop.Ide.TypeSystem static string EscapedApostrophe = "'"; static string EscapedQuote = """; - public static void AppendEscapedString (CachedStringBuilder builder, string toEscape, int start, int count) + public static void AppendEscapedString (StringBuilder builder, string toEscape, int start, int count) { if (toEscape == null) return; |