/// <summary> /// Writes all of the dirty blocks passed onto the disk subsystem. Also computes the checksum for the data. /// </summary> /// <param name="currentEndOfCommitPosition">the last valid byte of the file system where this data will be appended to.</param> /// <param name="stream">the source of the data to dump to the disk</param> /// <param name="length">The number by bytes to write to the file system.</param> /// <param name="waitForWriteToDisk">True to wait for a complete commit to disk before returning from this function.</param> public void Write(long currentEndOfCommitPosition, MemoryPoolStreamCore stream, long length, bool waitForWriteToDisk) { byte[] buffer = m_bufferQueue.Dequeue(); long endPosition = currentEndOfCommitPosition + length; long currentPosition = currentEndOfCommitPosition; while (currentPosition < endPosition) { IntPtr ptr; int streamLength; stream.ReadBlock(currentPosition, out ptr, out streamLength); int subLength = (int)Math.Min(streamLength, endPosition - currentPosition); Footer.ComputeChecksumAndClearFooter(ptr, m_fileStructureBlockSize, subLength); Marshal.Copy(ptr, buffer, 0, subLength); WriteRaw(currentPosition, buffer, subLength); currentPosition += subLength; } m_bufferQueue.Enqueue(buffer); if (waitForWriteToDisk) { FlushFileBuffers(); } else { using (m_isUsingStream.EnterReadLock()) { m_stream.Flush(false); } } }
/// <summary> /// Executes a commit of data. This will flush the data to the disk use the provided header data to properly /// execute this function. /// </summary> /// <param name="header"></param> public void CommitChanges(FileHeaderBlock header) { using (var pageLock = new IoSession(this, m_pageReplacementAlgorithm)) { //Determine how much committed data to write long lengthOfAllData = (header.LastAllocatedBlock + 1) * (long)m_fileStructureBlockSize; long copyLength = lengthOfAllData - m_lengthOfCommittedData; //Write the uncommitted data. m_queue.Write(m_lengthOfCommittedData, m_writeBuffer, copyLength, waitForWriteToDisk: true); byte[] bytes = header.GetBytes(); if (header.HeaderBlockCount == 10) { //Update the new header to position 0, position 1, and one of position 2-9 m_queue.WriteRaw(0, bytes, m_fileStructureBlockSize); m_queue.WriteRaw(m_fileStructureBlockSize, bytes, m_fileStructureBlockSize); m_queue.WriteRaw(m_fileStructureBlockSize * ((header.SnapshotSequenceNumber & 7) + 2), bytes, m_fileStructureBlockSize); } else { for (int x = 0; x < header.HeaderBlockCount; x++) { m_queue.WriteRaw(x * m_fileStructureBlockSize, bytes, m_fileStructureBlockSize); } } m_queue.FlushFileBuffers(); long startPos; //Copy recently committed data to the buffer pool if ((m_lengthOfCommittedData & (m_diskBlockSize - 1)) != 0) //Only if there is a split page. { startPos = m_lengthOfCommittedData & (~(long)(m_diskBlockSize - 1)); //Finish filling up the split page in the buffer. IntPtr ptrDest; if (pageLock.TryGetSubPage(startPos, out ptrDest)) { int length; IntPtr ptrSrc; m_writeBuffer.ReadBlock(m_lengthOfCommittedData, out ptrSrc, out length); Footer.WriteChecksumResultsToFooter(ptrSrc, m_fileStructureBlockSize, length); ptrDest += (m_diskBlockSize - length); Memory.Copy(ptrSrc, ptrDest, length); } startPos += m_diskBlockSize; } else { startPos = m_lengthOfCommittedData; } while (startPos < lengthOfAllData) { //If the address doesn't exist in the current list. Read it from the disk. int poolPageIndex; IntPtr poolAddress; m_pool.AllocatePage(out poolPageIndex, out poolAddress); m_writeBuffer.CopyTo(startPos, poolAddress, m_diskBlockSize); Footer.WriteChecksumResultsToFooter(poolAddress, m_fileStructureBlockSize, m_diskBlockSize); if (!m_pageReplacementAlgorithm.TryAddPage(startPos, poolAddress, poolPageIndex)) { m_pool.ReleasePage(poolPageIndex); } startPos += m_diskBlockSize; } m_lengthOfCommittedData = lengthOfAllData; } ReleaseWriteBufferSpace(); }