File: System\IO\Log\SimpleFileLog.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.Runtime.InteropServices;
    using System.Runtime.InteropServices.ComTypes;
 
    internal class SimpleFileLog
    {
        SmuggledIUnknown smuggledLog;
 
        internal SimpleFileLog(string absolutePath, int size)
        {
            // Open the log.
            // First, we try to open an existing log-- if that doesn't
            // work, we create a new one.
            //
            bool loaded = false;
            ILog log = (ILog)new SimpleFileBasedLog();
            try
            {
                if (File.Exists(absolutePath))
                {
                    try
                    {
                        IPersistFile ipf = (IPersistFile)log;
                        ipf.Load(absolutePath, (int)STGM.ReadWrite);
                        loaded = true;
                    }
                    catch (FileNotFoundException)
                    {
                        // File not found, create a new one.
                    }
                }
 
                if (!loaded)
                {
                    // File does not exist.  Attempt to create a new one,
                    // with the specified size.
                    //
                    IFileBasedLogInit logInit = (IFileBasedLogInit)log;
                    logInit.InitNew(absolutePath, size);
                }
 
                log.SetAccessPolicyHint(RECORD_READING_POLICY.RANDOM);
                this.smuggledLog = new SmuggledIUnknown(log);
            }
            catch (COMException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ConvertKnownException(exception));
            }
            catch (FileNotFoundException)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.Argument_FileNameInvalid)));
            }
            finally
            {
                SafeRelease(log);
            }
        }
 
        static void SafeRelease(object obj)
        {
            if (obj != null)
            {
                Marshal.FinalReleaseComObject(obj);
            }
        }
 
        internal void Close()
        {
 
            if (!this.smuggledLog.IsInvalid)
            {
                ILog log = (ILog)(this.smuggledLog.Smuggle());
                try
                {
                    this.smuggledLog.Close();
                }
                catch (COMException exception)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ConvertKnownException(exception));
                }
                finally
                {
                    SafeRelease(log);
                }
            }
        }
 
        internal void TruncatePrefix(SequenceNumber seqNumber)
        {
            if (this.smuggledLog.IsInvalid)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ObjectDisposed());
            }
 
            ILog log = (ILog)(this.smuggledLog.Smuggle());
            try
            {
                log.TruncatePrefix(seqNumber.High);
            }
            catch (COMException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ConvertKnownException(exception));
            }
            finally
            {
                SafeRelease(log);
            }
        }
 
        internal void Force(SequenceNumber seqNumber)
        {
            if (this.smuggledLog.IsInvalid)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ObjectDisposed());
            }
 
            ILog log = (ILog)(this.smuggledLog.Smuggle());
            try
            {
                log.Force(seqNumber.High);
            }
            catch (COMException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ConvertKnownException(exception));
            }
            finally
            {
                SafeRelease(log);
            }
        }
 
        internal void GetLogLimits(out SequenceNumber first, out SequenceNumber last)
        {
            ulong lsnfirst = 0;
            ulong lsnlast = 0;
 
            if (this.smuggledLog.IsInvalid)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ObjectDisposed());
            }
 
            ILog log = (ILog)(this.smuggledLog.Smuggle());
            try
            {
                log.GetLogLimits(out lsnfirst, out lsnlast);
            }
            catch (COMException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ConvertKnownException(exception));
            }
            finally
            {
                SafeRelease(log);
            }
 
            first = new SequenceNumber(lsnfirst);
            last = new SequenceNumber(lsnlast);
        }
 
        internal SequenceNumber AppendRecord(UnmanagedBlob[] data, bool fForceNow)
        {
            ulong recordLsn = 0;
 
            if (this.smuggledLog.IsInvalid)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ObjectDisposed());
            }
 
            ILog log = (ILog)(this.smuggledLog.Smuggle());
            try
            {
                // Due to WinSE 184096 bug, we always do force flush as a work around. Ignoring fForceNow value... 
                // If force flush is not done, the user will be able to read stale records after calling TruncatePrefix and 
                // the base lsn will not be updated to reflect the new lsn in TruncatePrefix call.
                // This is a temporary work around fix and if txflog issue is fixed at a later date, we need to have a QFE
                // to use the user passed in fForceNow flag value to support buffering of log records.
                // Related Bugs:
                //          WinSE: 184096
                //          Windows OS: 1729140
                //          MB: 57586 52216
 
                log.AppendRecord(data,
                                 data.Length,
                                 true,
                                 out recordLsn);
            }
            catch (COMException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ConvertKnownException(exception));
            }
            finally
            {
                SafeRelease(log);
            }
 
            return new SequenceNumber(recordLsn);
        }
 
        internal void ReadRecord(SequenceNumber seqNumtoRead, out byte[] record, out int recordSize, out SequenceNumber previous, out SequenceNumber next)
        {
            ulong prevLsn = 0;
            ulong nextLsn = 0;
            record = null;
            recordSize = 0;
 
            if (this.smuggledLog.IsInvalid)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ObjectDisposed());
            }
 
            ILog log = (ILog)(this.smuggledLog.Smuggle());
            try
            {
                CoTaskMemHandle pData = null;
                try
                {
                    log.ReadRecord(seqNumtoRead.High,
                                   out prevLsn,
                                   out nextLsn,
                                   out pData,
                                   out recordSize);
 
                    record = new byte[recordSize];
 
                    Marshal.Copy(pData.DangerousGetHandle(),
                                 record,
                                 0,
                                 recordSize);
                }
                catch (COMException exception)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ConvertKnownException(exception));
                }
                finally
                {
                    if (pData != null && !pData.IsInvalid) pData.Close();
                }
            }
            finally
            {
                SafeRelease(log);
            }
 
            previous = new SequenceNumber(prevLsn);
            next = new SequenceNumber(nextLsn);
        }
 
        internal void ReadRecordPrefix(SequenceNumber seqNumtoRead, out byte[] record, ref int cbData, out int recordSize, out SequenceNumber previous, out SequenceNumber next)
        {
            ulong prevLsn = 0;
            ulong nextLsn = 0;
            record = null;
            recordSize = 0;
 
            if (this.smuggledLog.IsInvalid)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ObjectDisposed());
            }
 
            ILog log = (ILog)(this.smuggledLog.Smuggle());
            try
            {
                record = new byte[cbData];
                log.ReadRecordPrefix(seqNumtoRead.High,
                                out prevLsn,
                                out nextLsn,
                                record,
                                ref cbData,
                                out recordSize);
 
            }
            catch (COMException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ConvertKnownException(exception));
            }
            finally
            {
                SafeRelease(log);
            }
 
            previous = new SequenceNumber(prevLsn);
            next = new SequenceNumber(nextLsn);
        }
    }
}