public override unsafe void ReleaseAllocationInfo(byte *baseAddress, long size) { base.ReleaseAllocationInfo(baseAddress, size); if (_supportsUnmapping == false) { return; } var ptr = new IntPtr(baseAddress); if (DeleteOnClose) { if (Syscall.madvise(ptr, new UIntPtr((ulong)size), MAdvFlags.MADV_DONTNEED) != 0) { if (_log.IsInfoEnabled) { _log.Info($"Failed to madvise MDV_DONTNEED for {FileName?.FullPath}"); } } } var result = Syscall.munmap(ptr, (UIntPtr)size); if (result == -1) { var err = Marshal.GetLastWin32Error(); Syscall.ThrowLastError(err, "munmap " + FileName); } NativeMemory.UnregisterFileMapping(FileName.FullPath, ptr, size); }
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 = startPage * Constants.Storage.PageSize; if (offset + size > _totalAllocationSize) { ThrowInvalidMappingRequested(startPage, size); } var mmflags = _copyOnWriteMode ? MmapFlags.MAP_PRIVATE : MmapFlags.MAP_SHARED; var startingBaseAddressPtr = Syscall.mmap64(IntPtr.Zero, (UIntPtr)size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, mmflags, _fd, offset); if (startingBaseAddressPtr.ToInt64() == -1) //system didn't succeed in mapping the address where we wanted { var err = Marshal.GetLastWin32Error(); Syscall.ThrowLastError(err, $"Unable to map {size / Constants.Size.Kilobyte:#,#0} kb starting at {startPage} on {FileName}"); } NativeMemory.RegisterFileMapping(FileName.FullPath, startingBaseAddressPtr, size, GetAllocatedInBytes); Interlocked.Add(ref _totalMapped, size); var mappedAddresses = new MappedAddresses { Address = startingBaseAddressPtr, File = FileName.FullPath, Size = size, StartPage = startPage, Usages = 1 }; addresses.Add(mappedAddresses); return(AddMappingToTransaction(state, startPage, size, mappedAddresses)); } finally { _globalMemory.ExitReadLock(); } }
public override void ReleaseAllocationInfo(byte *baseAddress, long size) { base.ReleaseAllocationInfo(baseAddress, size); if (Win32MemoryMapNativeMethods.UnmapViewOfFile(baseAddress) == false) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to UnMapView of file " + FileName); } NativeMemory.UnregisterFileMapping(_fileInfo.FullName, new IntPtr(baseAddress), size); }
private PagerState CreatePagerState() { var mmf = MemoryMappedFile.CreateFromFile(_fileStream, null, _fileStream.Length, _memoryMappedFileAccess, HandleInheritability.None, true); var fileMappingHandle = mmf.SafeMemoryMappedFileHandle.DangerousGetHandle(); Win32MemoryMapNativeMethods.NativeFileMapAccessType mmFileAccessType; if (_copyOnWriteMode) { mmFileAccessType = Win32MemoryMapNativeMethods.NativeFileMapAccessType.Copy; } else { mmFileAccessType = _access == Win32NativeFileAccess.GenericRead ? Win32MemoryMapNativeMethods.NativeFileMapAccessType.Read : Win32MemoryMapNativeMethods.NativeFileMapAccessType.Read | Win32MemoryMapNativeMethods.NativeFileMapAccessType.Write; } var startingBaseAddressPtr = Win32MemoryMapNativeMethods.MapViewOfFileEx(fileMappingHandle, mmFileAccessType, 0, 0, UIntPtr.Zero, //map all what was "reserved" in CreateFileMapping on previous row null); if (startingBaseAddressPtr == (byte *)0) //system didn't succeed in mapping the address where we wanted { var innerException = new Win32Exception(Marshal.GetLastWin32Error(), "Failed to MapView of file " + FileName); var errorMessage = $"Unable to allocate more pages - unsuccessfully tried to allocate continuous block of virtual memory with size = {new Size(_fileStream.Length, SizeUnit.Bytes)}"; throw new OutOfMemoryException(errorMessage, innerException); } NativeMemory.RegisterFileMapping(_fileInfo.FullName, new IntPtr(startingBaseAddressPtr), _fileStream.Length, GetAllocatedInBytes); // If we are working on memory validation mode, then protect the pages by default. ProtectPageRange(startingBaseAddressPtr, (ulong)_fileStream.Length); var allocationInfo = new PagerState.AllocationInfo { BaseAddress = startingBaseAddressPtr, Size = _fileStream.Length, MappedFile = mmf }; var newPager = new PagerState(this, Options.PrefetchSegmentSize, Options.PrefetchResetThreshold, allocationInfo); return(newPager); }
private static unsafe UnmanagedStringArray.UnmanagedString GetNullValueUnmanagedString() { var size = sizeof(short) + Encoding.UTF8.GetByteCount(Constants.Documents.Indexing.Fields.NullValue); byte *bytes = NativeMemory.AllocateMemory(size); // single allocation, we never free it fixed(char *chars = Constants.Documents.Indexing.Fields.NullValue) { *(short *)bytes = (short)Encoding.UTF8.GetBytes(chars, Constants.Documents.Indexing.Fields.NullValue.Length, bytes + sizeof(short), size - sizeof(short)); } return(new UnmanagedStringArray.UnmanagedString { Start = bytes }); }
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 Run() { NativeMemory.EnsureRegistered(); int tries = 0; while (true) { (WaitCallback callback, object state)result; while (_actions.TryDequeue(out result)) { try { result.callback(result.state); } catch { // there is nothing that we _can_ do here that would be right // and there is no meaningful error handling. Ignoring this because // callers are expected to do their own exception catching } } // PERF: Entering a kernel lock even if the ManualResetEventSlim will try to avoid that doing some spin locking // is very costly. This is a hack that is allowing amortize a bit very high frequency events. The proper // way to handle requires infrastructure changes. http://issues.hibernatingrhinos.com/issue/RavenDB-8126 if (tries < 5) { // Yield execution quantum. If we are in a high-frequency event we will be able to avoid the kernel lock. Thread.Sleep(0); tries++; } else { _event.WaitHandle.WaitOne(); _event.Reset(); // Nothing we can do here, just block. tries = 0; } } }
private PagerState CreatePagerState() { var startingBaseAddressPtr = Syscall.mmap64(IntPtr.Zero, (UIntPtr)_totalAllocationSize, MmapProts.PROT_READ | MmapProts.PROT_WRITE, MmapFlags.MAP_SHARED, _fd, 0L); if (startingBaseAddressPtr.ToInt64() == -1) //system didn't succeed in mapping the address where we wanted { var err = Marshal.GetLastWin32Error(); Syscall.ThrowLastError(err, "mmap on " + FileName); } NativeMemory.RegisterFileMapping(FileName.FullPath, startingBaseAddressPtr, _totalAllocationSize, GetAllocatedInBytes); var allocationInfo = new PagerState.AllocationInfo { BaseAddress = (byte *)startingBaseAddressPtr.ToPointer(), Size = _totalAllocationSize, MappedFile = null }; return(new PagerState(this, Options.PrefetchSegmentSize, Options.PrefetchResetThreshold, allocationInfo)); }
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 void BackgroundLogger() { NativeMemory.EnsureRegistered(); try { Interlocked.Increment(ref _generation); var threadStatesToRemove = new FastStack <WeakReference <LocalThreadWriterState> >(); while (_keepLogging) { try { var maxFileSize = MaxFileSizeInBytes; if (TryGetNewStreamAndApplyRetentionPolicies(maxFileSize, out var currentFile) == false) { if (_keepLogging == false) { return; } _hasEntries.Wait(1000); continue; } using (currentFile) { _readyToCompress.Set(); var sizeWritten = 0; var foundEntry = true; while (sizeWritten < maxFileSize) { if (foundEntry == false) { if (_keepLogging == false) { return; } // we don't want to have fsync here, we just // want to send it to the OS currentFile.Flush(flushToDisk: false); if (_hasEntries.IsSet == false) { // about to go to sleep, so can check if need to update offset or create new file for today logs UpdateLocalDateTimeOffset(); if (DateTime.Today != _today) { // let's create new file so its name will have today date break; } } _hasEntries.Wait(); if (_keepLogging == false) { return; } _hasEntries.Reset(); } foundEntry = false; for (var index = 0; index < _activePoolMessageEntries.Length; index++) { var messages = _activePoolMessageEntries[index]; for (var limit = 0; limit < 16; limit++) { if (messages.TryDequeue(out LogMessageEntry item) == false) { break; } foundEntry = true; sizeWritten += ActualWriteToLogTargets(item, currentFile); Debug.Assert(item.Data != null); _freePooledMessageEntries[index].Enqueue(item, 128); } } } } } catch (OutOfMemoryException) { Console.Error.WriteLine("Out of memory exception while trying to log, will avoid logging for the next 5 seconds"); DisableLogsFor(TimeSpan.FromSeconds(5)); } catch (Exception e) { var msg = e is IOException i && IsOutOfDiskSpaceException(i) ? "Couldn't create a new log file because of out of disk space! " + "Disabling the logs for 30 seconds" : "FATAL ERROR trying to log!"; Console.Error.WriteLine($"{msg}{Environment.NewLine}{e}"); DisableLogsFor(TimeSpan.FromSeconds(30)); } } } finally { _readyToCompress.Set(); if (_compressLoggingThread?.Join(1000) == false) { _tokenSource.Cancel(); } } }