/ src / Ryujinx.Memory / WindowsShared / PlaceholderManager.cs
PlaceholderManager.cs
  1  using Ryujinx.Common.Collections;
  2  using Ryujinx.Common.Memory.PartialUnmaps;
  3  using System;
  4  using System.Diagnostics;
  5  using System.Runtime.CompilerServices;
  6  using System.Runtime.Versioning;
  7  using System.Threading;
  8  
  9  namespace Ryujinx.Memory.WindowsShared
 10  {
 11      /// <summary>
 12      /// Windows memory placeholder manager.
 13      /// </summary>
 14      [SupportedOSPlatform("windows")]
 15      class PlaceholderManager
 16      {
 17          private const int InitialOverlapsSize = 10;
 18  
 19          private readonly MappingTree<ulong> _mappings;
 20          private readonly MappingTree<MemoryPermission> _protections;
 21          private readonly IntPtr _partialUnmapStatePtr;
 22          private readonly Thread _partialUnmapTrimThread;
 23  
 24          /// <summary>
 25          /// Creates a new instance of the Windows memory placeholder manager.
 26          /// </summary>
 27          public PlaceholderManager()
 28          {
 29              _mappings = new MappingTree<ulong>();
 30              _protections = new MappingTree<MemoryPermission>();
 31  
 32              _partialUnmapStatePtr = PartialUnmapState.GlobalState;
 33  
 34              _partialUnmapTrimThread = new Thread(TrimThreadLocalMapLoop)
 35              {
 36                  Name = "CPU.PartialUnmapTrimThread",
 37                  IsBackground = true,
 38              };
 39              _partialUnmapTrimThread.Start();
 40          }
 41  
 42          /// <summary>
 43          /// Gets a reference to the partial unmap state struct.
 44          /// </summary>
 45          /// <returns>A reference to the partial unmap state struct</returns>
 46          private unsafe ref PartialUnmapState GetPartialUnmapState()
 47          {
 48              return ref Unsafe.AsRef<PartialUnmapState>((void*)_partialUnmapStatePtr);
 49          }
 50  
 51          /// <summary>
 52          /// Trims inactive threads from the partial unmap state's thread mapping every few seconds.
 53          /// Should be run in a Background thread so that it doesn't stop the program from closing.
 54          /// </summary>
 55          private void TrimThreadLocalMapLoop()
 56          {
 57              while (true)
 58              {
 59                  Thread.Sleep(2000);
 60                  GetPartialUnmapState().TrimThreads();
 61              }
 62          }
 63  
 64          /// <summary>
 65          /// Reserves a range of the address space to be later mapped as shared memory views.
 66          /// </summary>
 67          /// <param name="address">Start address of the region to reserve</param>
 68          /// <param name="size">Size in bytes of the region to reserve</param>
 69          public void ReserveRange(ulong address, ulong size)
 70          {
 71              lock (_mappings)
 72              {
 73                  _mappings.Add(new RangeNode<ulong>(address, address + size, ulong.MaxValue));
 74              }
 75  
 76              lock (_protections)
 77              {
 78                  _protections.Add(new RangeNode<MemoryPermission>(address, address + size, MemoryPermission.None));
 79              }
 80          }
 81  
 82          /// <summary>
 83          /// Unreserves a range of memory that has been previously reserved with <see cref="ReserveRange"/>.
 84          /// </summary>
 85          /// <param name="address">Start address of the region to unreserve</param>
 86          /// <param name="size">Size in bytes of the region to unreserve</param>
 87          /// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unreserving the memory</exception>
 88          public void UnreserveRange(ulong address, ulong size)
 89          {
 90              ulong endAddress = address + size;
 91  
 92              lock (_mappings)
 93              {
 94                  RangeNode<ulong> node = _mappings.GetNodeByKey(address);
 95                  RangeNode<ulong> successorNode;
 96  
 97                  for (; node != null; node = successorNode)
 98                  {
 99                      successorNode = node.Successor;
100  
101                      if (IsMapped(node.Value))
102                      {
103                          if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)node.Start, 2))
104                          {
105                              throw new WindowsApiException("UnmapViewOfFile2");
106                          }
107                      }
108  
109                      _mappings.Remove(node);
110  
111                      if (node.End >= endAddress)
112                      {
113                          break;
114                      }
115                  }
116              }
117  
118              RemoveProtection(address, size);
119          }
120  
121          /// <summary>
122          /// Maps a shared memory view on a previously reserved memory region.
123          /// </summary>
124          /// <param name="sharedMemory">Shared memory that will be the backing storage for the view</param>
125          /// <param name="srcOffset">Offset in the shared memory to map</param>
126          /// <param name="location">Address to map the view into</param>
127          /// <param name="size">Size of the view in bytes</param>
128          /// <param name="owner">Memory block that owns the mapping</param>
129          public void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, MemoryBlock owner)
130          {
131              ref var partialUnmapLock = ref GetPartialUnmapState().PartialUnmapLock;
132              partialUnmapLock.AcquireReaderLock();
133  
134              try
135              {
136                  UnmapViewInternal(sharedMemory, location, size, owner, updateProtection: false);
137                  MapViewInternal(sharedMemory, srcOffset, location, size, updateProtection: true);
138              }
139              finally
140              {
141                  partialUnmapLock.ReleaseReaderLock();
142              }
143          }
144  
145          /// <summary>
146          /// Maps a shared memory view on a previously reserved memory region.
147          /// </summary>
148          /// <param name="sharedMemory">Shared memory that will be the backing storage for the view</param>
149          /// <param name="srcOffset">Offset in the shared memory to map</param>
150          /// <param name="location">Address to map the view into</param>
151          /// <param name="size">Size of the view in bytes</param>
152          /// <param name="updateProtection">Indicates if the memory protections should be updated after the map</param>
153          /// <exception cref="WindowsApiException">Thrown when the Windows API returns an error mapping the memory</exception>
154          private void MapViewInternal(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, bool updateProtection)
155          {
156              SplitForMap((ulong)location, (ulong)size, srcOffset);
157  
158              var ptr = WindowsApi.MapViewOfFile3(
159                  sharedMemory,
160                  WindowsApi.CurrentProcessHandle,
161                  location,
162                  srcOffset,
163                  size,
164                  0x4000,
165                  MemoryProtection.ReadWrite,
166                  IntPtr.Zero,
167                  0);
168  
169              if (ptr == IntPtr.Zero)
170              {
171                  throw new WindowsApiException("MapViewOfFile3");
172              }
173  
174              if (updateProtection)
175              {
176                  UpdateProtection((ulong)location, (ulong)size, MemoryPermission.ReadAndWrite);
177              }
178          }
179  
180          /// <summary>
181          /// Splits a larger placeholder, slicing at the start and end address, for a new memory mapping.
182          /// </summary>
183          /// <param name="address">Address to split</param>
184          /// <param name="size">Size of the new region</param>
185          /// <param name="backingOffset">Offset in the shared memory that will be mapped</param>
186          private void SplitForMap(ulong address, ulong size, ulong backingOffset)
187          {
188              ulong endAddress = address + size;
189  
190              var overlaps = new RangeNode<ulong>[InitialOverlapsSize];
191  
192              lock (_mappings)
193              {
194                  int count = _mappings.GetNodes(address, endAddress, ref overlaps);
195  
196                  Debug.Assert(count == 1);
197                  Debug.Assert(!IsMapped(overlaps[0].Value));
198  
199                  var overlap = overlaps[0];
200  
201                  ulong overlapStart = overlap.Start;
202                  ulong overlapEnd = overlap.End;
203                  ulong overlapValue = overlap.Value;
204  
205                  _mappings.Remove(overlap);
206  
207                  bool overlapStartsBefore = overlapStart < address;
208                  bool overlapEndsAfter = overlapEnd > endAddress;
209  
210                  if (overlapStartsBefore && overlapEndsAfter)
211                  {
212                      CheckFreeResult(WindowsApi.VirtualFree(
213                          (IntPtr)address,
214                          (IntPtr)size,
215                          AllocationType.Release | AllocationType.PreservePlaceholder));
216  
217                      _mappings.Add(new RangeNode<ulong>(overlapStart, address, overlapValue));
218                      _mappings.Add(new RangeNode<ulong>(endAddress, overlapEnd, AddBackingOffset(overlapValue, endAddress - overlapStart)));
219                  }
220                  else if (overlapStartsBefore)
221                  {
222                      ulong overlappedSize = overlapEnd - address;
223  
224                      CheckFreeResult(WindowsApi.VirtualFree(
225                          (IntPtr)address,
226                          (IntPtr)overlappedSize,
227                          AllocationType.Release | AllocationType.PreservePlaceholder));
228  
229                      _mappings.Add(new RangeNode<ulong>(overlapStart, address, overlapValue));
230                  }
231                  else if (overlapEndsAfter)
232                  {
233                      ulong overlappedSize = endAddress - overlapStart;
234  
235                      CheckFreeResult(WindowsApi.VirtualFree(
236                          (IntPtr)overlapStart,
237                          (IntPtr)overlappedSize,
238                          AllocationType.Release | AllocationType.PreservePlaceholder));
239  
240                      _mappings.Add(new RangeNode<ulong>(endAddress, overlapEnd, AddBackingOffset(overlapValue, overlappedSize)));
241                  }
242  
243                  _mappings.Add(new RangeNode<ulong>(address, endAddress, backingOffset));
244              }
245          }
246  
247          /// <summary>
248          /// Unmaps a view that has been previously mapped with <see cref="MapView"/>.
249          /// </summary>
250          /// <remarks>
251          /// For "partial unmaps" (when not the entire mapped range is being unmapped), it might be
252          /// necessary to unmap the whole range and then remap the sub-ranges that should remain mapped.
253          /// </remarks>
254          /// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param>
255          /// <param name="location">Address to unmap</param>
256          /// <param name="size">Size of the region to unmap in bytes</param>
257          /// <param name="owner">Memory block that owns the mapping</param>
258          public void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner)
259          {
260              ref var partialUnmapLock = ref GetPartialUnmapState().PartialUnmapLock;
261              partialUnmapLock.AcquireReaderLock();
262  
263              try
264              {
265                  UnmapViewInternal(sharedMemory, location, size, owner, updateProtection: true);
266              }
267              finally
268              {
269                  partialUnmapLock.ReleaseReaderLock();
270              }
271          }
272  
273          /// <summary>
274          /// Unmaps a view that has been previously mapped with <see cref="MapView"/>.
275          /// </summary>
276          /// <remarks>
277          /// For "partial unmaps" (when not the entire mapped range is being unmapped), it might be
278          /// necessary to unmap the whole range and then remap the sub-ranges that should remain mapped.
279          /// </remarks>
280          /// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param>
281          /// <param name="location">Address to unmap</param>
282          /// <param name="size">Size of the region to unmap in bytes</param>
283          /// <param name="owner">Memory block that owns the mapping</param>
284          /// <param name="updateProtection">Indicates if the memory protections should be updated after the unmap</param>
285          /// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unmapping or remapping the memory</exception>
286          private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner, bool updateProtection)
287          {
288              ulong startAddress = (ulong)location;
289              ulong unmapSize = (ulong)size;
290              ulong endAddress = startAddress + unmapSize;
291  
292              var overlaps = new RangeNode<ulong>[InitialOverlapsSize];
293              int count;
294  
295              lock (_mappings)
296              {
297                  count = _mappings.GetNodes(startAddress, endAddress, ref overlaps);
298              }
299  
300              for (int index = 0; index < count; index++)
301              {
302                  var overlap = overlaps[index];
303  
304                  if (IsMapped(overlap.Value))
305                  {
306                      lock (_mappings)
307                      {
308                          _mappings.Remove(overlap);
309                          _mappings.Add(new RangeNode<ulong>(overlap.Start, overlap.End, ulong.MaxValue));
310                      }
311  
312                      bool overlapStartsBefore = overlap.Start < startAddress;
313                      bool overlapEndsAfter = overlap.End > endAddress;
314  
315                      if (overlapStartsBefore || overlapEndsAfter)
316                      {
317                          // If the overlap extends beyond the region we are unmapping,
318                          // then we need to re-map the regions that are supposed to remain mapped.
319                          // This is necessary because Windows does not support partial view unmaps.
320                          // That is, you can only fully unmap a view that was previously mapped, you can't just unmap a chunck of it.
321  
322                          ref var partialUnmapState = ref GetPartialUnmapState();
323                          ref var partialUnmapLock = ref partialUnmapState.PartialUnmapLock;
324                          partialUnmapLock.UpgradeToWriterLock();
325  
326                          try
327                          {
328                              partialUnmapState.PartialUnmapsCount++;
329  
330                              if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2))
331                              {
332                                  throw new WindowsApiException("UnmapViewOfFile2");
333                              }
334  
335                              if (overlapStartsBefore)
336                              {
337                                  ulong remapSize = startAddress - overlap.Start;
338  
339                                  MapViewInternal(sharedMemory, overlap.Value, (IntPtr)overlap.Start, (IntPtr)remapSize, updateProtection: false);
340                                  RestoreRangeProtection(overlap.Start, remapSize);
341                              }
342  
343                              if (overlapEndsAfter)
344                              {
345                                  ulong overlappedSize = endAddress - overlap.Start;
346                                  ulong remapBackingOffset = overlap.Value + overlappedSize;
347                                  ulong remapAddress = overlap.Start + overlappedSize;
348                                  ulong remapSize = overlap.End - endAddress;
349  
350                                  MapViewInternal(sharedMemory, remapBackingOffset, (IntPtr)remapAddress, (IntPtr)remapSize, updateProtection: false);
351                                  RestoreRangeProtection(remapAddress, remapSize);
352                              }
353                          }
354                          finally
355                          {
356                              partialUnmapLock.DowngradeFromWriterLock();
357                          }
358                      }
359                      else if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2))
360                      {
361                          throw new WindowsApiException("UnmapViewOfFile2");
362                      }
363                  }
364              }
365  
366              CoalesceForUnmap(startAddress, unmapSize, owner);
367  
368              if (updateProtection)
369              {
370                  UpdateProtection(startAddress, unmapSize, MemoryPermission.None);
371              }
372          }
373  
374          /// <summary>
375          /// Coalesces adjacent placeholders after unmap.
376          /// </summary>
377          /// <param name="address">Address of the region that was unmapped</param>
378          /// <param name="size">Size of the region that was unmapped in bytes</param>
379          /// <param name="owner">Memory block that owns the mapping</param>
380          private void CoalesceForUnmap(ulong address, ulong size, MemoryBlock owner)
381          {
382              ulong endAddress = address + size;
383              ulong blockAddress = (ulong)owner.Pointer;
384              ulong blockEnd = blockAddress + owner.Size;
385              int unmappedCount = 0;
386  
387              lock (_mappings)
388              {
389                  RangeNode<ulong> node = _mappings.GetNodeByKey(address);
390  
391                  if (node == null)
392                  {
393                      // Nothing to coalesce if we have no overlaps.
394                      return;
395                  }
396  
397                  RangeNode<ulong> predecessor = node.Predecessor;
398                  RangeNode<ulong> successor = null;
399  
400                  for (; node != null; node = successor)
401                  {
402                      successor = node.Successor;
403                      var overlap = node;
404  
405                      if (!IsMapped(overlap.Value))
406                      {
407                          address = Math.Min(address, overlap.Start);
408                          endAddress = Math.Max(endAddress, overlap.End);
409  
410                          _mappings.Remove(overlap);
411                          unmappedCount++;
412                      }
413  
414                      if (node.End >= endAddress)
415                      {
416                          break;
417                      }
418                  }
419  
420                  if (predecessor != null && !IsMapped(predecessor.Value) && predecessor.Start >= blockAddress)
421                  {
422                      address = Math.Min(address, predecessor.Start);
423  
424                      _mappings.Remove(predecessor);
425                      unmappedCount++;
426                  }
427  
428                  if (successor != null && !IsMapped(successor.Value) && successor.End <= blockEnd)
429                  {
430                      endAddress = Math.Max(endAddress, successor.End);
431  
432                      _mappings.Remove(successor);
433                      unmappedCount++;
434                  }
435  
436                  _mappings.Add(new RangeNode<ulong>(address, endAddress, ulong.MaxValue));
437              }
438  
439              if (unmappedCount > 1)
440              {
441                  size = endAddress - address;
442  
443                  CheckFreeResult(WindowsApi.VirtualFree(
444                      (IntPtr)address,
445                      (IntPtr)size,
446                      AllocationType.Release | AllocationType.CoalescePlaceholders));
447              }
448          }
449  
450          /// <summary>
451          /// Reprotects a region of memory that has been mapped.
452          /// </summary>
453          /// <param name="address">Address of the region to reprotect</param>
454          /// <param name="size">Size of the region to reprotect in bytes</param>
455          /// <param name="permission">New permissions</param>
456          /// <returns>True if the reprotection was successful, false otherwise</returns>
457          public bool ReprotectView(IntPtr address, IntPtr size, MemoryPermission permission)
458          {
459              ref var partialUnmapLock = ref GetPartialUnmapState().PartialUnmapLock;
460              partialUnmapLock.AcquireReaderLock();
461  
462              try
463              {
464                  return ReprotectViewInternal(address, size, permission, false);
465              }
466              finally
467              {
468                  partialUnmapLock.ReleaseReaderLock();
469              }
470          }
471  
472          /// <summary>
473          /// Reprotects a region of memory that has been mapped.
474          /// </summary>
475          /// <param name="address">Address of the region to reprotect</param>
476          /// <param name="size">Size of the region to reprotect in bytes</param>
477          /// <param name="permission">New permissions</param>
478          /// <param name="throwOnError">Throw an exception instead of returning an error if the operation fails</param>
479          /// <returns>True if the reprotection was successful or if <paramref name="throwOnError"/> is true, false otherwise</returns>
480          /// <exception cref="WindowsApiException">If <paramref name="throwOnError"/> is true, it is thrown when the Windows API returns an error reprotecting the memory</exception>
481          private bool ReprotectViewInternal(IntPtr address, IntPtr size, MemoryPermission permission, bool throwOnError)
482          {
483              ulong reprotectAddress = (ulong)address;
484              ulong reprotectSize = (ulong)size;
485              ulong endAddress = reprotectAddress + reprotectSize;
486  
487              bool success = true;
488  
489              lock (_mappings)
490              {
491                  RangeNode<ulong> node = _mappings.GetNodeByKey(reprotectAddress);
492                  RangeNode<ulong> successorNode;
493  
494                  for (; node != null; node = successorNode)
495                  {
496                      successorNode = node.Successor;
497                      var overlap = node;
498  
499                      ulong mappedAddress = overlap.Start;
500                      ulong mappedSize = overlap.End - overlap.Start;
501  
502                      if (mappedAddress < reprotectAddress)
503                      {
504                          ulong delta = reprotectAddress - mappedAddress;
505                          mappedAddress = reprotectAddress;
506                          mappedSize -= delta;
507                      }
508  
509                      ulong mappedEndAddress = mappedAddress + mappedSize;
510  
511                      if (mappedEndAddress > endAddress)
512                      {
513                          ulong delta = mappedEndAddress - endAddress;
514                          mappedSize -= delta;
515                      }
516  
517                      if (!WindowsApi.VirtualProtect((IntPtr)mappedAddress, (IntPtr)mappedSize, WindowsApi.GetProtection(permission), out _))
518                      {
519                          if (throwOnError)
520                          {
521                              throw new WindowsApiException("VirtualProtect");
522                          }
523  
524                          success = false;
525                      }
526  
527                      if (node.End >= endAddress)
528                      {
529                          break;
530                      }
531                  }
532              }
533  
534              UpdateProtection(reprotectAddress, reprotectSize, permission);
535  
536              return success;
537          }
538  
539          /// <summary>
540          /// Checks the result of a VirtualFree operation, throwing if needed.
541          /// </summary>
542          /// <param name="success">Operation result</param>
543          /// <exception cref="WindowsApiException">Thrown if <paramref name="success"/> is false</exception>
544          private static void CheckFreeResult(bool success)
545          {
546              if (!success)
547              {
548                  throw new WindowsApiException("VirtualFree");
549              }
550          }
551  
552          /// <summary>
553          /// Adds an offset to a backing offset. This will do nothing if the backing offset is the special "unmapped" value.
554          /// </summary>
555          /// <param name="backingOffset">Backing offset</param>
556          /// <param name="offset">Offset to be added</param>
557          /// <returns>Added offset or just <paramref name="backingOffset"/> if the region is unmapped</returns>
558          private static ulong AddBackingOffset(ulong backingOffset, ulong offset)
559          {
560              if (backingOffset == ulong.MaxValue)
561              {
562                  return backingOffset;
563              }
564  
565              return backingOffset + offset;
566          }
567  
568          /// <summary>
569          /// Checks if a region is unmapped.
570          /// </summary>
571          /// <param name="backingOffset">Backing offset to check</param>
572          /// <returns>True if the backing offset is the special "unmapped" value, false otherwise</returns>
573          private static bool IsMapped(ulong backingOffset)
574          {
575              return backingOffset != ulong.MaxValue;
576          }
577  
578          /// <summary>
579          /// Adds a protection to the list of protections.
580          /// </summary>
581          /// <param name="address">Address of the protected region</param>
582          /// <param name="size">Size of the protected region in bytes</param>
583          /// <param name="permission">Memory permissions of the region</param>
584          private void UpdateProtection(ulong address, ulong size, MemoryPermission permission)
585          {
586              ulong endAddress = address + size;
587  
588              lock (_protections)
589              {
590                  RangeNode<MemoryPermission> node = _protections.GetNodeByKey(address);
591  
592                  if (node != null &&
593                      node.Start <= address &&
594                      node.End >= endAddress &&
595                      node.Value == permission)
596                  {
597                      return;
598                  }
599  
600                  RangeNode<MemoryPermission> successorNode;
601  
602                  ulong startAddress = address;
603  
604                  for (; node != null; node = successorNode)
605                  {
606                      successorNode = node.Successor;
607                      var protection = node;
608  
609                      ulong protAddress = protection.Start;
610                      ulong protEndAddress = protection.End;
611                      MemoryPermission protPermission = protection.Value;
612  
613                      _protections.Remove(protection);
614  
615                      if (protPermission == permission)
616                      {
617                          if (startAddress > protAddress)
618                          {
619                              startAddress = protAddress;
620                          }
621  
622                          if (endAddress < protEndAddress)
623                          {
624                              endAddress = protEndAddress;
625                          }
626                      }
627                      else
628                      {
629                          if (startAddress > protAddress)
630                          {
631                              _protections.Add(new RangeNode<MemoryPermission>(protAddress, startAddress, protPermission));
632                          }
633  
634                          if (endAddress < protEndAddress)
635                          {
636                              _protections.Add(new RangeNode<MemoryPermission>(endAddress, protEndAddress, protPermission));
637                          }
638                      }
639  
640                      if (node.End >= endAddress)
641                      {
642                          break;
643                      }
644                  }
645  
646                  _protections.Add(new RangeNode<MemoryPermission>(startAddress, endAddress, permission));
647              }
648          }
649  
650          /// <summary>
651          /// Removes protection from the list of protections.
652          /// </summary>
653          /// <param name="address">Address of the protected region</param>
654          /// <param name="size">Size of the protected region in bytes</param>
655          private void RemoveProtection(ulong address, ulong size)
656          {
657              ulong endAddress = address + size;
658  
659              lock (_protections)
660              {
661                  RangeNode<MemoryPermission> node = _protections.GetNodeByKey(address);
662                  RangeNode<MemoryPermission> successorNode;
663  
664                  for (; node != null; node = successorNode)
665                  {
666                      successorNode = node.Successor;
667                      var protection = node;
668  
669                      ulong protAddress = protection.Start;
670                      ulong protEndAddress = protection.End;
671                      MemoryPermission protPermission = protection.Value;
672  
673                      _protections.Remove(protection);
674  
675                      if (address > protAddress)
676                      {
677                          _protections.Add(new RangeNode<MemoryPermission>(protAddress, address, protPermission));
678                      }
679  
680                      if (endAddress < protEndAddress)
681                      {
682                          _protections.Add(new RangeNode<MemoryPermission>(endAddress, protEndAddress, protPermission));
683                      }
684  
685                      if (node.End >= endAddress)
686                      {
687                          break;
688                      }
689                  }
690              }
691          }
692  
693          /// <summary>
694          /// Restores the protection of a given memory region that was remapped, using the protections list.
695          /// </summary>
696          /// <param name="address">Address of the remapped region</param>
697          /// <param name="size">Size of the remapped region in bytes</param>
698          private void RestoreRangeProtection(ulong address, ulong size)
699          {
700              ulong endAddress = address + size;
701              var overlaps = new RangeNode<MemoryPermission>[InitialOverlapsSize];
702              int count;
703  
704              lock (_protections)
705              {
706                  count = _protections.GetNodes(address, endAddress, ref overlaps);
707              }
708  
709              for (int index = 0; index < count; index++)
710              {
711                  var protection = overlaps[index];
712  
713                  // If protection is R/W we don't need to reprotect as views are initially mapped as R/W.
714                  if (protection.Value == MemoryPermission.ReadAndWrite)
715                  {
716                      continue;
717                  }
718  
719                  ulong protAddress = protection.Start;
720                  ulong protEndAddress = protection.End;
721  
722                  if (protAddress < address)
723                  {
724                      protAddress = address;
725                  }
726  
727                  if (protEndAddress > endAddress)
728                  {
729                      protEndAddress = endAddress;
730                  }
731  
732                  ReprotectViewInternal((IntPtr)protAddress, (IntPtr)(protEndAddress - protAddress), protection.Value, true);
733              }
734          }
735      }
736  }