StandardRuntimePlatform.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Reflection;
  6. using System.Resources;
  7. using System.Runtime.CompilerServices;
  8. using System.Runtime.InteropServices;
  9. using System.Threading;
  10. using Avalonia.Platform;
  11. namespace Avalonia.Shared.PlatformSupport
  12. {
  13. internal partial class StandardRuntimePlatform : IRuntimePlatform
  14. {
  15. public void PostThreadPoolItem(Action cb) => ThreadPool.UnsafeQueueUserWorkItem(_ => cb(), null);
  16. public Assembly[] GetLoadedAssemblies() => AppDomain.CurrentDomain.GetAssemblies();
  17. public IDisposable StartSystemTimer(TimeSpan interval, Action tick)
  18. {
  19. return new Timer(_ => tick(), null, interval, interval);
  20. }
  21. public string GetStackTrace() => Environment.StackTrace;
  22. public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(this, size);
  23. class UnmanagedBlob : IUnmanagedBlob
  24. {
  25. private readonly StandardRuntimePlatform _plat;
  26. private IntPtr _address;
  27. private readonly object _lock = new object();
  28. #if DEBUG
  29. private static readonly List<string> Backtraces = new List<string>();
  30. private static Thread GCThread;
  31. private readonly string _backtrace;
  32. private static readonly object _btlock = new object();
  33. class GCThreadDetector
  34. {
  35. ~GCThreadDetector()
  36. {
  37. GCThread = Thread.CurrentThread;
  38. }
  39. }
  40. [MethodImpl(MethodImplOptions.NoInlining)]
  41. static void Spawn() => new GCThreadDetector();
  42. static UnmanagedBlob()
  43. {
  44. Spawn();
  45. GC.WaitForPendingFinalizers();
  46. }
  47. #endif
  48. public UnmanagedBlob(StandardRuntimePlatform plat, int size)
  49. {
  50. if (size <= 0)
  51. throw new ArgumentException("Positive number required", nameof(size));
  52. _plat = plat;
  53. _address = plat.Alloc(size);
  54. GC.AddMemoryPressure(size);
  55. Size = size;
  56. #if DEBUG
  57. _backtrace = Environment.StackTrace;
  58. lock (_btlock)
  59. Backtraces.Add(_backtrace);
  60. #endif
  61. }
  62. void DoDispose()
  63. {
  64. lock (_lock)
  65. {
  66. if (!IsDisposed)
  67. {
  68. #if DEBUG
  69. lock (_btlock)
  70. Backtraces.Remove(_backtrace);
  71. #endif
  72. _plat.Free(_address, Size);
  73. GC.RemoveMemoryPressure(Size);
  74. IsDisposed = true;
  75. _address = IntPtr.Zero;
  76. Size = 0;
  77. }
  78. }
  79. }
  80. public void Dispose()
  81. {
  82. #if DEBUG
  83. if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId)
  84. {
  85. Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: "
  86. + Environment.StackTrace
  87. + "\n\nBlob created by " + _backtrace);
  88. }
  89. #endif
  90. DoDispose();
  91. GC.SuppressFinalize(this);
  92. }
  93. ~UnmanagedBlob()
  94. {
  95. #if DEBUG
  96. Console.Error.WriteLine("Undisposed native blob created by " + _backtrace);
  97. #endif
  98. DoDispose();
  99. }
  100. public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address;
  101. public int Size { get; private set; }
  102. public bool IsDisposed { get; private set; }
  103. }
  104. #if FULLDOTNET || DOTNETCORE
  105. [DllImport("libc", SetLastError = true)]
  106. private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset);
  107. [DllImport("libc", SetLastError = true)]
  108. private static extern int munmap(IntPtr addr, IntPtr length);
  109. [DllImport("libc", SetLastError = true)]
  110. private static extern long sysconf(int name);
  111. private bool? _useMmap;
  112. private bool UseMmap
  113. => _useMmap ?? ((_useMmap = GetRuntimeInfo().OperatingSystem == OperatingSystemType.Linux)).Value;
  114. IntPtr Alloc(int size)
  115. {
  116. if (UseMmap)
  117. {
  118. var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero);
  119. if (rv.ToInt64() == -1 || (ulong) rv.ToInt64() == 0xffffffff)
  120. {
  121. var errno = Marshal.GetLastWin32Error();
  122. throw new Exception("Unable to allocate memory: " + errno);
  123. }
  124. return rv;
  125. }
  126. else
  127. return Marshal.AllocHGlobal(size);
  128. }
  129. void Free(IntPtr ptr, int len)
  130. {
  131. if (UseMmap)
  132. {
  133. if (munmap(ptr, new IntPtr(len)) == -1)
  134. {
  135. var errno = Marshal.GetLastWin32Error();
  136. throw new Exception("Unable to free memory: " + errno);
  137. }
  138. }
  139. else
  140. Marshal.FreeHGlobal(ptr);
  141. }
  142. #else
  143. IntPtr Alloc(int size) => Marshal.AllocHGlobal(size);
  144. void Free(IntPtr ptr, int len) => Marshal.FreeHGlobal(ptr);
  145. #endif
  146. }
  147. }