Author: rgrabowski Date: Mon May 25 17:46:43 2009 New Revision: 778461 URL: http://svn.apache.org/viewvc?rev=778461&view=rev Log: Fix for LOG4NET-164. Added MutexLock which allows for faster inter-process file locking compared to MinimalLock.
Modified: logging/log4net/trunk/src/Appender/FileAppender.cs Modified: logging/log4net/trunk/src/Appender/FileAppender.cs URL: http://svn.apache.org/viewvc/logging/log4net/trunk/src/Appender/FileAppender.cs?rev=778461&r1=778460&r2=778461&view=diff ============================================================================== --- logging/log4net/trunk/src/Appender/FileAppender.cs (original) +++ logging/log4net/trunk/src/Appender/FileAppender.cs Mon May 25 17:46:43 2009 @@ -20,7 +20,7 @@ using System; using System.IO; using System.Text; - +using System.Threading; using log4net.Util; using log4net.Layout; using log4net.Core; @@ -162,7 +162,7 @@ } void IDisposable.Dispose() { - this.Close(); + Close(); } public override void Write(byte[] buffer, int offset, int count) { @@ -356,6 +356,56 @@ get { return m_appender; } set { m_appender = value; } } + + /// <summary> + /// Helper method that creates a FileStream under CurrentAppender's SecurityContext. + /// </summary> + /// <remarks> + /// <para> + /// Typically called during OpenFile or AcquireLock. + /// </para> + /// <para> + /// If the directory portion of the <paramref name="filename"/> does not exist, it is created + /// via Directory.CreateDirecctory. + /// </para> + /// </remarks> + /// <param name="filename"></param> + /// <param name="append"></param> + /// <param name="fileShare"></param> + /// <returns></returns> + protected Stream CreateStream(string filename, bool append, FileShare fileShare) + { + using (CurrentAppender.SecurityContext.Impersonate(this)) + { + // Ensure that the directory structure exists + string directoryFullName = Path.GetDirectoryName(filename); + + // Only create the directory if it does not exist + // doing this check here resolves some permissions failures + if (!Directory.Exists(directoryFullName)) + { + Directory.CreateDirectory(directoryFullName); + } + + FileMode fileOpenMode = append ? FileMode.Append : FileMode.Create; + return new FileStream(filename, fileOpenMode, FileAccess.Write, FileShare.Read); + } + } + + /// <summary> + /// Helper method to close <paramref name="stream"/> under CurrentAppender's SecurityContext. + /// </summary> + /// <remarks> + /// Does not set <paramref name="stream"/> to null. + /// </remarks> + /// <param name="stream"></param> + protected void CloseStream(Stream stream) + { + using (CurrentAppender.SecurityContext.Impersonate(this)) + { + stream.Close(); + } + } } /// <summary> @@ -389,21 +439,7 @@ { try { - using(CurrentAppender.SecurityContext.Impersonate(this)) - { - // Ensure that the directory structure exists - string directoryFullName = Path.GetDirectoryName(filename); - - // Only create the directory if it does not exist - // doing this check here resolves some permissions failures - if (!Directory.Exists(directoryFullName)) - { - Directory.CreateDirectory(directoryFullName); - } - - FileMode fileOpenMode = append ? FileMode.Append : FileMode.Create; - m_stream = new FileStream(filename, fileOpenMode, FileAccess.Write, FileShare.Read); - } + m_stream = CreateStream(filename, append, FileShare.Read); } catch (Exception e1) { @@ -421,10 +457,8 @@ /// </remarks> public override void CloseFile() { - using(CurrentAppender.SecurityContext.Impersonate(this)) - { - m_stream.Close(); - } + CloseStream(m_stream); + m_stream = null; } /// <summary> @@ -522,22 +556,7 @@ { try { - using(CurrentAppender.SecurityContext.Impersonate(this)) - { - // Ensure that the directory structure exists - string directoryFullName = Path.GetDirectoryName(m_filename); - - // Only create the directory if it does not exist - // doing this check here resolves some permissions failures - if (!Directory.Exists(directoryFullName)) - { - Directory.CreateDirectory(directoryFullName); - } - - FileMode fileOpenMode = m_append ? FileMode.Append : FileMode.Create; - m_stream = new FileStream(m_filename, fileOpenMode, FileAccess.Write, FileShare.Read); - m_append=true; - } + m_stream = CreateStream(m_filename, m_append, FileShare.Read); } catch (Exception e1) { @@ -558,14 +577,108 @@ /// </remarks> public override void ReleaseLock() { - using(CurrentAppender.SecurityContext.Impersonate(this)) - { - m_stream.Close(); - m_stream=null; - } + CloseStream(m_stream); + m_stream = null; } } + /// <summary> + /// Provides cross-process file locking. + /// </summary> + /// <author>Ron Grabowski</author> + /// <author>Steve Wranovsky</author> + public class MutexLock : LockingModelBase + { + private Mutex m_mutex = null; + private bool m_mutexClosed = false; + private Stream m_stream = null; + + /// <summary> + /// Open the file specified and prepare for logging. + /// </summary> + /// <param name="filename">The filename to use</param> + /// <param name="append">Whether to append to the file, or overwrite</param> + /// <param name="encoding">The encoding to use</param> + /// <remarks> + /// <para> + /// Open the file specified and prepare for logging. + /// No writes will be made until <see cref="AcquireLock"/> is called. + /// Must be called before any calls to <see cref="AcquireLock"/>, + /// -<see cref="ReleaseLock"/> and <see cref="CloseFile"/>. + /// </para> + /// </remarks> + public override void OpenFile(string filename, bool append, Encoding encoding) + { + try + { + m_stream = CreateStream(filename, append, FileShare.ReadWrite); + + string mutextFriendlyFilename = filename + .Replace("\\", "_") + .Replace(":", "_") + .Replace("/", "_"); + + m_mutex = new Mutex(false, mutextFriendlyFilename); + } + catch (Exception e1) + { + CurrentAppender.ErrorHandler.Error("Unable to acquire lock on file " + filename + ". " + e1.Message); + } + } + + /// <summary> + /// Close the file + /// </summary> + /// <remarks> + /// <para> + /// Close the file. No further writes will be made. + /// </para> + /// </remarks> + public override void CloseFile() + { + CloseStream(m_stream); + m_stream = null; + + m_mutex.ReleaseMutex(); + m_mutex.Close(); + m_mutexClosed = true; + } + + /// <summary> + /// Acquire the lock on the file + /// </summary> + /// <returns>A stream that is ready to be written to.</returns> + /// <remarks> + /// <para> + /// Does nothing. The lock is already taken + /// </para> + /// </remarks> + public override Stream AcquireLock() + { + // TODO: add timeout? + m_mutex.WaitOne(); + + // should always be true (and fast) for FileStream + if (m_stream.CanSeek) + { + m_stream.Seek(0, SeekOrigin.End); + } + + return m_stream; + } + + /// <summary> + /// + /// </summary> + public override void ReleaseLock() + { + if (m_mutexClosed == false) + { + m_mutex.ReleaseMutex(); + } + } + } + #endregion Locking Models #region Public Instance Constructors