In my WCF service I need to provide functionality of files downloading with supporting of Range
HTTP header for chunked downloading. The first call to GetFile
method of service creates new MemoryMappedFile
instance from file on disk. Let's assume that in time when MMF created by the first request and that request still processing, the second call to GetFile
method of service opening existing MMF and returning streamed response to client. What happens if MMF will be disposed (and source file closed on MemoryMappedFile disposing) by thread which create it? Should second call successfully read all content from already opened ViewStream or no?
I had wrote small test and seems that till MemoryMappedFile opened by OpenExisting
method, it's lifetime extended and source file keeps opened. Is this true, or I missed some pitfall? I can't find any documentation for such case in MSDN.
Update: added additional Thread.Sleep call after existing MMF opened before obtaining MapView of file to simulate threads race
private static readonly string mapName = "foo";
private static readonly string fileName = @"some big file";
static void Main(string[] args)
{
var t1 = Task.Factory.StartNew(OpenMemoryMappedFile);
var t2 = Task.Factory.StartNew(ReadMemoryMappedFile);
Task.WaitAll(t1, t2);
}
private static void OpenMemoryMappedFile()
{
var stream = File.OpenRead(fileName);
using (var mmf = MemoryMappedFile.CreateFromFile(stream, mapName, 0, MemoryMappedFileAccess.Read, null, HandleInheritability.None, false))
{
Console.WriteLine("Memory mapped file created");
Thread.Sleep(1000); // timeout for another thread to open existing MMF
}
Console.WriteLine("Memory mapped file disposed");
}
private static void ReadMemoryMappedFile()
{
Thread.Sleep(100); //wait till MMF created
var buffer = new byte[1024 * 1024]; //1MB chunk
long totalLength = 0;
using (var f = File.OpenRead(fileName))
{
totalLength = f.Length;
}
using (var mmf = MemoryMappedFile.OpenExisting(mapName, MemoryMappedFileRights.Read))
{
Console.WriteLine("Existing MMF opened successfully");
Thread.Sleep(2000); //simulate threads race
using (var viewStream = mmf.CreateViewStream(0, 0, MemoryMappedFileAccess.Read))
{
Console.WriteLine("View of file mapped successfully");
File.Delete(Path.GetFileName(fileName));
using (var fileStream = File.Open(Path.GetFileName(fileName), FileMode.CreateNew, FileAccess.Write))
using (var writer = new BinaryWriter(fileStream))
{
int readBytes;
do
{
readBytes = viewStream.Read(buffer, 0, buffer.Length);
writer.Write(buffer, 0, readBytes);
Console.Write("{0:P}% of target file saved\r", fileStream.Length / (float)totalLength);
Thread.Sleep(10); //simulate network latency
} while (readBytes > 0);
Console.WriteLine();
Console.WriteLine("File saved successfully");
}
}
}
}