/src/Sparrow/Platform/Posix/PosixElectricFencedMemory.cs

https://github.com/fitzchak/ravendb · C# · 122 lines · 79 code · 30 blank · 13 comment · 20 complexity · 29b0e8a80776a9e49d100b24b7b715a2 MD5 · raw file

  1. using System;
  2. using System.Runtime.InteropServices;
  3. using Sparrow.Platform.Posix;
  4. using Voron.Platform.Posix;
  5. using System.Threading;
  6. namespace Sparrow.Platform
  7. {
  8. public unsafe class PosixElectricFencedMemory
  9. {
  10. public static long usage =0;
  11. public static byte* Allocate(int size)
  12. {
  13. var remaining = size % 4096;
  14. var sizeInPages = (size / 4096) + (remaining == 0 ? 0 : 1);
  15. var allocatedSize = ((sizeInPages + 2) * 4096);
  16. var virtualAlloc = (byte*)Syscall.mmap64(IntPtr.Zero, (UIntPtr)allocatedSize, MmapProts.PROT_NONE,
  17. MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS, -1, 0L);
  18. if (virtualAlloc == (byte*)-1)
  19. {
  20. var err = Marshal.GetLastWin32Error();
  21. throw new InvalidOperationException("Failed to mmap with size " + allocatedSize + " . Err=" + err);
  22. }
  23. var msync = Syscall.msync((IntPtr)virtualAlloc, (UIntPtr)(uint)allocatedSize, MsyncFlags.MS_SYNC | MsyncFlags.MS_INVALIDATE);
  24. if (msync != 0)
  25. {
  26. throw new InvalidOperationException("Failed to free call msync " + (IntPtr)virtualAlloc + ". Err=" + Marshal.GetLastWin32Error());
  27. }
  28. virtualAlloc = (byte*)Syscall.mmap64((IntPtr)virtualAlloc, (UIntPtr)allocatedSize,
  29. MmapProts.PROT_READ | MmapProts.PROT_WRITE,
  30. MmapFlags.MAP_FIXED | MmapFlags.MAP_SHARED | MmapFlags.MAP_ANONYMOUS, -1, 0L);
  31. if (virtualAlloc == (byte*)-1)
  32. {
  33. var err = Marshal.GetLastWin32Error();
  34. throw new InvalidOperationException("Failed to re-mmap with size " + allocatedSize + " . Err=" + err);
  35. }
  36. msync = Syscall.msync((IntPtr)virtualAlloc, (UIntPtr)(uint)allocatedSize, MsyncFlags.MS_SYNC | MsyncFlags.MS_INVALIDATE);
  37. if (msync != 0)
  38. {
  39. throw new InvalidOperationException("Failed to free call msync " + (IntPtr)virtualAlloc + ". Err=" + Marshal.GetLastWin32Error());
  40. }
  41. *(int*)virtualAlloc = allocatedSize;
  42. if (Syscall.mprotect((IntPtr)virtualAlloc, 4096, ProtFlag.PROT_NONE) != 0)
  43. {
  44. throw new InvalidOperationException("Could not mark the memory as inaccessible because " +
  45. Marshal.GetLastWin32Error());
  46. }
  47. if (Syscall.mprotect((IntPtr)(virtualAlloc + 4096 + (sizeInPages*4096)), 4096, ProtFlag.PROT_NONE) != 0)
  48. {
  49. throw new InvalidOperationException("Could not mark the memory as inaccessible because " +
  50. Marshal.GetLastWin32Error());
  51. }
  52. Interlocked.Add(ref usage, allocatedSize);
  53. var firstWritablePage = virtualAlloc + 4096;
  54. Memory.Set(firstWritablePage, 0xED, 4096 * sizeInPages); // don't assume zero'ed mem
  55. if (remaining == 0)
  56. return firstWritablePage;
  57. // give the memory out so its end would be at the 2nd guard page
  58. return firstWritablePage + (4096 - remaining);
  59. }
  60. public static void Free(byte* p)
  61. {
  62. var remaining = (int)((long)p % 4096);
  63. var firstWritablePage = p - remaining;
  64. for (int i = 0; i < remaining; i++)
  65. {
  66. if (firstWritablePage[i] != 0xED)
  67. throw new InvalidOperationException("Invalid memory usage, you killed Ed!");
  68. }
  69. var address = firstWritablePage - 4096;
  70. if (Syscall.mprotect((IntPtr)address, 4096, ProtFlag.PROT_READ) != 0)
  71. {
  72. throw new InvalidOperationException("Could not mark the memory as readable because " +
  73. Marshal.GetLastWin32Error());
  74. }
  75. var dwSize = *(int*)address;
  76. // var virtualAlloc = (byte*)Syscall.mmap((IntPtr)address, (UIntPtr)dwSize, MmapProts.PROT_NONE,
  77. // MmapFlags.MAP_FIXED | MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS, -1, IntPtr.Zero);
  78. // if (virtualAlloc == null)
  79. // {
  80. // var err = Marshal.GetLastWin32Error();
  81. // throw new InvalidOperationException("Failed to free with " + (IntPtr)address + ". Err=" + err);
  82. // }
  83. // var msync = Syscall.msync((IntPtr)virtualAlloc, (UIntPtr)(uint)dwSize, MsyncFlags.MS_SYNC | MsyncFlags.MS_INVALIDATE);
  84. // if (msync != 0)
  85. // {
  86. // throw new InvalidOperationException("Failed to free call msync " + (IntPtr)address + ". Err=" + Marshal.GetLastWin32Error());
  87. // }
  88. Syscall.munmap((IntPtr)address, (UIntPtr)(uint)dwSize);
  89. Interlocked.Add(ref usage, -dwSize);
  90. }
  91. }
  92. }