/// <summary> /// Get next record in iterator /// </summary> /// <param name="entry">Copy of entry, if found</param> /// <param name="entryLength">Actual length of entry</param> /// <param name="currentAddress">Logical address of entry</param> /// <param name="nextAddress">Logical address of next entry</param> /// <returns></returns> public unsafe bool GetNext(out byte[] entry, out int entryLength, out long currentAddress, out long nextAddress) { epoch.Resume(); if (GetNextInternal(out long physicalAddress, out entryLength, out currentAddress, out nextAddress)) { if (getMemory != null) { // Use user delegate to allocate memory entry = getMemory(entryLength); if (entry.Length < entryLength) { throw new FasterException("Byte array provided has invalid length"); } } else { // We allocate a byte array from heap entry = new byte[entryLength]; } fixed(byte *bp = entry) Buffer.MemoryCopy((void *)(headerSize + physicalAddress), bp, entryLength, entryLength); epoch.Suspend(); return(true); } entry = default; epoch.Suspend(); return(false); }
private bool WaitForFrameLoad(long currentAddress, long currentFrame) { if (loaded[currentFrame].IsSet) { return(false); } try { epoch?.Suspend(); loaded[currentFrame].Wait(loadedCancel[currentFrame].Token); // Ensure we have completed ongoing load } catch (Exception e) { loadedPage[currentFrame] = -1; loadedCancel[currentFrame] = new CancellationTokenSource(); Utility.MonotonicUpdate(ref nextAddress, (1 + (currentAddress >> logPageSizeBits)) << logPageSizeBits, out _); throw new FasterException("Page read from storage failed, skipping page. Inner exception: " + e.ToString()); } finally { epoch?.Resume(); } return(true); }
/// <summary> /// Protects later processing from concurrent version changes until Leave() is called. Method may block if /// version change is under way. Leave() must eventually be called on the same thread so the rest of the /// system makes meaningful progress. /// </summary> /// <returns> /// current version number. This guarantees that any version change logic from smaller version numbers /// have finished, and not version change logic to larger versions will begin during protection. /// </returns> public long Enter() { epoch.Resume(); // Temporarily block if a version change is under way --- depending on whether the thread observes // versionChanged, they are either in the current version or the next while (true) { var ev = versionChanged; if (ev == null) { break; } // Allow version change to complete by leaving this epoch. epoch.Suspend(); ev.Wait(); epoch.Resume(); } return(Interlocked.Read(ref version)); }
/// <summary> /// Try to enqueue entry to log (in memory). If it returns true, we are /// done. If it returns false, we need to retry. /// </summary> /// <param name="entry">Entry to be enqueued to log</param> /// <param name="logicalAddress">Logical address of added entry</param> /// <returns>Whether the append succeeded</returns> public unsafe bool TryEnqueue(byte[] entry, out long logicalAddress) { logicalAddress = 0; epoch.Resume(); var length = entry.Length; logicalAddress = allocator.TryAllocate(headerSize + Align(length)); if (logicalAddress == 0) { epoch.Suspend(); return(false); } var physicalAddress = allocator.GetPhysicalAddress(logicalAddress); fixed(byte *bp = entry) Buffer.MemoryCopy(bp, (void *)(headerSize + physicalAddress), length, length); SetHeader(length, (byte *)physicalAddress); epoch.Suspend(); return(true); }
/// <summary> /// Get next record /// </summary> /// <param name="recordInfo"></param> /// <returns>True if record found, false if end of scan</returns> public bool GetNext(out RecordInfo recordInfo) { recordInfo = default; while (true) { currentAddress = nextAddress; // Check for boundary conditions if (currentAddress >= endAddress) { return(false); } epoch?.Resume(); var headAddress = hlog.HeadAddress; if (currentAddress < hlog.BeginAddress) { epoch?.Suspend(); throw new FasterException("Iterator address is less than log BeginAddress " + hlog.BeginAddress); } if (frameSize == 0 && currentAddress < headAddress) { epoch?.Suspend(); throw new FasterException("Iterator address is less than log HeadAddress in memory-scan mode"); } var currentPage = currentAddress >> hlog.LogPageSizeBits; var offset = currentAddress & hlog.PageSizeMask; if (currentAddress < headAddress) { BufferAndLoad(currentAddress, currentPage, currentPage % frameSize); } long physicalAddress; if (currentAddress >= headAddress) { physicalAddress = hlog.GetPhysicalAddress(currentAddress); currentPhysicalAddress = 0; } else { currentPhysicalAddress = physicalAddress = frame.GetPhysicalAddress(currentPage % frameSize, offset); } // Check if record fits on page, if not skip to next page var recordSize = hlog.GetRecordSize(physicalAddress).Item2; if ((currentAddress & hlog.PageSizeMask) + recordSize > hlog.PageSize) { nextAddress = (1 + (currentAddress >> hlog.LogPageSizeBits)) << hlog.LogPageSizeBits; epoch?.Suspend(); continue; } nextAddress = currentAddress + recordSize; ref var info = ref hlog.GetInfo(physicalAddress); if (info.Invalid || info.IsNull()) { epoch?.Suspend(); continue; } recordInfo = info; if (currentPhysicalAddress == 0) { currentKey = hlog.GetKey(physicalAddress); currentValue = hlog.GetValue(physicalAddress); } epoch?.Suspend(); return(true); }
/// <summary> /// Retrieve physical address of next iterator value /// (under epoch protection if it is from main page buffer) /// </summary> /// <param name="physicalAddress"></param> /// <param name="entryLength"></param> /// <param name="epochTaken"></param> /// <returns></returns> private unsafe bool GetNextInternal(out long physicalAddress, out int entryLength, out bool epochTaken) { physicalAddress = 0; entryLength = 0; epochTaken = false; currentAddress = nextAddress; while (true) { // Check for boundary conditions if (currentAddress < allocator.BeginAddress) { Debug.WriteLine("Iterator address is less than log BeginAddress " + allocator.BeginAddress + ", adjusting iterator address"); currentAddress = allocator.BeginAddress; } if ((currentAddress >= endAddress) || (currentAddress >= fasterLog.CommittedUntilAddress)) { nextAddress = currentAddress; return(false); } if (frameSize == 0 && currentAddress < allocator.HeadAddress) { throw new Exception("Iterator address is less than log HeadAddress in memory-scan mode"); } var currentPage = currentAddress >> allocator.LogPageSizeBits; var offset = currentAddress & allocator.PageSizeMask; var headAddress = allocator.HeadAddress; if (currentAddress < headAddress) { BufferAndLoad(currentAddress, currentPage, currentPage % frameSize); physicalAddress = frame.GetPhysicalAddress(currentPage % frameSize, offset); } else { epoch.Resume(); headAddress = allocator.HeadAddress; if (currentAddress < headAddress) // rare case { epoch.Suspend(); continue; } physicalAddress = allocator.GetPhysicalAddress(currentAddress); } // Get and check entry length entryLength = fasterLog.GetLength((byte *)physicalAddress); if (entryLength == 0) { if (currentAddress >= headAddress) { epoch.Suspend(); } nextAddress = (1 + (currentAddress >> allocator.LogPageSizeBits)) << allocator.LogPageSizeBits; if (0 != fasterLog.GetChecksum((byte *)physicalAddress)) { var curPage = currentAddress >> allocator.LogPageSizeBits; throw new Exception("Invalid checksum found during scan, skipping page " + curPage); } else { // We are likely at end of page, skip to next currentAddress = nextAddress; continue; } } int recordSize = headerSize + Align(entryLength); if ((currentAddress & allocator.PageSizeMask) + recordSize > allocator.PageSize) { if (currentAddress >= headAddress) { epoch.Suspend(); } nextAddress = (1 + (currentAddress >> allocator.LogPageSizeBits)) << allocator.LogPageSizeBits; throw new Exception("Invalid length of record found: " + entryLength + ", skipping page"); } // Verify checksum if needed if (currentAddress < headAddress) { if (!fasterLog.VerifyChecksum((byte *)physicalAddress, entryLength)) { var curPage = currentAddress >> allocator.LogPageSizeBits; nextAddress = (1 + (currentAddress >> allocator.LogPageSizeBits)) << allocator.LogPageSizeBits; throw new Exception("Invalid checksum found during scan, skipping page " + curPage); } } if ((currentAddress & allocator.PageSizeMask) + recordSize == allocator.PageSize) { nextAddress = (1 + (currentAddress >> allocator.LogPageSizeBits)) << allocator.LogPageSizeBits; } else { nextAddress = currentAddress + recordSize; } epochTaken = currentAddress >= headAddress; return(true); } }
/// <summary> /// Get next record in iterator /// </summary> /// <param name="recordInfo"></param> /// <returns></returns> public bool GetNext(out RecordInfo recordInfo) { recordInfo = default; currentKey = default; currentValue = default; while (true) { currentAddress = nextAddress; // Check for boundary conditions if (currentAddress >= endAddress) { return(false); } epoch?.Resume(); var headAddress = hlog.HeadAddress; if (currentAddress < hlog.BeginAddress) { epoch?.Suspend(); throw new FasterException("Iterator address is less than log BeginAddress " + hlog.BeginAddress); } if (frameSize == 0 && currentAddress < headAddress) { epoch?.Suspend(); throw new FasterException("Iterator address is less than log HeadAddress in memory-scan mode"); } var currentPage = currentAddress >> hlog.LogPageSizeBits; var offset = (currentAddress & hlog.PageSizeMask) / recordSize; if (currentAddress < headAddress) { BufferAndLoad(currentAddress, currentPage, currentPage % frameSize); } // Check if record fits on page, if not skip to next page if ((currentAddress & hlog.PageSizeMask) + recordSize > hlog.PageSize) { nextAddress = (1 + (currentAddress >> hlog.LogPageSizeBits)) << hlog.LogPageSizeBits; epoch?.Suspend(); continue; } nextAddress = currentAddress + recordSize; if (currentAddress >= headAddress) { // Read record from cached page memory var page = currentPage % hlog.BufferSize; if (hlog.values[page][offset].info.Invalid) { epoch?.Suspend(); continue; } recordInfo = hlog.values[page][offset].info; currentKey = hlog.values[page][offset].key; currentValue = hlog.values[page][offset].value; epoch?.Suspend(); return(true); } var currentFrame = currentPage % frameSize; if (frame.GetInfo(currentFrame, offset).Invalid) { epoch?.Suspend(); continue; } recordInfo = frame.GetInfo(currentFrame, offset); currentKey = frame.GetKey(currentFrame, offset); currentValue = frame.GetValue(currentFrame, offset); epoch?.Suspend(); return(true); } }