Skip to content

Commit fbaf8b5

Browse files
committed
Fix FontSource/FontFileStream embedded resource memory leak
1 parent 73c7e63 commit fbaf8b5

File tree

1 file changed

+32
-12
lines changed
  • src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/FontCache

1 file changed

+32
-12
lines changed

src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/FontCache/FontSource.cs

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -184,27 +184,27 @@ public UnmanagedMemoryStream GetUnmanagedStream()
184184
{
185185
WebResponse response = WpfWebRequestHelper.CreateRequestAndGetResponse(_fontUri);
186186
fontStream = response.GetResponseStream();
187-
if (String.Equals(response.ContentType, ObfuscatedContentType, StringComparison.Ordinal))
187+
if (string.Equals(response.ContentType, ObfuscatedContentType, StringComparison.Ordinal))
188188
{
189189
// The third parameter makes sure the original stream is closed
190190
// when the deobfuscating stream is disposed.
191191
fontStream = new DeobfuscatingStream(fontStream, _fontUri, false);
192192
}
193193
}
194194

195-
UnmanagedMemoryStream unmanagedStream = fontStream as UnmanagedMemoryStream;
196-
197-
if (unmanagedStream != null)
198-
return unmanagedStream;
195+
// We don't want any memory leaks
196+
// TODO: Remove FinalizableUnmanagedStream once FontFileStream is migrated from C++/CLI.
197+
if (fontStream is UnmanagedMemoryStream unmanagedMemoryStream)
198+
return new FinalizableUnmanagedStream(unmanagedMemoryStream);
199199

200+
// Convert the DeobfuscatingStream to byte[]; add it to our cache, dispose it
200201
bits = StreamToByteArray(fontStream);
202+
lock (s_resourceCache)
203+
{
204+
s_resourceCache.Add(_fontUri, bits, false);
205+
}
201206

202-
fontStream?.Close();
203-
}
204-
205-
lock (s_resourceCache)
206-
{
207-
s_resourceCache.Add(_fontUri, bits, false);
207+
fontStream.Close();
208208
}
209209

210210
return ByteArrayToUnmanagedStream(bits);
@@ -358,7 +358,7 @@ private Stream GetCompositeFontResourceStream()
358358
Assembly fontResourceAssembly = Assembly.GetExecutingAssembly();
359359
ResourceManager rm = new($"{ReflectionUtils.GetAssemblyPartialName(fontResourceAssembly)}.g", fontResourceAssembly);
360360

361-
return rm?.GetStream($"fonts/{fontFilename}");
361+
return rm.GetStream($"fonts/{fontFilename}");
362362
}
363363

364364
#endregion Private Methods
@@ -371,6 +371,26 @@ private Stream GetCompositeFontResourceStream()
371371

372372
#region Private Classes
373373

374+
/// <summary>
375+
/// Calls <see cref="UnmanagedMemoryStream.Dispose"/> from the destructor.
376+
/// </summary>
377+
// TODO: Remove this once FontFileStream is migrated from C++/CLI.
378+
private sealed class FinalizableUnmanagedStream : UnmanagedMemoryStream
379+
{
380+
private readonly UnmanagedMemoryStream _unmanaged;
381+
382+
internal unsafe FinalizableUnmanagedStream(UnmanagedMemoryStream unmanagedStream)
383+
{
384+
_unmanaged = unmanagedStream;
385+
Initialize(_unmanaged.PositionPointer, _unmanaged.Length, _unmanaged.Length, FileAccess.Read);
386+
}
387+
388+
~FinalizableUnmanagedStream()
389+
{
390+
_unmanaged.Dispose();
391+
}
392+
}
393+
374394
private class PinnedByteArrayStream : UnmanagedMemoryStream
375395
{
376396
internal PinnedByteArrayStream(byte [] bits)

0 commit comments

Comments
 (0)