dovecot-2.2: Added istream-timeout, which triggers I/O event and...

dovecot at dovecot.org dovecot at dovecot.org
Thu Apr 3 16:55:06 UTC 2014


details:   http://hg.dovecot.org/dovecot-2.2/rev/b1a756176ed2
changeset: 17189:b1a756176ed2
user:      Timo Sirainen <tss at iki.fi>
date:      Thu Apr 03 19:53:13 2014 +0300
description:
Added istream-timeout, which triggers I/O event and fails with ETIMEDOUT after the timeout.

diffstat:

 src/lib/Makefile.am       |    2 +
 src/lib/istream-timeout.c |  123 ++++++++++++++++++++++++++++++++++++++++++++++
 src/lib/istream-timeout.h |    9 +++
 3 files changed, 134 insertions(+), 0 deletions(-)

diffs (159 lines):

diff -r fd186eff7325 -r b1a756176ed2 src/lib/Makefile.am
--- a/src/lib/Makefile.am	Thu Apr 03 19:51:52 2014 +0300
+++ b/src/lib/Makefile.am	Thu Apr 03 19:53:13 2014 +0300
@@ -70,6 +70,7 @@
 	istream-seekable.c \
 	istream-sized.c \
 	istream-tee.c \
+	istream-timeout.c \
 	ioloop.c \
 	ioloop-iolist.c \
 	ioloop-notify-none.c \
@@ -197,6 +198,7 @@
 	istream-seekable.h \
 	istream-sized.h \
 	istream-tee.h \
+	istream-timeout.h \
 	ioloop.h \
 	ioloop-iolist.h \
 	ioloop-private.h \
diff -r fd186eff7325 -r b1a756176ed2 src/lib/istream-timeout.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/istream-timeout.c	Thu Apr 03 19:53:13 2014 +0300
@@ -0,0 +1,123 @@
+/* Copyright (c) 2014 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "time-util.h"
+#include "istream-private.h"
+#include "istream-timeout.h"
+
+struct timeout_istream {
+	struct istream_private istream;
+
+	struct timeout *to;
+	struct timeval last_read_timestamp;
+
+	unsigned int timeout_msecs;
+	bool update_timestamp;
+};
+
+static void i_stream_timeout_close(struct iostream_private *stream,
+				   bool close_parent)
+{
+	struct timeout_istream *tstream = (struct timeout_istream *)stream;
+
+	if (tstream->to != NULL)
+		timeout_remove(&tstream->to);
+	if (close_parent)
+		i_stream_close(tstream->istream.parent);
+}
+
+static void i_stream_timeout_switch_ioloop(struct istream_private *stream)
+{
+	struct timeout_istream *tstream = (struct timeout_istream *)stream;
+
+	if (tstream->to != NULL)
+		tstream->to = io_loop_move_timeout(&tstream->to);
+}
+
+static void i_stream_timeout(struct timeout_istream *tstream)
+{
+	unsigned int msecs;
+	int diff;
+
+	timeout_remove(&tstream->to);
+
+	diff = timeval_diff_msecs(&ioloop_timeval, &tstream->last_read_timestamp);
+	if (diff < (int)tstream->timeout_msecs) {
+		/* we haven't reached the read timeout yet, update it */
+		if (diff < 0)
+			diff = 0;
+		tstream->to = timeout_add(tstream->timeout_msecs - diff,
+					  i_stream_timeout, tstream);
+		return;
+	}
+
+	msecs = tstream->timeout_msecs % 1000;
+	io_stream_set_error(&tstream->istream.iostream,
+			    "Read timeout in %u%s s after %"PRIuUOFF_T" bytes",
+			    tstream->timeout_msecs/1000,
+			    msecs == 0 ? "" : t_strdup_printf(".%u", msecs),
+			    tstream->istream.istream.v_offset);
+	tstream->istream.istream.stream_errno = ETIMEDOUT;
+
+	i_stream_set_input_pending(tstream->istream.parent, TRUE);
+}
+
+static ssize_t
+i_stream_timeout_read(struct istream_private *stream)
+{
+	struct timeout_istream *tstream = (struct timeout_istream *)stream;
+	ssize_t ret;
+
+	i_stream_seek(stream->parent, stream->parent_start_offset +
+		      stream->istream.v_offset);
+
+	ret = i_stream_read_copy_from_parent(&stream->istream);
+	if (ret < 0) {
+		/* failed */
+	} else if (tstream->to == NULL) {
+		/* first read. add the timeout here instead of in init
+		   in case the stream is created long before it's actually
+		   read from. */
+		tstream->to = tstream->timeout_msecs == 0 ? NULL :
+			timeout_add(tstream->timeout_msecs,
+				    i_stream_timeout, tstream);
+		tstream->update_timestamp = TRUE;
+		tstream->last_read_timestamp = ioloop_timeval;
+	} else if (ret > 0 && tstream->to != NULL) {
+		/* we read something, reset the timeout */
+		timeout_reset(tstream->to);
+		/* make sure we get called again on the next ioloop run.
+		   this updates the timeout to the timestamp where we actually
+		   would have wanted to start waiting for more data (so if
+		   there is long-running code outside the ioloop it's not
+		   counted) */
+		tstream->update_timestamp = TRUE;
+		tstream->last_read_timestamp = ioloop_timeval;
+		i_stream_set_input_pending(&stream->istream, TRUE);
+	} else if (tstream->update_timestamp) {
+		tstream->update_timestamp = FALSE;
+		tstream->last_read_timestamp = ioloop_timeval;
+	}
+	return ret;
+}
+
+struct istream *
+i_stream_create_timeout(struct istream *input, unsigned int timeout_msecs)
+{
+	struct timeout_istream *tstream;
+
+	tstream = i_new(struct timeout_istream, 1);
+	tstream->timeout_msecs = timeout_msecs;
+	tstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
+	tstream->istream.stream_size_passthrough = TRUE;
+
+	tstream->istream.read = i_stream_timeout_read;
+	tstream->istream.switch_ioloop = i_stream_timeout_switch_ioloop;
+	tstream->istream.iostream.close = i_stream_timeout_close;
+
+	tstream->istream.istream.blocking = input->blocking;
+	tstream->istream.istream.seekable = input->seekable;
+	return i_stream_create(&tstream->istream, input,
+			       i_stream_get_fd(input));
+}
diff -r fd186eff7325 -r b1a756176ed2 src/lib/istream-timeout.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/istream-timeout.h	Thu Apr 03 19:53:13 2014 +0300
@@ -0,0 +1,9 @@
+#ifndef ISTREAM_TIMEOUT_H
+#define ISTREAM_TIMEOUT_H
+
+/* Return ETIMEDOUT error if read() doesn't return anything for timeout_msecs.
+   If timeout_msecs=0, there is no timeout. */
+struct istream *
+i_stream_create_timeout(struct istream *input, unsigned int timeout_msecs);
+
+#endif


More information about the dovecot-cvs mailing list