FileRecordSequenceHelper.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ WCF / WCF / 3.5.30729.1 / untmp / Orcas / SP / ndp / cdf / src / WCF / Log / System / IO / Log / FileRecordSequenceHelper.cs / 1 / FileRecordSequenceHelper.cs

                            //------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------
using System;
using System.Threading; 
using System.Collections.Generic;
 
namespace System.IO.Log 
{
    // This class contains all the workarounds for simple file log. 
    internal class FileRecordSequenceHelper
    {
        SimpleFileLog log;
        SequenceNumber newBaseSeqNum; 
        SequenceNumber lastRestartArea;
 
        // We take a reader lock during append and a writer lock during WriteRestartArea and Truncate. 
        ReaderWriterLock appendLock;
 
        // We need to handle truncate failure during Recover otherwise the user will not be able to open the log.
        // If truncate fails, all operations except 'Read' retry trucate.
        bool truncateFailed;
 
        internal FileRecordSequenceHelper(SimpleFileLog log)
        { 
            this.log = log; 
            this.appendLock = new ReaderWriterLock();
            this.truncateFailed = false; 
            this.lastRestartArea = SequenceNumber.Invalid;
            this.newBaseSeqNum = SequenceNumber.Invalid;

            Recover(); 
        }
 
        internal SequenceNumber BaseSequenceNumber 
        {
            get 
            {
                SequenceNumber first, last;
                this.log.GetLogLimits(out first, out last);
                return first; 
            }
        } 
 
        internal SequenceNumber LastSequenceNumber
        { 
            get
            {
                SequenceNumber first, last;
                log.GetLogLimits(out first, out last); 
                if (last != SequenceNumber.Invalid)
                { 
                    if (last < first) 
                    {
                        // Special condition - log empty. 
                        // When the last sequence number returned by simple file log is less than the first,
                        // the log is empty.
                        // For IO.Log OM, if log is empty then Base lsn == Last lsn.
                        return new SequenceNumber(first.High, 0); 
                    }
                    else 
                    { 
                        // The low part is 1 because the last sequence number should be greater than
                        // the sequence number of the last record in the log. 
                        // Last sequence number is a valid input only to WriteRestartArea.
                        return new SequenceNumber(last.High, 1);
                    }
                } 
                else
                { 
                    return last; 
                }
            } 
        }

        internal SequenceNumber RestartSequenceNumber
        { 
            get { return this.lastRestartArea; }
        } 
 
        // During WriteRestarArea, we truncate all records before the new base sequence number.
        // The new base sequence number is recorded in the restart-area record. 
        // If truncate failed during WriteRestartArea, then the records before the new base seq number
        // will still be present in the log.  During recovery, we will cleanup the log by removing these records.

        // Recovery steps - 
        // Scan the log backwards
        // Stop when the last restart area is found 
        // Truncate the log if needed 

        private void Recover() 
        {
            SequenceNumber first;
            SequenceNumber last;
            this.log.GetLogLimits(out first, out last); 

            // Internal knowledge - if last < first, log is empty 
            if (last < first) 
                return;
 
            SequenceNumber sn = last;
            while (sn != SequenceNumber.Invalid && first <= sn && sn <= last)
            {
                FileLogRecordStream stream = new FileLogRecordStream(log, sn); 
                if (stream.Header.IsRestartArea)
                { 
                    this.lastRestartArea = stream.RecordSequenceNumber; 

                    // if the base sequence number is different from 
                    // the next undo lsn, then we crashed during or
                    // before truncate.  Perform the truncate now.
                    if (first < stream.Header.NextUndoLsn)
                    { 

                        if (stream.Header.NextUndoLsn == SequenceNumber.Invalid) 
                        { 
                            // WriteRestartArea was called with LastSequenceNumber
 
                            if (first != stream.RecordSequenceNumber)
                            {
                                this.newBaseSeqNum = stream.RecordSequenceNumber;
                            } 
                        }
                        else 
                        { 
                            this.newBaseSeqNum = stream.Header.NextUndoLsn;
                        } 

                        // This method is called from the constructor.  So no need to take a write lock.
                        if (this.newBaseSeqNum != SequenceNumber.Invalid)
                        { 
                            try
                            { 
                                log.TruncatePrefix(this.newBaseSeqNum); 
                                this.newBaseSeqNum = SequenceNumber.Invalid;
                            } 
#pragma warning suppress 56500
                            catch (Exception exception)
                            {
                                // Truncate failed again.  We were unable to cleanup the log. 
                                this.truncateFailed = true;
 
                                if (DiagnosticUtility.IsFatal(exception)) throw; 
                            }
                        } 
                    }

                    break;
                } 

                sn = stream.PrevLsn; 
            } 
        }
 
        internal void ValidateSequenceNumber(SequenceNumber sequenceNumber)
        {
            if (sequenceNumber == SequenceNumber.Invalid || sequenceNumber.Low != 0)
            { 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.SequenceNumberInvalid());
            } 
 
            if (sequenceNumber < this.BaseSequenceNumber || sequenceNumber > this.LastSequenceNumber)
            { 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.SequenceNumberNotActive("sequenceNumber"));
            }
        }
 
        internal void AdvanceBaseSequeceNumber(SequenceNumber newBaseSequenceNumber)
        { 
            TruncateIfNecessary(); 

            bool lockHeld = false; 
            try
            {
                try { }
                finally 
                {
                    appendLock.AcquireWriterLock(-1); 
                    lockHeld = true; 
                }
                ValidateSequenceNumber(newBaseSequenceNumber); 
                this.log.TruncatePrefix(newBaseSequenceNumber);
            }
            finally
            { 
                if (lockHeld)
                { 
                    appendLock.ReleaseWriterLock(); 
                }
            } 
        }

        internal SequenceNumber Append( IList> data,
                                        SequenceNumber nextUndoRecord, 
                                        SequenceNumber previousRecord,
                                        bool forceFlush) 
        { 
            TruncateIfNecessary();
 
            bool lockHeld = false;
            try
            {
                try { } 
                finally
                { 
                    appendLock.AcquireReaderLock(-1); 
                    lockHeld = true;
                } 
                using (AppendHelper helper = new AppendHelper(
                                                    data,
                                                    previousRecord,
                                                    nextUndoRecord, 
                                                    false))
                { 
 
                    return log.AppendRecord(helper.Blobs, forceFlush);
                } 
            }
            finally
            {
                if (lockHeld) 
                {
                    appendLock.ReleaseReaderLock(); 
                } 
            }
        } 

        internal SequenceNumber Flush(SequenceNumber sequenceNumber)
        {
            TruncateIfNecessary(); 

            if (sequenceNumber == SequenceNumber.Invalid) 
            { 
                // Re-interpret... SimpleFileLog uses 0 to mean "flush
                // entire log", not SequenceNumber.Invalid. 
                //
                sequenceNumber = new SequenceNumber(0);
            }
            else 
            {
                ValidateSequenceNumber(sequenceNumber); 
            } 

            this.log.Force(sequenceNumber); 

            return sequenceNumber;
        }
 
        internal SequenceNumber WriteRestartAreaInternal(
            IList> data, 
            SequenceNumber newBaseSeqNum) 
        {
 
            if(data == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentNull("data"));
            } 

            TruncateIfNecessary(); 
 
            bool lockHeld = false;
            try 
            {
                try { }
                finally
                { 
                    appendLock.AcquireWriterLock(-1);
                    lockHeld = true; 
                } 

                SequenceNumber sn; 
                AppendHelper helper;
                bool lastSeqNum, firstSeqNum;

                lastSeqNum = newBaseSeqNum == this.LastSequenceNumber; 
                firstSeqNum = newBaseSeqNum == this.BaseSequenceNumber;
 
                if (lastSeqNum) 
                {
                    // We dont know the new base sequence number. 
                    // It will be the sequence number we get from append.
                    helper = new AppendHelper(data,
                                            this.lastRestartArea,
                                            SequenceNumber.Invalid, 
                                            true);
                } 
                else 
                {
                    if(!firstSeqNum) 
                    {
                        ValidateSequenceNumber(newBaseSeqNum);

                        // Validate newBaseSequenceNumber by reading the corresponding record. 
                        int cbData = 1;
                        byte[] record; 
                        int recordSize; 
                        SequenceNumber prev, next;
 
                        // Sequence number validation checks if the Seq Num > BSN and Seq Num < LSN.
                        // Now we validate if Seq Number is a valid number in this sequence.
                        try
                        { 
                            this.log.ReadRecordPrefix(newBaseSeqNum, out record, ref cbData, out recordSize, out prev, out next);
                        } 
                        catch(ArgumentException exception) 
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentOutOfRange(SR.Argument_TailInvalid, exception)); 
                        }
                    }

                    helper = new AppendHelper(data, 
                                            this.lastRestartArea,
                                            newBaseSeqNum, 
                                            true); 
                }
 
                // If there is a failure betweeen append and truncate,
                // then the log will be in an inconsistent state.  To
                // solve this, we write the new base sequence number in
                // the header.  During recovery from failure, we will find 
                // the restart area and truncate the log.
 
                using (helper) 
                {
                    // No need for a reader appendLock, since WriteRestartAreaInternal is under a lock. 
                    sn = this.log.AppendRecord(helper.Blobs, true);
                }

                this.lastRestartArea = sn; 

                if(firstSeqNum) 
                { 
                    return sn;
                } 

                if (lastSeqNum)
                {
                    newBaseSeqNum = sn; 
                }
 
                try 
                {
                    log.TruncatePrefix(newBaseSeqNum); 
                }
#pragma warning suppress 56500
                catch (Exception exception)
                { 
                    this.newBaseSeqNum = newBaseSeqNum;
                    this.truncateFailed = true; 
 
                    if (DiagnosticUtility.IsFatal(exception)) throw;
                } 

                return sn;
            }
            finally 
            {
                if (lockHeld) 
                { 
                    appendLock.ReleaseWriterLock();
                } 
            }
        }

        private void TruncateIfNecessary() 
        {
            // Retry trucate if it failed during Recover or WriteRestartArea. 
            if (this.truncateFailed) 
            {
                bool lockHeld = false; 
                try
                {
                    try { }
                    finally 
                    {
                        appendLock.AcquireWriterLock(-1); 
                        lockHeld = true; 
                    }
                    if (this.truncateFailed) 
                    {
                        this.log.TruncatePrefix(this.newBaseSeqNum);
                        this.truncateFailed = false;
                    } 
                }
                finally 
                { 
                    if (lockHeld)
                    { 
                        appendLock.ReleaseWriterLock();
                    }
                }
            } 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK