private void EmitPendingBuffers(bool doAll, bool mustWait)
{
// When combining parallel deflation with a ZipSegmentedStream, it's
// possible for the ZSS to throw from within this method. In that
// case, Close/Dispose will be called on this stream, if this stream
// is employed within a using or try/finally pair as required. But
// this stream is unaware of the pending exception, so the Close()
// method invokes this method AGAIN. This can lead to a deadlock.
// Therefore, failfast if re-entering.
if (emitting) return;
emitting = true;
if ((doAll && (_latestCompressed != _lastFilled)) || mustWait) {
_newlyCompressedBlob.WaitOne();
}
do
{
int firstSkip = -1;
int millisecondsToWait = doAll ? 200 : (mustWait ? -1 : 0);
int nextToWrite = -1;
do
{
if (Monitor.TryEnter(_toWrite, millisecondsToWait))
{
nextToWrite = -1;
try
{
if (_toWrite.Count > 0)
nextToWrite = _toWrite.Dequeue();
}
finally
{
Monitor.Exit(_toWrite);
}
if (nextToWrite >= 0)
{
WorkItem workitem = _pool[nextToWrite];
if (workitem.ordinal != _lastWritten + 1)
{
// out of order. requeue and try again.
TraceOutput(TraceBits.EmitSkip,
"Emit skip wi({0}) ord({1}) lw({2}) fs({3})",
workitem.index,
workitem.ordinal,
_lastWritten,
firstSkip);
lock(_toWrite)
{
_toWrite.Enqueue(nextToWrite);
}
if (firstSkip == nextToWrite)
{
// We went around the list once.
// None of the items in the list is the one we want.
// Now wait for a compressor to signal again.
_newlyCompressedBlob.WaitOne();
firstSkip = -1;
}
else if (firstSkip == -1)
firstSkip = nextToWrite;
continue;
}
firstSkip = -1;
TraceOutput(TraceBits.EmitBegin,
"Emit begin wi({0}) ord({1}) cba({2})",
workitem.index,
workitem.ordinal,
workitem.compressedBytesAvailable);
_outStream.Write(workitem.compressed, 0, workitem.compressedBytesAvailable);
_runningCrc.Combine(workitem.crc, workitem.inputBytesAvailable);
_totalBytesProcessed += workitem.inputBytesAvailable;
workitem.inputBytesAvailable = 0;
TraceOutput(TraceBits.EmitDone,
"Emit done wi({0}) ord({1}) cba({2}) mtw({3})",
workitem.index,
workitem.ordinal,
workitem.compressedBytesAvailable,
millisecondsToWait);
_lastWritten = workitem.ordinal;
_toFill.Enqueue(workitem.index);
// don't wait next time through
if (millisecondsToWait == -1) millisecondsToWait = 0;
}
}
else
nextToWrite = -1;
} while (nextToWrite >= 0);
} while (doAll && (_lastWritten != _latestCompressed || _lastWritten != _lastFilled));
emitting = false;
}