Skip to content

Commit

Permalink
introduce cache for sort handles to prevent from memory leak in certa…
Browse files Browse the repository at this point in the history
…in scenarios

Commit migrated from dotnet/coreclr@a4f5a22
  • Loading branch information
adamsitnik committed Jun 14, 2019
1 parent ba6160b commit c666b2b
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,30 @@
// See the LICENSE file in the project root for more information.

using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;

using Internal.Runtime.CompilerServices;

namespace System.Globalization
{
public partial class CompareInfo
{
[NonSerialized]
private IntPtr _sortHandle;

[NonSerialized]
private bool _isAsciiEqualityOrdinal;

// in most scenarios there is a limited number of cultures with limited number of sort options
// so caching the sort handles and not freeing them is OK, see https://github.com/dotnet/coreclr/pull/25117 for more
[NonSerialized]
private static Dictionary<string, IntPtr> s_sortNameToSortHandleCache;

private void InitSort(CultureInfo culture)
{
_sortName = culture.SortName;
Expand All @@ -27,17 +37,38 @@ private void InitSort(CultureInfo culture)
}
else
{
Interop.Globalization.ResultCode resultCode = Interop.Globalization.GetSortHandle(GetNullTerminatedUtf8String(_sortName), out _sortHandle);
if (resultCode != Interop.Globalization.ResultCode.Success)
{
Interop.Globalization.CloseSortHandle(_sortHandle);
_isAsciiEqualityOrdinal = (_sortName == "en-US" || _sortName == "");

if (resultCode == Interop.Globalization.ResultCode.OutOfMemory)
throw new OutOfMemoryException();
lock (_lock)
{
if (s_sortNameToSortHandleCache == null)
{
s_sortNameToSortHandleCache = new Dictionary<string, IntPtr>();
}

throw new ExternalException(SR.Arg_ExternalException);
if (!s_sortNameToSortHandleCache.TryGetValue(_sortName, out _sortHandle))
{
s_sortNameToSortHandleCache.Add(_sortName, (_sortHandle = GetSortHandle(_sortName)));
}
}
_isAsciiEqualityOrdinal = (_sortName == "en-US" || _sortName == "");
}
}

private static IntPtr GetSortHandle(string sortName)
{
var resultCode = Interop.Globalization.GetSortHandle(GetNullTerminatedUtf8String(sortName), out IntPtr sortHandle);
if (resultCode == Interop.Globalization.ResultCode.Success)
{
return sortHandle;
}
else
{
Interop.Globalization.CloseSortHandle(sortHandle);

if (resultCode == Interop.Globalization.ResultCode.OutOfMemory)
throw new OutOfMemoryException();

throw new ExternalException(SR.Arg_ExternalException);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,9 @@ private unsafe bool EndsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> suffi
}

// PAL ends here
[NonSerialized]
private IntPtr _sortHandle;

private const uint LCMAP_SORTKEY = 0x00000400;
private const uint LCMAP_HASH = 0x00040000;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public partial class CompareInfo : IDeserializationCallback
~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace |
CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.StringSort);

[NonSerialized]
private static readonly object _lock = new object(); // must be initialized before Invariant field

// Cache the invariant CompareInfo
internal static readonly CompareInfo Invariant = CultureInfo.InvariantCulture.CompareInfo;

Expand All @@ -57,9 +60,6 @@ public partial class CompareInfo : IDeserializationCallback

private int culture; // Do not rename (binary serialization). The fields sole purpose is to support Desktop serialization.

[NonSerialized]
private IntPtr _sortHandle;

internal CompareInfo(CultureInfo culture)
{
m_name = culture._name;
Expand Down

0 comments on commit c666b2b

Please sign in to comment.