FileRecordSequenceHelper.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / WCF / Log / System / IO / Log / FileRecordSequenceHelper.cs / 1305376 / FileRecordSequenceHelper.cs

                            //------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------

namespace System.IO.Log 
{
    using System; 
    using System.Runtime; 
    using System.Threading;
    using System.Collections.Generic; 

    // This class contains all the workarounds for simple file log.
    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 (Fx.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 (Fx.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.
//-----------------------------------------------------------

namespace System.IO.Log 
{
    using System; 
    using System.Runtime; 
    using System.Threading;
    using System.Collections.Generic; 

    // This class contains all the workarounds for simple file log.
    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 (Fx.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 (Fx.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.

                        

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