Using Lock files for simple interprocess co-ordination mechanism

Hi folks, once I published this article I got a number of response stating this was very bad practice for web based applications, and I fully agree and this was never my intent. We currently only use this method in Windows Services that are running inside the firewall and I would never expect this method to be used in any web-sites or web-services for what i would consider very obvious reasons.

When developing robust reliable services you often need the ability to run multiple instances of those services on the same or different servers for reliability or scalability reasons. When managing processes outside of a database you may need a mechanism to block access to a resource (say an email in-box) while another instance of your service is working on that resource.
To manage such cases we often use a mechanism of accessing a file using an exclusive lock on it, and if that lock cannot be acquired then we know something else is working on that resource.

Code example.

Public Class InBoxReader
    Public Sub Main()
        Dim emailInBox As String = "myAutoInbox"

        Using lockHelper As New LockFileHelper(String.Format("\SomeSharedLocation\{0}.lock", emailInBox))
            If lockHelper.LockAcquire(100) Then
                ' O.k have lock now do processing on the InBox.
                ' The lock is automatically released in the Dispose method of the class.
            End If
        End Using

    End Sub
End Class
''' <summary>
''' Lock file helper.
''' </summary>
''' <remarks>
'''-------------------------------------------------------------------------------------------------
''' We use lock files in various places in the system to provide a simple co-ordination mechanism
''' between different threads within a process and for sharing access to resources with the same
''' process running on different machines.
'''-------------------------------------------------------------------------------------------------
''' </remarks>
Public Class LockFileHelper
    Implements IDisposable
    Private _lockFileName As String
    Private _ioStream As IO.FileStream
    Private _acquiredLock As Boolean = False
    Private _wasLocked As Boolean = False
    Public Sub New(ByVal LockFileName As String)
        _lockFileName = LockFileName
    End Sub
    Public ReadOnly Property LockFileName() As String
        Get
            Return _lockFileName
        End Get
    End Property
    Public ReadOnly Property WasLocked() As Boolean
        Get
            Return _wasLocked
        End Get
    End Property
    Public Function Exists() As Boolean
        Return IO.File.Exists(_lockFileName)
    End Function
    ''' <summary>
    ''' Simple check to see if the file is already locked.
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function IsLocked() As Boolean
        Dim isFileLocked As Boolean = False
        Try
            If Exists() Then
                _ioStream = IO.File.Open(_lockFileName, IO.FileMode.Create, IO.FileAccess.ReadWrite, IO.FileShare.None)
                _ioStream.Close()
            End If
        Catch ex As System.IO.IOException
            ' File is in used by another process.
            isFileLocked = True
        Catch ex As Exception
            ' Any other type of exception, re throw as we don't know what to do with it.
            Throw
        End Try

        Return isFileLocked
    End Function
    ''' <summary>
    ''' Attempt to acquire a lock within the given timeout, but throw an exception if we can't acquire it.
    ''' </summary>
    ''' <param name="TimeOutMilliseconds"></param>
    ''' <remarks></remarks>
    Public Sub LockAcquireWithException(ByVal TimeOutMilliseconds As Int32)
        If Not LockAcquire(TimeOutMilliseconds) Then
            Throw New Exception(String.Format("Timed out trying to acquire a lock on the file {0}", _lockFileName))
        End If
    End Sub
    ''' <summary>
    ''' Attempt to acquire a lock within the given timeout.
    ''' </summary>
    ''' <param name="TimeOutMilliseconds"></param>
    ''' <returns>
    ''' True - If an exclusive lock was successfully acquired on the file
    ''' False - If could not get an exclusive lock on the file
    ''' </returns>
    Public Function LockAcquire(ByVal TimeOutMilliseconds As Int32) As Boolean
        '-------------------------------------------------------------------------------------------------
        ' See have we already acquired the lock. THis can be useful in situations where we are passing
        ' locks around to various processes and each process may want to be sure it has acquired the lock.
        '-------------------------------------------------------------------------------------------------
        If _acquiredLock Then
            Return _acquiredLock
        End If

        _wasLocked = False
        Dim StartTicks As Int32 = System.Environment.TickCount
        Dim TimedOut As Boolean = False
        If Not IO.Directory.Exists(IO.Path.GetDirectoryName(_lockFileName)) Then
            IO.Directory.CreateDirectory(IO.Path.GetDirectoryName(_lockFileName))
        End If
        Do
            Try
                _ioStream = IO.File.Open(_lockFileName, IO.FileMode.Create, IO.FileAccess.ReadWrite, IO.FileShare.None)
                _acquiredLock = True
            Catch ex As System.IO.IOException
                ' File is in used by another process.
                _wasLocked = True
                Threading.Thread.Sleep(100)
            Catch ex As Exception
                Throw ex
            End Try
            TimedOut = ((System.Environment.TickCount - StartTicks) >= TimeOutMilliseconds)
        Loop Until _acquiredLock OrElse TimedOut
        '-------------------------------------------------------------------------------------------------
        ' Return back the status of the lock acquisition.
        '-------------------------------------------------------------------------------------------------
        Return _acquiredLock
    End Function
    ''' <summary>
    ''' Release the lock on the file (If we have a lock that is.)
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub LockRelease()
        If _acquiredLock Then
            _acquiredLock = False
            If Not IsNothing(_ioStream) Then
                _ioStream.Close()
                _ioStream = Nothing
            End If
        End If
    End Sub
#Region " IDisposable Support "
    Private disposedValue As Boolean = False        ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                Call LockRelease()
            End If

            ' TODO: free shared unmanaged resources
        End If
        Me.disposedValue = True
    End Sub
    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region
End Class