1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
|
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
namespace FlickrNet
{
/// <summary>
/// A non-reentrant mutex that is implemented using
/// a lock file, and thus works across processes,
/// sessions, and machines (as long as the underlying
/// FS provides robust r/w locking).
///
/// To use:
///
/// FileLock fLock = new FileLock(@"c:\foo\my.lock");
///
/// using (fLock.Acquire())
/// {
/// // protected operations
/// }
/// </summary>
internal class LockFile
{
private readonly string filepath;
private readonly DisposeHelper disposeHelper;
private Stream stream;
public LockFile(string filepath)
{
this.filepath = filepath;
this.disposeHelper = new DisposeHelper(this);
}
public IDisposable Acquire()
{
string dir = Path.GetDirectoryName(filepath);
lock (this)
{
#if !WindowsCE
while (stream != null)
Monitor.Wait(this);
#endif
while (true)
{
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
try
{
Debug.Assert(stream == null, "Stream was not null--programmer error");
stream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.None, 8, false);
return disposeHelper;
}
catch (IOException ioe)
{
int errorCode = SafeNativeMethods.GetErrorCode(ioe);
switch (errorCode)
{
case 32:
case 33:
case 32 | 0x1620:
case 33 | 0x1620:
Thread.Sleep(50);
continue;
default:
throw;
}
}
}
}
}
internal void Release()
{
lock (this)
{
#if !WindowsCE
// Doesn't hurt to pulse. Note that waiting threads will not actually
// continue to execute until this critical section is exited.
Monitor.PulseAll(this);
#endif
if (stream == null)
throw new InvalidOperationException("Tried to dispose a FileLock that was not owned");
try
{
stream.Close();
try
{
File.Delete(filepath);
} catch(IOException) { /* could fail if already acquired elsewhere */ }
}
finally
{
stream = null;
}
}
}
private class DisposeHelper : IDisposable
{
private readonly LockFile lockFile;
public DisposeHelper(LockFile lockFile)
{
this.lockFile = lockFile;
}
public void Dispose()
{
lockFile.Release();
}
}
}
}
|