private void CleanupMemory(TransactionState txState) { _globalMemory.EnterWriteLock(); try { foreach (var addr in txState.AddressesToUnload) { if (addr.Usages != 0) { continue; } if (!_globalMapping.TryGetValue(addr.StartPage, out var set)) { continue; } if (!set.TryRemove(addr)) { continue; } if (LockMemory && addr.Size > 0) { UnlockMemory32Bits((byte *)addr.Address, addr.Size); } Interlocked.Add(ref _totalMapped, -addr.Size); UnmapViewOfFile((byte *)addr.Address); NativeMemory.UnregisterFileMapping(addr.File, addr.Address, addr.Size); if (set.Count == 0) { _globalMapping.TryRemove(addr.StartPage, out set); } } } finally { _globalMemory.ExitWriteLock(); } }
private void LockMemory32Bits(byte *address, long sizeToLock, TransactionState state) { try { if (Sodium.sodium_mlock(address, (UIntPtr)sizeToLock) != 0) { lock (WorkingSetIncreaseLocker) { if (Sodium.sodium_mlock(address, (UIntPtr)sizeToLock) != 0 && DoNotConsiderMemoryLockFailureAsCatastrophicError == false) { TryHandleFailureToLockMemory(address, sizeToLock); } } } } catch (Exception e) { throw new InvalidOperationException("Failed to lock memory in 32-bit mode", e); } }
private TransactionState GetTransactionState(IPagerLevelTransactionState tx) { TransactionState transactionState; if (tx.SparsePagerTransactionState == null) { transactionState = new TransactionState(); tx.SparsePagerTransactionState = new Dictionary <AbstractPager, TransactionState> { { this, transactionState } }; tx.OnDispose += TxOnOnDispose; return(transactionState); } if (tx.SparsePagerTransactionState.TryGetValue(this, out transactionState) == false) { transactionState = new TransactionState(); tx.SparsePagerTransactionState[this] = transactionState; } return(transactionState); }
private LoadedPage MapPages(TransactionState state, long startPage, long size) { _globalMemory.EnterReadLock(); try { var addresses = _globalMapping.GetOrAdd(startPage, _ => new ConcurrentSet <MappedAddresses>()); foreach (var addr in addresses) { if (addr.Size < size) { continue; } Interlocked.Increment(ref addr.Usages); return(AddMappingToTransaction(state, startPage, size, addr)); } var offset = new WindowsMemoryMapPager.SplitValue { Value = (ulong)startPage * Constants.Storage.PageSize }; if ((long)offset.Value + size > _fileStreamLength) { // this can happen when the file size is not a natural multiple of the allocation granularity // frex: granularity of 64KB, and the file size is 80KB. In this case, a request to map the last // 64 kb will run into a problem, there aren't any. In this case only, we'll map the bytes that are // actually there in the file, and if the code will attempt to access beyond the end of file, we'll get // an access denied error, but this is already handled in higher level of the code, since we aren't just // handing out access to the full range we are mapping immediately. if ((long)offset.Value < _fileStreamLength) { size = _fileStreamLength - (long)offset.Value; } else { ThrowInvalidMappingRequested(startPage, size); } } var result = MapViewOfFileEx(_hFileMappingObject, _mmFileAccessType, offset.High, offset.Low, (UIntPtr)size, null); if (result == null) { var lastWin32Error = Marshal.GetLastWin32Error(); const int ERROR_NOT_ENOUGH_MEMORY = 8; if (lastWin32Error == ERROR_NOT_ENOUGH_MEMORY) { throw new OutOfMemoryException($"Unable to map {size / Constants.Size.Kilobyte:#,#0} kb starting at {startPage} on {FileName}", new Win32Exception(lastWin32Error)); } else { const int INVALID_HANDLE = 6; if (lastWin32Error == INVALID_HANDLE && Disposed) { ThrowAlreadyDisposedException(); } throw new Win32Exception( $"Unable to map {size / Constants.Size.Kilobyte:#,#0} kb starting at {startPage} on {FileName} (lastWin32Error={lastWin32Error})", new Win32Exception(lastWin32Error)); } } if (LockMemory && size > 0) { LockMemory32Bits(result, size, state); } NativeMemory.RegisterFileMapping(_fileInfo.FullName, new IntPtr(result), size, GetAllocatedInBytes); Interlocked.Add(ref _totalMapped, size); var mappedAddresses = new MappedAddresses { Address = (IntPtr)result, File = _fileInfo.FullName, Size = size, StartPage = startPage, Usages = 1 }; addresses.Add(mappedAddresses); return(AddMappingToTransaction(state, startPage, size, mappedAddresses)); } finally { _globalMemory.ExitReadLock(); } }
private byte *ReturnPagePointerOrGrowAllocation(LoadedPage page, long distanceFromStart, TransactionState state, bool canUnmap) { var pageHeader = (PageHeader *)(page.Pointer + (distanceFromStart * PageSize)); if ((pageHeader->Flags & PageFlags.Overflow) != PageFlags.Overflow) { // single page, already loaded, can return immediately. return((byte *)pageHeader); } // overflow, so need to make sure it is in the range we mapped. var numberOfOverflowPages = this.GetNumberOfOverflowPages(pageHeader->OverflowSize); if (numberOfOverflowPages + distanceFromStart < page.NumberOfPages) { // the entire range is already mapped, can return immediately return((byte *)pageHeader); } if (canUnmap) { Debug.Assert(state.AddressesToUnload[state.AddressesToUnload.Count - 1] == new IntPtr(page.Pointer)); state.AddressesToUnload.RemoveAt(state.AddressesToUnload.Count - 1); UnmapViewOfFile(page.Pointer); } var ammountToMapInBytes = NearestSizeToAllocationGranularity((distanceFromStart + numberOfOverflowPages) * PageSize); page = MapPages(state, page.StartPage, ammountToMapInBytes); return(page.Pointer + distanceFromStart * PageSize); }