public Archiver(string outputFile, int fileAlignment) { archiveFile = outputFile; newWriteSignal = new Semaphore(0, Int32.MaxValue); exitEvent = new ManualResetEvent(false); writeData = new List <byte[]>(4000); outstandingReads = new GT2.RefCounter(); wasFinished = 0; writeThread = new Thread(new ParameterizedThreadStart(WriteThread)); writeThread.SetApartmentState(ApartmentState.STA); #if DEBUG writeThread.Name = "Archiver Write Thread"; #endif writeThread.Start(--fileAlignment); }
private void WriteThread(object o) { #if TEST_AS_FRENCH Tools.SetThreadToFrench(); #endif int fileAlignment = (int)o; string idxFile = Path.ChangeExtension(archiveFile, ".idx"); MemoryStream memoryIdx = new MemoryStream(4000 * 4); int bytesWrittenSoFar = 0; int writeIndexSoFar = 0; GT2.RefCounter completeWriteCount = new GT2.RefCounter(); FileStream archive = new FileStream(archiveFile, FileMode.Create, FileAccess.Write, FileShare.None, UInt16.MaxValue, true); using (BinaryWriter idxWriter = new BinaryWriter(memoryIdx)) { WaitHandle[] waitables = new WaitHandle[2] { newWriteSignal, exitEvent }; bool exitLoop = false; while (!exitLoop) { int signalled = WaitHandle.WaitAny(waitables, Timeout.Infinite, false); if (signalled == 0) { int count; lock (writeData) { count = writeData.Count; } while (writeIndexSoFar < count) { // the reads can complete out of order, but we need to write the archive file // in order. So if the alert we got wasn't for the next file, go back and wait // this is also why it's a while loop, so that when we eventually get the signal // for the next file, we can process the other ones we've aleady seen the signal for byte[] nextData = writeData[writeIndexSoFar]; if (nextData != null) { WriteState ws = new WriteState(archive, completeWriteCount); int dataLen = nextData.Length; int lengthWithAlignment = dataLen; int padAmount = 0; if (fileAlignment > 0) { lengthWithAlignment = (lengthWithAlignment + fileAlignment) & ~fileAlignment; padAmount = lengthWithAlignment - dataLen; Array.Resize(ref nextData, lengthWithAlignment); } archive.Position = bytesWrittenSoFar; archive.BeginWrite(nextData, 0, lengthWithAlignment, new AsyncCallback(WriteComplete), ws); idxWriter.Write(bytesWrittenSoFar | padAmount); bytesWrittenSoFar += lengthWithAlignment; ++writeIndexSoFar; } } } else { exitLoop = true; } Debug.Assert(signalled >= 0 && signalled <= 1, "Archive write thread got a signal other than 0 or 1!"); } } bool cancelled = (Thread.VolatileRead(ref wasFinished) == 0); if (!cancelled) { using (BinaryWriter idxFileWriter = new BinaryWriter(new FileStream(idxFile, FileMode.Create, FileAccess.Write, FileShare.None))) { // how many idxFileWriter.Write(writeIndexSoFar); // the offsets idxFileWriter.Write(memoryIdx.ToArray()); // file size idxFileWriter.Write(bytesWrittenSoFar); } } while (!completeWriteCount.HasReached(writeIndexSoFar)) { Thread.Sleep(100); } archive.Dispose(); if (cancelled) { File.Delete(archiveFile); } }
public WriteState(FileStream file, GT2.RefCounter count) { refCount = count; archive = file; }