bool return_nolf_line:1;
bool stream_size_passthrough:1; /* stream is parent's size */
bool nonpersistent_buffers:1;
+ /* After IO is added back to this istream, call io_set_pending() for
+ it. This is set only for the root istream. */
bool io_pending:1;
+ /* If this is TRUE for the istream or its parents after i_stream_read(),
+ set the istream IO pending again. This is cleared before each
+ istream read(). The purpose is to prevent hangs in case a child
+ istream has set the IO pending, but a parent istream read() won't
+ call the child istream's read() e.g. because its internal buffer is
+ already full. In such situation the IO must be set pending again.
+ This is especially necessary when the istream doesn't otherwise make
+ it visible that it has buffered data, such as ssl-istream. */
+ bool io_pending_until_read:1;
};
struct istream_snapshot {
return prev_snapshot;
}
+static bool i_stream_is_io_pending_until_read(struct istream_private *_stream)
+{
+ while (_stream->parent != NULL && !_stream->io_pending_until_read)
+ _stream = _stream->parent->real_stream;
+ return _stream->io_pending_until_read;
+}
+
ssize_t i_stream_read(struct istream *stream)
{
struct istream_private *_stream = stream->real_stream;
}
}
#endif
+ if (!_stream->istream.eof &&
+ i_stream_is_io_pending_until_read(_stream)) {
+ /* One of the parent istreams still has IO pending, because its
+ read() wasn't called. Set IO back to pending to prevent
+ hangs. */
+ i_stream_set_input_pending(stream, TRUE);
+ }
return ret;
}
_stream->high_pos = 0;
} else {
_stream->high_pos = 0;
+ _stream->io_pending_until_read = FALSE;
ret = _stream->read(_stream);
}
i_assert(old_size <= _stream->pos - _stream->skip);
if (!pending)
return;
+ stream->real_stream->io_pending_until_read = TRUE;
+
stream = i_stream_get_root_io(stream);
if (stream->real_stream->io != NULL)
io_set_pending(stream->real_stream->io);