Exemple #1
0
        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();
            }
        }
Exemple #3
0
 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);
 }
Exemple #4
0
        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();
            }
        }
Exemple #7
0
            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;
                    }
                }
            }
Exemple #8
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();
            }
        }
Exemple #10
0
        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();
                }
            }
        }