File: System\IO\Log\LogRecordSequence.cs
Project: ndp\cdf\src\NetFx35\System.IO.Log\System.IO.Log.csproj (System.IO.Log)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
namespace System.IO.Log
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Security.AccessControl;
    using System.Security.Permissions;
    using System.Threading;
 
 
    public sealed class LogRecordSequence : IRecordSequence
    {
        const uint INFINITE = 0xFFFFFFFF;
 
        const int DefaultWriteBufferCount = 10;
        const int DefaultReadBufferCount = unchecked((int)INFINITE);
        const int DefaultBufferSize = 64 * 1024;
 
        // This is just a common-sense buffer size, to prevent
        // accidental integer overflow and such.
        //
        const int MaxBufferSize = 0x40000000;
 
        const int CLFS_SECTOR_SIZE = 0x00000200;
        const int CLFS_SECTOR_ALIGN = (CLFS_SECTOR_SIZE - 1);
 
        LogStore store;
        int bufferSize;
        int writeBufferCount;
        bool ownStore;
        bool retryAppend;
 
        SafeMarshalContext marshalContext;
 
        // The LogStore constructor will demand full trust, so we don't have to.
        public LogRecordSequence(
            string path,
            FileMode mode)
        {
            this.store = new LogStore(path, mode);
            this.ownStore = true;
 
            this.bufferSize = GetBufferSize(DefaultBufferSize);
            this.writeBufferCount = DefaultWriteBufferCount;
        }
 
        // The LogStore constructor will demand full trust, so we don't have to.
        public LogRecordSequence(
            string path,
            FileMode mode,
            FileAccess access)
        {
            this.store = new LogStore(path, mode, access);
            this.ownStore = true;
 
            this.bufferSize = GetBufferSize(DefaultBufferSize);
            this.writeBufferCount = DefaultWriteBufferCount;
        }
 
        // The LogStore constructor will demand full trust, so we don't have to.
        public LogRecordSequence(
            string path,
            FileMode mode,
            FileAccess access,
            FileShare share)
        {
            this.store = new LogStore(path, mode, access, share);
            this.ownStore = true;
 
            this.bufferSize = GetBufferSize(DefaultBufferSize);
            this.writeBufferCount = DefaultWriteBufferCount;
        }
 
        // The LogStore constructor will demand full trust, so we don't have to.
        public LogRecordSequence(
            string path,
            FileMode mode,
            FileAccess access,
            FileShare share,
            int bufferSize,
            int bufferCount)
        {
            if (bufferSize <= 0)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentOutOfRange("bufferSize"));
            if (bufferCount <= 0)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentOutOfRange("bufferCount"));
 
            this.store = new LogStore(path, mode, access, share);
            this.ownStore = true;
 
            this.bufferSize = GetBufferSize(bufferSize);
            this.writeBufferCount = bufferCount;
        }
 
        // The LogStore constructor will demand full trust, so we don't have to.
        public LogRecordSequence(
            string path,
            FileMode mode,
            FileAccess access,
            FileShare share,
            int bufferSize,
            int bufferCount,
            FileSecurity fileSecurity)
        {
            if (bufferSize <= 0)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentOutOfRange("bufferSize"));
            if (bufferCount <= 0)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentOutOfRange("bufferCount"));
 
            this.store = new LogStore(path,
                                      mode,
                                      access,
                                      share,
                                      fileSecurity);
            this.ownStore = true;
 
            this.bufferSize = GetBufferSize(bufferSize);
            this.writeBufferCount = bufferCount;
        }
 
        [PermissionSetAttribute(SecurityAction.Demand, Unrestricted = true)]
        public LogRecordSequence(LogStore logStore)
        {
            if (logStore == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentNull("logStore"));
 
            this.store = logStore;
            this.ownStore = false;
 
            this.bufferSize = GetBufferSize(DefaultBufferSize);
            this.writeBufferCount = DefaultWriteBufferCount;
        }
 
        [PermissionSetAttribute(SecurityAction.Demand, Unrestricted = true)]
        public LogRecordSequence(
            LogStore logStore,
            int bufferSize,
            int bufferCount)
        {
            if (logStore == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentNull("logStore"));
 
            if (bufferSize <= 0)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentOutOfRange("bufferSize"));
            if (bufferCount <= 0)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentOutOfRange("bufferCount"));
 
            this.store = logStore;
            this.ownStore = false;
 
            this.bufferSize = GetBufferSize(bufferSize);
            this.writeBufferCount = bufferCount;
        }
 
        public SequenceNumber BaseSequenceNumber
        {
            get
            {
                return this.store.BaseSequenceNumber;
            }
        }
 
        public SequenceNumber LastSequenceNumber
        {
            get
            {
                return this.store.LastSequenceNumber;
            }
        }
 
        public LogStore LogStore
        {
            get
            {
                return this.store;
            }
        }
 
        internal SafeMarshalContext MarshalContext
        {
            get
            {
                EnsureMarshalContext();
                return this.marshalContext;
            }
        }
 
        internal SafeMarshalContext InternalMarshalContext
        {
            get
            {
                return this.marshalContext;
            }
        }
 
        public long MaximumRecordLength
        {
            get
            {
                return this.bufferSize - LogLogRecordHeader.Size;
            }
        }
 
        public long ReservedBytes
        {
            get
            {
                CLFS_INFORMATION info;
                this.store.GetLogFileInformation(out info);
 
                return checked((long)(info.TotalUndoCommitment));
            }
        }
 
        public bool RetryAppend
        {
            get
            {
                return this.retryAppend;
            }
 
            set
            {
                this.retryAppend = value;
            }
        }
 
        public SequenceNumber RestartSequenceNumber
        {
            get
            {
                CLFS_INFORMATION info;
                this.store.GetLogFileInformation(out info);
 
                if (info.RestartLsn == Const.CLFS_LSN_INVALID)
                    return SequenceNumber.Invalid;
 
                return new SequenceNumber(info.RestartLsn);
            }
        }
 
        public event EventHandler<TailPinnedEventArgs> TailPinned
        {
            add
            {
                this.store.LogManagement.TailPinned += value;
            }
 
            remove
            {
                this.store.LogManagement.TailPinned -= value;
            }
        }
 
        public void AdvanceBaseSequenceNumber(
            SequenceNumber newBaseSequenceNumber)
        {
            this.store.ValidateSequenceNumber(
                ref newBaseSequenceNumber,
                SequenceNumberConstraint.Arbitrary,
                "newBaseSequenceNumber");
 
            ulong newBaseLsn = newBaseSequenceNumber.High;
 
            EnsureMarshalContext();
 
            UnsafeNativeMethods.AdvanceLogBaseSync(this.marshalContext,
                                                   ref newBaseLsn,
                                                   0);
        }
 
        public SequenceNumber Append(
            ArraySegment<byte> data,
            SequenceNumber nextUndoRecord,
            SequenceNumber previousRecord,
            RecordAppendOptions recordAppendOptions)
        {
            return Append(new ArraySegment<byte>[] { data },
                          nextUndoRecord,
                          previousRecord,
                          recordAppendOptions);
        }
 
 
        public SequenceNumber Append(
            ArraySegment<byte> data,
            SequenceNumber nextUndoRecord,
            SequenceNumber previousRecord,
            RecordAppendOptions recordAppendOptions,
            ReservationCollection reservations)
        {
            return Append(new ArraySegment<byte>[] { data },
                          nextUndoRecord,
                          previousRecord,
                          recordAppendOptions,
                          reservations);
        }
 
        public SequenceNumber Append(
            IList<ArraySegment<byte>> data,
            SequenceNumber userRecord,
            SequenceNumber previousRecord,
            RecordAppendOptions recordAppendOptions)
        {
            return Append(
                data,
                userRecord,
                previousRecord,
                recordAppendOptions,
                null);
        }
 
        public SequenceNumber Append(
            IList<ArraySegment<byte>> data,
            SequenceNumber userRecord,
            SequenceNumber previousRecord,
            RecordAppendOptions recordAppendOptions,
            ReservationCollection reservations)
        {
            long totalRecordSize;
            LogReservationCollection lrc;
 
            if ((this.store.Access & FileAccess.Write) == 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.NotSupported(SR.NotSupported_ReadOnly));
            }
 
            if (recordAppendOptions > (RecordAppendOptions.ForceAppend | RecordAppendOptions.ForceFlush) ||
               recordAppendOptions < RecordAppendOptions.None)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentOutOfRange("recordAppendOptions"));
            }
 
            this.store.ValidateSequenceNumber(
                ref userRecord,
                SequenceNumberConstraint.Arbitrary,
                "userRecord");
            this.store.ValidateSequenceNumber(
                ref previousRecord,
                SequenceNumberConstraint.Arbitrary,
                "previousRecord");
 
            totalRecordSize = ValidateData(data);
            lrc = ValidateReservationCollection(reservations);
 
            EnsureMarshalContext();
 
            LogReserveAndAppendState state = new LogReserveAndAppendState();
 
            state.RecordSequence = this;
            state.Data = data;
            state.TotalRecordSize = totalRecordSize;
            state.UserLsn = userRecord.High;
            state.PreviousLsn = previousRecord.High;
            state.RecordAppendOptions = recordAppendOptions;
            state.ReservationCollection = lrc;
 
            state.Start();
 
            return new SequenceNumber(state.ResultLsn);
        }
 
        public IAsyncResult BeginAppend(
            ArraySegment<byte> data,
            SequenceNumber nextUndoRecord,
            SequenceNumber previousRecord,
            RecordAppendOptions recordAppendOptions,
            AsyncCallback callback,
            object state)
        {
            return BeginAppend(new ArraySegment<byte>[] { data },
                               nextUndoRecord,
                               previousRecord,
                               recordAppendOptions,
                               callback,
                               state);
        }
 
        public IAsyncResult BeginAppend(
            ArraySegment<byte> data,
            SequenceNumber nextUndoRecord,
            SequenceNumber previousRecord,
            RecordAppendOptions recordAppendOptions,
            ReservationCollection reservations,
            AsyncCallback callback,
            object state)
        {
            return BeginAppend(new ArraySegment<byte>[] { data },
                               nextUndoRecord,
                               previousRecord,
                               recordAppendOptions,
                               reservations,
                               callback,
                               state);
        }
 
        public IAsyncResult BeginAppend(
            IList<ArraySegment<byte>> data,
            SequenceNumber userRecord,
            SequenceNumber previousRecord,
            RecordAppendOptions recordAppendOptions,
            AsyncCallback callback,
            object state)
        {
            return BeginAppend(
                data,
                userRecord,
                previousRecord,
                recordAppendOptions,
                null,
                callback,
                state);
        }
 
        public IAsyncResult BeginAppend(
            IList<ArraySegment<byte>> data,
            SequenceNumber userRecord,
            SequenceNumber previousRecord,
            RecordAppendOptions recordAppendOptions,
            ReservationCollection reservations,
            AsyncCallback callback,
            object state)
        {
            long totalRecordSize;
            LogReservationCollection lrc;
 
            if ((this.store.Access & FileAccess.Write) == 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.NotSupported(SR.NotSupported_ReadOnly));
            }
 
            if (recordAppendOptions > (RecordAppendOptions.ForceAppend | RecordAppendOptions.ForceFlush) ||
               recordAppendOptions < RecordAppendOptions.None)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentOutOfRange("recordAppendOptions"));
            }
 
            this.store.ValidateSequenceNumber(
                ref userRecord,
                SequenceNumberConstraint.Arbitrary,
                "userRecord");
            this.store.ValidateSequenceNumber(
                ref previousRecord,
                SequenceNumberConstraint.Arbitrary,
                "previousRecord");
 
            totalRecordSize = ValidateData(data);
            lrc = ValidateReservationCollection(reservations);
 
            EnsureMarshalContext();
 
            LogAppendAsyncResult result = new LogAppendAsyncResult(this,
                                                                   callback,
                                                                   state);
            result.Data = data;
            result.TotalRecordSize = totalRecordSize;
            result.UserLsn = userRecord.High;
            result.PreviousLsn = previousRecord.High;
            result.RecordAppendOptions = recordAppendOptions;
            result.ReservationCollection = lrc;
 
            result.Start();
 
            return result;
        }
 
        public IAsyncResult BeginFlush(
            SequenceNumber sequenceNumber,
            AsyncCallback callback,
            object state)
        {
            this.store.ValidateSequenceNumber(
                ref sequenceNumber,
                SequenceNumberConstraint.Arbitrary,
                "sequenceNumber");
 
            EnsureMarshalContext();
 
            LogFlushAsyncResult flushResult = new LogFlushAsyncResult(this,
                                                                      callback,
                                                                      state);
            if (sequenceNumber == SequenceNumber.Invalid)
            {
                flushResult.SequenceNumber = 0;
            }
            else
            {
                flushResult.SequenceNumber = sequenceNumber.High;
            }
 
            flushResult.Start();
            return flushResult;
        }
 
        public IAsyncResult BeginReserveAndAppend(
            ArraySegment<byte> data,
            SequenceNumber nextUndoRecord,
            SequenceNumber previousRecord,
            RecordAppendOptions recordAppendOptions,
            ReservationCollection reservationCollection,
            long[] reservations,
            AsyncCallback callback,
            object state)
        {
            return BeginReserveAndAppend(new ArraySegment<byte>[] { data },
                                         nextUndoRecord,
                                         previousRecord,
                                         recordAppendOptions,
                                         reservationCollection,
                                         reservations,
                                         callback,
                                         state);
        }
 
        public IAsyncResult BeginReserveAndAppend(
            IList<ArraySegment<byte>> data,
            SequenceNumber userRecord,
            SequenceNumber previousRecord,
            RecordAppendOptions recordAppendOptions,
            ReservationCollection reservationCollection,
            long[] reservations,
            AsyncCallback callback,
            object state)
        {
            long totalRecordSize;
            LogReservationCollection lrc;
 
            this.store.ValidateSequenceNumber(
                ref userRecord,
                SequenceNumberConstraint.Arbitrary,
                "userRecord");
            this.store.ValidateSequenceNumber(
                ref previousRecord,
                SequenceNumberConstraint.Arbitrary,
                "previousRecord");
 
            if (reservationCollection == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentNull("reservationCollection"));
            }
            if (reservations == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentNull("reservations"));
            }
            ValidateReservations(reservations);
 
            totalRecordSize = ValidateData(data);
            lrc = ValidateReservationCollection(reservationCollection);
 
            EnsureMarshalContext();
 
            LogAppendAsyncResult result = new LogAppendAsyncResult(this,
                                                                   callback,
                                                                   state);
            result.Data = data;
            result.TotalRecordSize = totalRecordSize;
            result.UserLsn = userRecord.High;
            result.PreviousLsn = previousRecord.High;
            result.RecordAppendOptions = recordAppendOptions;
            result.ReservationCollection = lrc;
            result.Reservations = reservations;
 
            result.Start();
 
            return result;
        }
 
        public IAsyncResult BeginWriteRestartArea(
            ArraySegment<byte> data,
            SequenceNumber newBaseSeqNum,
            ReservationCollection reservation,
            AsyncCallback callback,
            object state)
        {
            return BeginWriteRestartArea(new ArraySegment<byte>[] { data },
                                         newBaseSeqNum,
                                         reservation,
                                         callback,
                                         state);
        }
 
        public IAsyncResult BeginWriteRestartArea(
            IList<ArraySegment<byte>> data,
            SequenceNumber newBaseSeqNum,
            ReservationCollection reservationCollection,
            AsyncCallback callback,
            object state)
        {
            long totalRecordSize;
            LogReservationCollection lrc;
 
            this.store.ValidateSequenceNumber(
                ref newBaseSeqNum,
                SequenceNumberConstraint.Arbitrary,
                "newBaseSeqNum");
 
            totalRecordSize = ValidateData(data);
            lrc = ValidateReservationCollection(reservationCollection);
 
            EnsureMarshalContext();
 
            LogWriteRestartAreaAsyncResult result;
            result = new LogWriteRestartAreaAsyncResult(this,
                                                        callback,
                                                        state);
            result.Data = data;
            result.TotalRecordSize = totalRecordSize;
            result.NewBaseLsn = newBaseSeqNum.High;
            result.ReservationCollection = lrc;
 
            result.Start();
 
            return result;
        }
 
        public ReservationCollection CreateReservationCollection()
        {
            return new LogReservationCollection(this);
        }
 
        public void Dispose()
        {
            if ((this.marshalContext != null) &&
                (!this.marshalContext.IsInvalid))
            {
                this.marshalContext.Close();
            }
 
            if (this.ownStore)
                this.store.Dispose();
        }
 
        public SequenceNumber EndAppend(
            IAsyncResult result)
        {
            LogAppendAsyncResult lar = result as LogAppendAsyncResult;
            if (lar == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.InvalidAsyncResult());
 
            // Having some reservations to make is a requirement of
            // calling BeginReserveAndAppend, so if we have some, we
            // know this is not our AsyncResult.
            //
            if (lar.Reservations != null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.InvalidAsyncResult());
 
            lar.End();
 
            return new SequenceNumber(lar.ResultLsn);
        }
 
        public SequenceNumber EndFlush(
            IAsyncResult result)
        {
            LogFlushAsyncResult lar = result as LogFlushAsyncResult;
            if (lar == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.InvalidAsyncResult());
 
            lar.End();
 
            return new SequenceNumber(lar.ResultLsn);
        }
 
        public SequenceNumber EndReserveAndAppend(
            IAsyncResult result)
        {
            LogAppendAsyncResult lar = result as LogAppendAsyncResult;
            if (lar == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.InvalidAsyncResult());
 
            // Having some reservations to make is a requirement of
            // calling BeginReserveAndAppend, so if we have none, we
            // know this is an AsyncResult for some OTHER method.
            //
            if (lar.Reservations == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.InvalidAsyncResult());
 
            lar.End();
 
            return new SequenceNumber(lar.ResultLsn);
        }
 
        public SequenceNumber EndWriteRestartArea(
            IAsyncResult result)
        {
            LogWriteRestartAreaAsyncResult lar = result as LogWriteRestartAreaAsyncResult;
            if (lar == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.InvalidAsyncResult());
 
            lar.End();
 
            return new SequenceNumber(lar.ResultLsn);
        }
 
        public SequenceNumber Flush()
        {
            return Flush(SequenceNumber.Invalid);
        }
 
        public SequenceNumber Flush(SequenceNumber sequenceNumber)
        {
            this.store.ValidateSequenceNumber(
                ref sequenceNumber,
                SequenceNumberConstraint.Arbitrary,
                "sequenceNumber");
 
            ulong resultLsn;
            ulong lsn;
 
            if (sequenceNumber == SequenceNumber.Invalid)
            {
                lsn = 0;
            }
            else
            {
                lsn = sequenceNumber.High;
            }
 
            EnsureMarshalContext();
            UnsafeNativeMethods.FlushLogToLsnSync(this.marshalContext,
                                                  ref lsn,
                                                  out resultLsn);
 
            return new SequenceNumber(resultLsn);
        }
 
        public IEnumerable<LogRecord> ReadLogRecords(
            SequenceNumber start,
            LogRecordEnumeratorType logRecordEnum)
        {
            this.store.ValidateSequenceNumber(
                ref start,
                SequenceNumberConstraint.Arbitrary,
                "start");
 
            CLFS_CONTEXT_MODE mode;
            switch (logRecordEnum)
            {
                case LogRecordEnumeratorType.User:
                    mode = CLFS_CONTEXT_MODE.ClfsContextUndoNext;
                    break;
 
                case LogRecordEnumeratorType.Previous:
                    mode = CLFS_CONTEXT_MODE.ClfsContextPrevious;
                    break;
 
                case LogRecordEnumeratorType.Next:
                    mode = CLFS_CONTEXT_MODE.ClfsContextForward;
                    break;
 
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentOutOfRange("logRecordEnum"));
            }
 
            return new RecordEnumerable(this, mode, start.High);
        }
 
        public IEnumerable<LogRecord> ReadRestartAreas()
        {
            return new RestartAreaEnumerable(this);
        }
 
        public SequenceNumber ReserveAndAppend(
            ArraySegment<byte> data,
            SequenceNumber nextUndoRecord,
            SequenceNumber previousRecord,
            RecordAppendOptions recordAppendOptions,
            ReservationCollection reservationCollection,
            params long[] reservations)
        {
            return ReserveAndAppend(new ArraySegment<byte>[] { data },
                                    nextUndoRecord,
                                    previousRecord,
                                    recordAppendOptions,
                                    reservationCollection,
                                    reservations);
        }
 
        public SequenceNumber ReserveAndAppend(
            IList<ArraySegment<byte>> data,
            SequenceNumber userRecord,
            SequenceNumber previousRecord,
            RecordAppendOptions recordAppendOptions,
            ReservationCollection reservationCollection,
            params long[] reservations)
        {
            long totalRecordSize;
            LogReservationCollection lrc;
 
            this.store.ValidateSequenceNumber(
                ref userRecord,
                SequenceNumberConstraint.Arbitrary,
                "userRecord");
            this.store.ValidateSequenceNumber(
                ref previousRecord,
                SequenceNumberConstraint.Arbitrary,
                "previousRecord");
 
            totalRecordSize = ValidateData(data);
            lrc = ValidateReservationCollection(reservationCollection);
            if (lrc == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentNull("reservationCollection"));
            }
            if (reservations == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentNull("reservations"));
            }
            ValidateReservations(reservations);
 
            EnsureMarshalContext();
 
            LogReserveAndAppendState state = new LogReserveAndAppendState();
 
            state.RecordSequence = this;
            state.Data = data;
            state.TotalRecordSize = totalRecordSize;
            state.UserLsn = userRecord.High;
            state.PreviousLsn = previousRecord.High;
            state.RecordAppendOptions = recordAppendOptions;
            state.ReservationCollection = lrc;
            state.Reservations = reservations;
 
            state.Start();
 
            return new SequenceNumber(state.ResultLsn);
        }
 
        public void SetLastRecord(
            SequenceNumber sequenceNumber)
        {
            this.store.ValidateSequenceNumber(
                ref sequenceNumber,
                SequenceNumberConstraint.Arbitrary,
                "sequenceNumber");
 
            ulong lsn = sequenceNumber.High;
 
            try
            {
                EnsureMarshalContext();
                UnsafeNativeMethods.TruncateLogSync(this.marshalContext,
                                                                        ref lsn);
            }
            catch (EntryPointNotFoundException)
            {
                // CLFS API for SetEndOfLog doesn;t update the client marshaling area and directly
                // uses the log handle. The client marshalling and kernal stats becomes out of sync 
                // and will cause subsequent appends to fail.
                // Work around - Invalidate the client marshall area. Call SetEndOfLog.
                // Subsequent Append calls will create a new marshalling context.
 
                InvalidateMarshalContext();
                UnsafeNativeMethods.SetEndOfLogSync(this.store.Handle,
                                                    ref lsn);
            }
        }
 
        public SequenceNumber WriteRestartArea(
            ArraySegment<byte> data)
        {
            return WriteRestartArea(new ArraySegment<byte>[] { data });
        }
 
        public SequenceNumber WriteRestartArea(
            IList<ArraySegment<byte>> data)
        {
            return WriteRestartArea(data, SequenceNumber.Invalid);
        }
 
        public SequenceNumber WriteRestartArea(
            ArraySegment<byte> data,
            SequenceNumber newBaseSeqNum)
        {
            return WriteRestartArea(new ArraySegment<byte>[] { data },
                                    newBaseSeqNum);
        }
 
        public SequenceNumber WriteRestartArea(
            IList<ArraySegment<byte>> data,
            SequenceNumber newBaseSeqNum)
        {
            return WriteRestartArea(data, newBaseSeqNum, null);
        }
 
        public SequenceNumber WriteRestartArea(
            ArraySegment<byte> data,
            SequenceNumber newBaseSeqNum,
            ReservationCollection reservations)
        {
            return WriteRestartArea(new ArraySegment<byte>[] { data },
                                    newBaseSeqNum,
                                    reservations);
        }
 
        public SequenceNumber WriteRestartArea(
            IList<ArraySegment<byte>> data,
            SequenceNumber newBaseSeqNum,
            ReservationCollection reservationCollection)
        {
            long totalRecordSize;
            LogReservationCollection lrc;
 
            this.store.ValidateSequenceNumber(
                ref newBaseSeqNum,
                SequenceNumberConstraint.Arbitrary,
                "newBaseSeqNum");
 
            totalRecordSize = ValidateData(data);
            lrc = ValidateReservationCollection(reservationCollection);
 
            EnsureMarshalContext();
 
            LogWriteRestartAreaState state = new LogWriteRestartAreaState();
 
            state.RecordSequence = this;
            state.Data = data;
            state.TotalRecordSize = totalRecordSize;
            state.ReservationCollection = lrc;
            state.NewBaseLsn = newBaseSeqNum.High;
 
            state.Start();
 
            return new SequenceNumber(state.ResultLsn);
        }
 
        void EnsureMarshalContext()
        {
            // NOTE: Simple synchronization here may cause problems. I
            //       am counting on the unfairness of the lock object to
            //       make this fast enough.
            //
            lock (this.store.SyncRoot)
            {
                if ((this.marshalContext != null) &&
                    (!this.marshalContext.IsInvalid))
                {
                    return;
                }
 
                try
                {
                    this.marshalContext =
                        UnsafeNativeMethods.CreateLogMarshallingArea(
                            this.store.Handle,
                            IntPtr.Zero,
                            IntPtr.Zero,
                            IntPtr.Zero,
                            this.bufferSize,
                            this.writeBufferCount,
                            DefaultReadBufferCount);
                }
                catch (InvalidOperationException)
                {
                    if (this.store.Extents.Count == 0)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                                        Error.InvalidOperation(SR.InvalidOperation_MustHaveExtents));
                    }
                    else
                    {
                        throw;
                    }
                }
            }
        }
 
        void InvalidateMarshalContext()
        {
            lock (this.store.SyncRoot)
            {
                if (this.marshalContext != null)
                {
                    this.marshalContext.Close();
                    this.marshalContext = null;
                }
            }
        }
 
        int GetBufferSize(int bufferSize)
        {
            if (bufferSize > MaxBufferSize)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentOutOfRange("bufferSize"));
            }
 
            bufferSize += LogLogRecordHeader.Size;
            bufferSize = (bufferSize + CLFS_SECTOR_ALIGN) & ~CLFS_SECTOR_ALIGN;
 
            return bufferSize;
        }
 
        long ValidateData(IList<ArraySegment<byte>> data)
        {
            if (data == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentNull("data"));
 
            long dataLength = 0;
            for (int i = 0; i < data.Count; i++)
            {
                ArraySegment<byte> segment = data[i];
 
                dataLength = checked(dataLength + segment.Count);
                if (dataLength > MaximumRecordLength)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentInvalid(SR.LogRecSeq_Append_TooBig));
            }
 
            return dataLength + LogLogRecordHeader.Size;
        }
 
        LogReservationCollection ValidateReservationCollection(
            ReservationCollection reservations)
        {
            LogReservationCollection lrc = null;
            if (reservations != null)
            {
                lrc = reservations as LogReservationCollection;
                if (lrc == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                                        Error.ArgumentInvalid(SR.LogRecSeq_InvalidReservationCollection));
 
                if (lrc.RecordSequence != this)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                                        Error.ArgumentInvalid(SR.LogRecSeq_InvalidReservationCollection));
            }
 
            return lrc;
        }
 
        void ValidateReservations(long[] reservations)
        {
            foreach (long reservation in reservations)
            {
                if ((reservation < 0) || (reservation > MaximumRecordLength))
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ArgumentOutOfRange("reservations"));
            }
        }
 
        class RecordEnumerable : IEnumerable<LogRecord>
        {
            LogRecordSequence recordSequence;
            CLFS_CONTEXT_MODE mode;
            ulong startLsn;
 
            internal RecordEnumerable(LogRecordSequence recordSequence,
                                      CLFS_CONTEXT_MODE mode,
                                      ulong startLsn)
            {
                this.recordSequence = recordSequence;
                this.mode = mode;
                this.startLsn = startLsn;
            }
 
            IEnumerator IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }
 
            public IEnumerator<LogRecord> GetEnumerator()
            {
                return new LogLogRecordEnumerator(this.recordSequence,
                                                  this.mode,
                                                  this.startLsn);
            }
        }
 
        class RestartAreaEnumerable : IEnumerable<LogRecord>
        {
            LogRecordSequence recordSequence;
 
            internal RestartAreaEnumerable(LogRecordSequence recordSequence)
            {
                this.recordSequence = recordSequence;
            }
 
            IEnumerator IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }
 
            public IEnumerator<LogRecord> GetEnumerator()
            {
                return new LogRestartAreaEnumerator(this.recordSequence);
            }
        }
    }
}