changeset 532:3b53dd1280c6 HEAD

I/O buffers now use real blocking instead of setting up a sub-ioloop to poll(). alarm() is called every 30 secs to send SIGHUP and break out of the read/write calls, so the given timeout values aren't exact. Also some other cleanups, like not including ioloop.h in [io]buffer.h which broke several other files which hadn't included it itself..
author Timo Sirainen <tss@iki.fi>
date Mon, 28 Oct 2002 06:18:26 +0200
parents 9aabf64c8da2
children 1f1ff728ff65
files src/auth/login-connection.c src/auth/master.c src/imap/client.c src/lib-index/mbox/mbox-rewrite.c src/lib-storage/index/mbox/mbox-expunge.c src/lib-storage/index/mbox/mbox-save.c src/lib/Makefile.am src/lib/alarm-hup.c src/lib/alarm-hup.h src/lib/file-lock.c src/lib/ibuffer-data.c src/lib/ibuffer-file.c src/lib/ibuffer-mmap.c src/lib/ibuffer.c src/lib/ibuffer.h src/lib/iobuffer-internal.h src/lib/iobuffer.c src/lib/lib.c src/lib/obuffer-file.c src/lib/obuffer.c src/lib/obuffer.h src/login/auth-connection.c src/login/client-authenticate.c src/login/client.c src/master/auth-process.c src/master/login-process.c
diffstat 26 files changed, 370 insertions(+), 371 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/login-connection.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/auth/login-connection.c	Mon Oct 28 06:18:26 2002 +0200
@@ -1,9 +1,10 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "common.h"
-#include "network.h"
+#include "ioloop.h"
 #include "ibuffer.h"
 #include "obuffer.h"
+#include "network.h"
 #include "login-connection.h"
 
 #include <stdlib.h>
--- a/src/auth/master.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/auth/master.c	Mon Oct 28 06:18:26 2002 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "common.h"
+#include "ioloop.h"
 #include "obuffer.h"
 #include "network.h"
 #include "cookie.h"
--- a/src/imap/client.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/imap/client.c	Mon Oct 28 06:18:26 2002 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "common.h"
+#include "ioloop.h"
 #include "network.h"
 #include "ibuffer.h"
 #include "obuffer.h"
@@ -29,8 +30,7 @@
 
 static void client_input(Client *client);
 
-static void client_output_timeout(void *context,
-				  Timeout timeout __attr_unused__)
+static void client_output_timeout(void *context)
 {
 	Client *client = context;
 
@@ -38,8 +38,7 @@
 	o_buffer_close(client->outbuf);
 }
 
-static void client_input_timeout(void *context,
-				 Timeout timeout __attr_unused__)
+static void client_input_timeout(void *context)
 {
 	Client *client = context;
 
--- a/src/lib-index/mbox/mbox-rewrite.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib-index/mbox/mbox-rewrite.c	Mon Oct 28 06:18:26 2002 +0200
@@ -374,8 +374,7 @@
 
 	inbuf = i_buffer_create_mmap(in_fd, data_stack_pool,
 				     1024*256, 0, 0, FALSE);
-	outbuf = o_buffer_create_file(out_fd, data_stack_pool, 1024,
-				      IO_PRIORITY_DEFAULT, FALSE);
+	outbuf = o_buffer_create_file(out_fd, data_stack_pool, 1024, 0, FALSE);
 
 	ret = o_buffer_send_ibuffer(outbuf, inbuf);
 	if (ret < 0)
@@ -459,8 +458,7 @@
 	//offset = hdr_size = body_size = 0; /* just to keep compiler happy */
 
 	t_push();
-	outbuf = o_buffer_create_file(tmp_fd, data_stack_pool, 8192,
-				      IO_PRIORITY_DEFAULT, FALSE);
+	outbuf = o_buffer_create_file(tmp_fd, data_stack_pool, 8192, 0, FALSE);
 
 	failed = FALSE; seq = 1;
 	rec = index->lookup(index, 1);
--- a/src/lib-storage/index/mbox/mbox-expunge.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib-storage/index/mbox/mbox-expunge.c	Mon Oct 28 06:18:26 2002 +0200
@@ -128,7 +128,7 @@
 
 	t_push();
 	outbuf = o_buffer_create_file(ibox->index->mbox_fd, data_stack_pool,
-				      4096, IO_PRIORITY_DEFAULT, FALSE);
+				      4096, 0, FALSE);
 
 	failed = !expunge_real(ibox, rec, seq, inbuf, outbuf, notify);
 
--- a/src/lib-storage/index/mbox/mbox-save.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib-storage/index/mbox/mbox-save.c	Mon Oct 28 06:18:26 2002 +0200
@@ -195,7 +195,7 @@
 		t_push();
 		outbuf = o_buffer_create_file(index->mbox_fd,
 					      data_stack_pool, 4096,
-					      IO_PRIORITY_DEFAULT, FALSE);
+					      0, FALSE);
 
 		if (!write_from_line(box->storage, outbuf, mbox_path,
 				     internal_date) ||
--- a/src/lib/Makefile.am	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib/Makefile.am	Mon Oct 28 06:18:26 2002 +0200
@@ -11,6 +11,7 @@
 	ioloop-select.c
 
 liblib_a_SOURCES = \
+	alarm-hup.c \
 	base64.c \
 	compat.c \
 	data-stack.c \
@@ -54,6 +55,7 @@
 	write-full.c
 
 noinst_HEADERS = \
+	alarm-hup.h \
 	base64.h \
 	compat.h \
 	data-stack.h \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/alarm-hup.c	Mon Oct 28 06:18:26 2002 +0200
@@ -0,0 +1,113 @@
+/*
+ alarm-hup.c - Send SIGHUP once in a while to avoid infinitely blocking calls
+
+    Copyright (c) 2002 Timo Sirainen
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "lib.h"
+#include "alarm-hup.h"
+
+#include <signal.h>
+#include <unistd.h>
+
+static int initialized = FALSE;
+static unsigned int alarm_timeout = 30;
+
+unsigned int alarm_hup_set_interval(unsigned int timeout)
+{
+	unsigned int old;
+
+	old = alarm_timeout;
+	alarm_timeout = timeout;
+
+	alarm(alarm_timeout);
+	return old;
+}
+
+static void sig_alarm(int signo __attr_unused__)
+{
+	/* we need fcntl() to stop with EINTR */
+	if (raise(SIGHUP) < 0)
+		i_fatal("kill(): %m");
+
+	/* do it again */
+	alarm(alarm_timeout);
+
+#ifndef HAVE_SIGACTION
+	signal(SIGALRM, sig_alarm);
+#endif
+}
+
+void alarm_hup_init(void)
+{
+#ifdef HAVE_SIGACTION
+	struct sigaction act;
+#endif
+
+	if (initialized)
+		return;
+	initialized = TRUE;
+
+#ifdef HAVE_SIGACTION
+	if (sigemptyset(&act.sa_mask) < 0)
+		i_fatal("sigemptyset(): %m");
+	act.sa_flags = 0;
+	act.sa_handler = sig_alarm;
+
+	while (sigaction(SIGALRM, &act, NULL) < 0) {
+		if (errno != EINTR)
+			i_fatal("sigaction(): %m");
+	}
+#else
+	/* at least Linux blocks raise(SIGHUP) inside SIGALRM
+	   handler if it's added with signal().. sigaction() should
+	   be pretty much everywhere though, so this code is pretty
+	   useless. */
+#warning timeouting may not work
+	signal(SIGALRM, sig_alarm);
+#endif
+
+	alarm(alarm_timeout);
+}
+
+void alarm_hup_deinit(void)
+{
+#ifdef HAVE_SIGACTION
+	struct sigaction act;
+#endif
+
+	if (!initialized)
+		return;
+	initialized = FALSE;
+
+	alarm(0);
+
+#ifdef HAVE_SIGACTION
+	act.sa_handler = SIG_DFL;
+	while (sigaction(SIGALRM, &act, NULL) < 0) {
+		if (errno != EINTR)
+			i_fatal("sigaction(): %m");
+	}
+#else
+	signal(SIGALRM, SIG_DFL);
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/alarm-hup.h	Mon Oct 28 06:18:26 2002 +0200
@@ -0,0 +1,12 @@
+#ifndef __ALARM_HUP_H
+#define __ALARM_HUP_H
+
+/* Set new alarm() interval. Returns the old one. alarm() is called
+   immediately with the specified timeout. */
+unsigned int alarm_hup_set_interval(unsigned int timeout);
+
+/* init() may be called multiple times. */
+void alarm_hup_init(void);
+void alarm_hup_deinit(void);
+
+#endif
--- a/src/lib/file-lock.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib/file-lock.c	Mon Oct 28 06:18:26 2002 +0200
@@ -24,24 +24,24 @@
 */
 
 #include "lib.h"
+#include "alarm-hup.h"
 #include "file-lock.h"
 
+#include <time.h>
 #include <signal.h>
 
-static int got_alarm = FALSE;
-
-static void sig_alarm(int signo __attr_unused__)
-{
-	got_alarm = TRUE;
-
-	/* we need fcntl() to stop with EINTR */
-	if (raise(SIGHUP) < 0)
-		i_fatal("kill(): %m");
-}
-
-static int file_lock(int fd, int wait_lock, int lock_type)
+static int file_lock(int fd, int wait_lock, int lock_type,
+		     unsigned int timeout)
 {
 	struct flock fl;
+	time_t timeout_time;
+
+	if (timeout == 0)
+		timeout_time = 0;
+	else {
+		alarm_hup_init();
+		timeout_time = time(NULL) + timeout;
+	}
 
 	fl.l_type = lock_type;
 	fl.l_whence = SEEK_SET;
@@ -55,7 +55,7 @@
 		if (errno != EINTR)
 			return -1;
 
-		if (got_alarm) {
+		if (timeout != 0 && time(NULL) >= timeout_time) {
 			errno = EAGAIN;
 			return 0;
 		}
@@ -66,56 +66,13 @@
 
 int file_try_lock(int fd, int lock_type)
 {
-        got_alarm = FALSE;
-	return file_lock(fd, FALSE, lock_type);
+	return file_lock(fd, FALSE, lock_type, 0);
 }
 
-int file_wait_lock(int fd, int lock_type, unsigned int timeout __attr_unused__)
+int file_wait_lock(int fd, int lock_type, unsigned int timeout)
 {
-#ifdef HAVE_SIGACTION
-	struct sigaction act;
-#endif
 	int ret;
 
-	got_alarm = FALSE;
-
-	if (timeout > 0 && lock_type != F_UNLCK) {
-#ifdef HAVE_SIGACTION
-		if (sigemptyset(&act.sa_mask) < 0)
-			i_fatal("sigemptyset(): %m");
-		act.sa_flags = 0;
-		act.sa_handler = sig_alarm;
-
-		while (sigaction(SIGALRM, &act, NULL) < 0) {
-			if (errno != EINTR)
-				i_fatal("sigaction(): %m");
-		}
-#else
-		/* at least Linux blocks raise(SIGHUP) inside SIGALRM
-		   handler if it's added with signal().. sigaction() should
-		   be pretty much everywhere though, so this code is pretty
-		   useless. */
-#warning file_wait_lock() timeouting may not work
-		signal(SIGALRM, sig_alarm);
-#endif
-
-		alarm(timeout);
-	}
-
-	ret = file_lock(fd, TRUE, lock_type);
-
-	if (timeout > 0 && lock_type != F_UNLCK) {
-		alarm(0);
-
-#ifdef HAVE_SIGACTION
-		act.sa_handler = SIG_DFL;
-		while (sigaction(SIGALRM, &act, NULL) < 0) {
-			if (errno != EINTR)
-				i_fatal("sigaction(): %m");
-		}
-#else
-		signal(SIGALRM, SIG_IGN);
-#endif
-	}
+	ret = file_lock(fd, TRUE, lock_type, timeout);
 	return ret;
 }
--- a/src/lib/ibuffer-data.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib/ibuffer-data.c	Mon Oct 28 06:18:26 2002 +0200
@@ -41,7 +41,7 @@
 
 static void _set_blocking(_IOBuffer *buf __attr_unused__,
 			  int timeout_msecs __attr_unused__,
-			  TimeoutFunc timeout_func __attr_unused__,
+			  void (*timeout_func)(void *) __attr_unused__,
 			  void *context __attr_unused__)
 {
 }
--- a/src/lib/ibuffer-file.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib/ibuffer-file.c	Mon Oct 28 06:18:26 2002 +0200
@@ -24,11 +24,16 @@
 */
 
 #include "lib.h"
+#include "alarm-hup.h"
 #include "ibuffer-internal.h"
 
 #include <unistd.h>
+#include <network.h>
 
-#define I_BUFFER_MIN_SIZE 1024
+#define I_BUFFER_MIN_SIZE 4096
+
+#define BUFFER_IS_BLOCKING(fbuf) \
+	((fbuf)->timeout_msecs != 0)
 
 typedef struct {
 	_IBuffer ibuf;
@@ -37,18 +42,12 @@
 	uoff_t skip_left;
 
 	int timeout_msecs;
-	TimeoutFunc timeout_func;
+	void (*timeout_func)(void *);
 	void *timeout_context;
 
 	unsigned int autoclose_fd:1;
 } FileIBuffer;
 
-typedef struct {
-	IOLoop ioloop;
-	IBuffer *buf;
-	int timeout;
-} IOLoopReadContext;
-
 static void _close(_IOBuffer *buf)
 {
 	FileIBuffer *fbuf = (FileIBuffer *) buf;
@@ -76,13 +75,18 @@
 }
 
 static void _set_blocking(_IOBuffer *buf, int timeout_msecs,
-			  TimeoutFunc timeout_func, void *context)
+			  void (*timeout_func)(void *), void *context)
 {
 	FileIBuffer *fbuf = (FileIBuffer *) buf;
 
 	fbuf->timeout_msecs = timeout_msecs;
 	fbuf->timeout_func = timeout_func;
 	fbuf->timeout_context = context;
+
+	net_set_nonblock(fbuf->ibuf.fd, timeout_msecs == 0);
+
+	if (timeout_msecs != 0)
+		alarm_hup_init();
 }
 
 static void i_buffer_grow(_IBuffer *buf, size_t bytes)
@@ -104,8 +108,7 @@
 
 static void i_buffer_compress(_IBuffer *buf)
 {
-	memmove(buf->w_buffer, buf->w_buffer + buf->skip,
-		buf->pos - buf->skip);
+	memmove(buf->w_buffer, buf->w_buffer + buf->skip, buf->pos - buf->skip);
 	buf->pos -= buf->skip;
 
 	if (buf->skip > buf->cr_lookup_pos)
@@ -116,63 +119,10 @@
 	buf->skip = 0;
 }
 
-static void ioloop_read(void *context, int fd __attr_unused__,
-			IO io __attr_unused__)
-{
-	IOLoopReadContext *ctx = context;
-
-	if (i_buffer_read(ctx->buf) != 0) {
-		/* got data / error */
-		io_loop_stop(ctx->ioloop);
-	}
-}
-
-static void ioloop_timeout(void *context, Timeout timeout __attr_unused__)
-{
-	IOLoopReadContext *ctx = context;
-
-	ctx->timeout = TRUE;
-	io_loop_stop(ctx->ioloop);
-}
-
-static ssize_t i_buffer_read_blocking(_IBuffer *buf)
-{
-	FileIBuffer *fbuf = (FileIBuffer *) buf;
-        IOLoopReadContext ctx;
-	Timeout to;
-	IO io;
-
-	t_push();
-
-	/* create a new I/O loop */
-	memset(&ctx, 0, sizeof(ctx));
-	ctx.ioloop = io_loop_create(data_stack_pool);
-	ctx.buf = &buf->ibuffer;
-
-	io = io_add(buf->fd, IO_READ, ioloop_read, &ctx);
-	to = fbuf->timeout_msecs <= 0 ? NULL :
-		timeout_add(fbuf->timeout_msecs, ioloop_timeout, &ctx);
-
-	io_loop_run(ctx.ioloop);
-
-	io_remove(io);
-	if (to != NULL) {
-		if (ctx.timeout && fbuf->timeout_func != NULL) {
-			/* call user-given timeout function */
-			fbuf->timeout_func(fbuf->timeout_context, to);
-		}
-		timeout_remove(to);
-	}
-
-	io_loop_destroy(ctx.ioloop);
-	t_pop();
-
-	return buf->pos > buf->skip ? (ssize_t) (buf->pos-buf->skip) : -1;
-}
-
 static ssize_t _read(_IBuffer *buf)
 {
 	FileIBuffer *fbuf = (FileIBuffer *) buf;
+	time_t timeout_time;
 	size_t size;
 	ssize_t ret;
 
@@ -207,27 +157,32 @@
 		}
 	}
 
-	ret = read(buf->fd, buf->w_buffer + buf->pos, size);
-	if (ret == 0) {
-		/* EOF */
-		buf->ibuffer.buf_errno = 0;
-		return -1;
-	}
+	timeout_time = GET_TIMEOUT_TIME(fbuf);
 
-	if (ret < 0) {
-		if (errno == EINTR || errno == EAGAIN)
-			ret = 0;
-		else {
-			buf->ibuffer.buf_errno = errno;
+	ret = -1;
+	do {
+		if (ret == 0 && time(NULL) > timeout_time) {
+			/* timeouted */
+			if (fbuf->timeout_func != NULL)
+				fbuf->timeout_func(fbuf->timeout_context);
+			buf->ibuffer.buf_errno = EAGAIN;
 			return -1;
 		}
-	}
-	buf->pos += ret;
+
+		ret = read(buf->fd, buf->w_buffer + buf->pos, size);
+		if (ret == 0) {
+			/* EOF */
+			buf->ibuffer.buf_errno = 0;
+			return -1;
+		}
 
-	do {
-		if (ret == 0 && fbuf->timeout_msecs > 0) {
-			/* blocking read */
-			ret = i_buffer_read_blocking(buf);
+		if (ret < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				ret = 0;
+			else {
+				buf->ibuffer.buf_errno = errno;
+				return -1;
+			}
 		}
 
 		if (ret > 0 && fbuf->skip_left > 0) {
@@ -241,8 +196,9 @@
 				fbuf->skip_left = 0;
 			}
 		}
-	} while (ret == 0 && fbuf->timeout_msecs != 0);
+	} while (ret == 0 && BUFFER_IS_BLOCKING(fbuf));
 
+	buf->pos += ret;
 	return ret;
 }
 
--- a/src/lib/ibuffer-mmap.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib/ibuffer-mmap.c	Mon Oct 28 06:18:26 2002 +0200
@@ -85,7 +85,7 @@
 
 static void _set_blocking(_IOBuffer *buf __attr_unused__,
 			  int timeout_msecs __attr_unused__,
-			  TimeoutFunc timeout_func __attr_unused__,
+			  void (*timeout_func)(void *) __attr_unused__,
 			  void *context __attr_unused__)
 {
 	/* we never block */
--- a/src/lib/ibuffer.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib/ibuffer.c	Mon Oct 28 06:18:26 2002 +0200
@@ -55,7 +55,7 @@
 }
 
 void i_buffer_set_blocking(IBuffer *buf, int timeout_msecs,
-			   TimeoutFunc timeout_func, void *context)
+			   void (*timeout_func)(void *), void *context)
 {
 	_io_buffer_set_blocking(buf->real_buffer, timeout_msecs,
 				timeout_func, context);
--- a/src/lib/ibuffer.h	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib/ibuffer.h	Mon Oct 28 06:18:26 2002 +0200
@@ -1,8 +1,6 @@
 #ifndef __IBUFFER_H
 #define __IBUFFER_H
 
-#include "ioloop.h" /* TimeoutFunc */
-
 struct _IBuffer {
 	uoff_t start_offset;
 	uoff_t v_offset, v_size, v_limit; /* relative to start_offset */
@@ -42,11 +40,11 @@
    removes the limit. The offset is  */
 void i_buffer_set_read_limit(IBuffer *buf, uoff_t v_offset);
 /* Makes reads blocking until at least one byte is read. timeout_func is
-   called if nothing is read in specified time. The blocking state in file
-   descriptor isn't changed, but for timeout to work it must be in
-   non-blocking state. Setting timeout_msecs to 0 makes it non-blocking. */
+   called if nothing is read in specified time. Setting timeout_msecs to 0
+   makes it non-blocking. This call changes non-blocking state of file
+   descriptor. */
 void i_buffer_set_blocking(IBuffer *buf, int timeout_msecs,
-			   TimeoutFunc timeout_func, void *context);
+			   void (*timeout_func)(void *), void *context);
 
 /* Returns number of bytes read if read was ok, -1 if EOF or error, -2 if the
    buffer is full. */
--- a/src/lib/iobuffer-internal.h	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib/iobuffer-internal.h	Mon Oct 28 06:18:26 2002 +0200
@@ -15,7 +15,7 @@
 	void (*destroy)(_IOBuffer *buf);
 	void (*set_max_size)(_IOBuffer *buf, size_t max_size);
 	void (*set_blocking)(_IOBuffer *buf, int timeout_msecs,
-			     TimeoutFunc timeout_func, void *context);
+			     void (*timeout_func)(void *), void *context);
 };
 
 void _io_buffer_init(Pool pool, _IOBuffer *buf);
@@ -24,6 +24,12 @@
 void _io_buffer_close(_IOBuffer *buf);
 void _io_buffer_set_max_size(_IOBuffer *buf, size_t max_size);
 void _io_buffer_set_blocking(_IOBuffer *buf, int timeout_msecs,
-			     TimeoutFunc timeout_func, void *context);
+			     void (*timeout_func)(void *), void *context);
+
+#define GET_TIMEOUT_TIME(fbuf) \
+        ((fbuf)->timeout_msecs == 0 ? 0 : \
+	 time(NULL) + ((fbuf)->timeout_msecs / 1000))
+#define BUFFER_IS_BLOCKING(fbuf) \
+	((fbuf)->timeout_msecs != 0)
 
 #endif
--- a/src/lib/iobuffer.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib/iobuffer.c	Mon Oct 28 06:18:26 2002 +0200
@@ -64,7 +64,7 @@
 }
 
 void _io_buffer_set_blocking(_IOBuffer *buf, int timeout_msecs,
-			     TimeoutFunc timeout_func, void *context)
+			     void (*timeout_func)(void *), void *context)
 {
 	buf->set_blocking(buf, timeout_msecs, timeout_func, context);
 }
--- a/src/lib/lib.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib/lib.c	Mon Oct 28 06:18:26 2002 +0200
@@ -24,6 +24,7 @@
 */
 
 #include "lib.h"
+#include "alarm-hup.h"
 
 #include <stdlib.h>
 #include <time.h>
@@ -50,6 +51,8 @@
 
 void lib_deinit(void)
 {
+	alarm_hup_deinit(); /* doesn't harm even if init is never called */
+
         imem_deinit();
 	data_stack_deinit();
         failures_deinit();
--- a/src/lib/obuffer-file.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib/obuffer-file.c	Mon Oct 28 06:18:26 2002 +0200
@@ -24,6 +24,7 @@
 */
 
 #include "lib.h"
+#include "alarm-hup.h"
 #include "network.h"
 #include "sendfile-util.h"
 #include "ibuffer.h"
@@ -38,8 +39,6 @@
 
 #define IS_BUFFER_EMPTY(fbuf) \
 	(!(fbuf)->full && (fbuf)->head == (fbuf)->tail)
-#define BUFFER_IS_BLOCKING(fbuf) \
-	((fbuf)->timeout_msecs != 0)
 
 #define MAX_SSIZE_T(size) \
 	((size) < SSIZE_T_MAX ? (size_t)(size) : SSIZE_T_MAX)
@@ -56,7 +55,7 @@
 	size_t head, tail; /* first unsent/unused byte */
 
 	int timeout_msecs;
-	TimeoutFunc timeout_func;
+	void (*timeout_func)(void *);
 	void *timeout_context;
 
 	unsigned int full:1; /* if head == tail, is buffer empty or full? */
@@ -65,17 +64,6 @@
 	unsigned int autoclose_fd:1;
 } FileOBuffer;
 
-typedef struct {
-	IOLoop ioloop;
-	FileOBuffer *fbuf;
-	uoff_t sent;
-	int timeout;
-
-	IBuffer *inbuf;
-	struct iovec iov[3];
-	unsigned int iov_len;
-} IOLoopWriteContext;
-
 static void buffer_closed(FileOBuffer *fbuf)
 {
 	if (fbuf->autoclose_fd && fbuf->fd != -1) {
@@ -117,13 +105,18 @@
 }
 
 static void _set_blocking(_IOBuffer *buf, int timeout_msecs,
-			  TimeoutFunc timeout_func, void *context)
+			  void (*timeout_func)(void *), void *context)
 {
 	FileOBuffer *fbuf = (FileOBuffer *) buf;
 
 	fbuf->timeout_msecs = timeout_msecs;
 	fbuf->timeout_func = timeout_func;
 	fbuf->timeout_context = context;
+
+	net_set_nonblock(fbuf->fd, timeout_msecs == 0);
+
+	if (timeout_msecs != 0)
+		alarm_hup_init();
 }
 
 static void _cork(_OBuffer *buf)
@@ -221,61 +214,6 @@
 	return ret;
 }
 
-static void ioloop_send(IOLoopWriteContext *ctx)
-{
-	if (o_buffer_writev(ctx->fbuf, ctx->iov, ctx->iov_len) < 0 ||
-	    ctx->iov[ctx->iov_len-1].iov_len == 0) {
-		/* error / all sent */
-		io_loop_stop(ctx->ioloop);
-	}
-}
-
-static void ioloop_timeout(void *context, Timeout timeout __attr_unused__)
-{
-	IOLoopWriteContext *ctx = context;
-
-	ctx->timeout = TRUE;
-	io_loop_stop(ctx->ioloop);
-}
-
-static int o_buffer_ioloop(FileOBuffer *fbuf, IOLoopWriteContext *ctx,
-			   void (*send_func)(IOLoopWriteContext *ctx))
-{
-	Timeout to;
-	IO io;
-
-	/* close old IO */
-	if (fbuf->io != NULL)
-		io_remove(fbuf->io);
-
-	t_push();
-
-	/* create a new I/O loop */
-	ctx->ioloop = io_loop_create(data_stack_pool);
-	ctx->fbuf = fbuf;
-
-	io = io_add(fbuf->fd, IO_WRITE, (IOFunc) send_func, ctx);
-	to = fbuf->timeout_msecs <= 0 ? NULL :
-		timeout_add(fbuf->timeout_msecs, ioloop_timeout, ctx);
-
-	io_loop_run(ctx->ioloop);
-
-	io_remove(io);
-
-	if (to != NULL) {
-		if (ctx->timeout && fbuf->timeout_func != NULL) {
-			/* call user-given timeout function */
-			fbuf->timeout_func(fbuf->timeout_context, to);
-		}
-		timeout_remove(to);
-	}
-
-	io_loop_destroy(ctx->ioloop);
-	t_pop();
-
-	return fbuf->obuf.obuffer.closed ? -1 : 1;
-}
-
 /* returns how much of vector was used */
 static int o_buffer_fill_iovec(FileOBuffer *fbuf, struct iovec iov[2])
 {
@@ -302,18 +240,36 @@
 static int o_buffer_send_blocking(FileOBuffer *fbuf, const void *data,
 				  size_t size)
 {
-	IOLoopWriteContext ctx;
-
-	memset(&ctx, 0, sizeof(ctx));
+	time_t timeout_time;
+	struct iovec iov[3];
+	int iov_len, first;
 
-	ctx.iov_len = o_buffer_fill_iovec(fbuf, ctx.iov);
+	iov_len = o_buffer_fill_iovec(fbuf, iov);
 	if (size > 0) {
-		ctx.iov[ctx.iov_len].iov_base = (void *) data;
-		ctx.iov[ctx.iov_len].iov_len = size;
-		ctx.iov_len++;
+		iov[iov_len].iov_base = (void *) data;
+		iov[iov_len].iov_len = size;
+		iov_len++;
 	}
 
-        return o_buffer_ioloop(fbuf, &ctx, ioloop_send);
+	first = TRUE;
+
+	timeout_time = GET_TIMEOUT_TIME(fbuf);
+	while (iov[iov_len-1].iov_len != 0) {
+		if (first)
+			first = FALSE;
+		else if (time(NULL) > timeout_time) {
+			/* timeouted */
+			if (fbuf->timeout_func != NULL)
+				fbuf->timeout_func(fbuf->timeout_context);
+			fbuf->obuf.obuffer.buf_errno = EAGAIN;
+			return -1;
+		}
+
+		if (o_buffer_writev(fbuf, iov, iov_len) < 0)
+			return -1;
+	}
+
+        return 1;
 }
 
 static int buffer_flush(FileOBuffer *fbuf)
@@ -515,7 +471,9 @@
 
 	buf->obuffer.buf_errno = 0;
 
-	if (IS_BUFFER_EMPTY(fbuf) &&
+	/* never try sending buffer immediately if we're block,
+	   so we don't need to deal with timeout issues here */
+	if (IS_BUFFER_EMPTY(fbuf) && BUFFER_IS_BLOCKING(fbuf) &&
 	    (!fbuf->corked || !_have_space(buf, size))) {
 		iov.iov_base = (void *) data;
 		iov.iov_len = size;
@@ -538,77 +496,14 @@
 	}
 }
 
-static void ioloop_sendfile(IOLoopWriteContext *ctx)
+static off_t io_buffer_sendfile(_OBuffer *outbuf, IBuffer *inbuf)
 {
-	OBuffer *outbuf;
+	FileOBuffer *foutbuf = (FileOBuffer *) outbuf;
+	time_t timeout_time;
+	uoff_t start_offset;
 	uoff_t offset, send_size;
 	ssize_t ret;
-	int in_fd;
-
-	outbuf = &ctx->fbuf->obuf.obuffer;
-	in_fd = i_buffer_get_fd(ctx->inbuf);
-	i_assert(in_fd != -1);
-
-	offset = ctx->inbuf->start_offset + ctx->inbuf->v_offset;
-	send_size = ctx->inbuf->v_limit - ctx->inbuf->v_offset;
-
-	ret = safe_sendfile(ctx->fbuf->fd, in_fd, &offset,
-			    MAX_SSIZE_T(send_size));
-	if (ret < 0) {
-		if (errno != EINTR && errno != EAGAIN) {
-			outbuf->buf_errno = errno;
-			buffer_closed(ctx->fbuf);
-		}
-		ret = 0;
-	}
-
-	i_buffer_skip(ctx->inbuf, (size_t)ret);
-	outbuf->offset += ret;
-
-	if (outbuf->closed || (size_t)ret == send_size)
-		io_loop_stop(ctx->ioloop);
-}
-
-static void ioloop_copy(IOLoopWriteContext *ctx)
-{
-	const unsigned char *data;
-	size_t size;
-	int pos;
-
-	i_assert(ctx->iov_len <= 2);
-
-	(void)i_buffer_read_data(ctx->inbuf, &data, &size, O_BUFFER_MIN_SIZE-1);
-
-	if (size == 0) {
-		/* all sent */
-		io_loop_stop(ctx->ioloop);
-		return;
-	}
-
-	pos = ctx->iov_len++;
-	ctx->iov[pos].iov_base = (void *) data;
-	ctx->iov[pos].iov_len = size;
-
-	if (o_buffer_writev(ctx->fbuf, ctx->iov, ctx->iov_len) < 0) {
-		/* error */
-		io_loop_stop(ctx->ioloop);
-		return;
-	}
-
-	i_buffer_skip(ctx->inbuf, size - ctx->iov[pos].iov_len);
-
-	do {
-		ctx->iov_len--;
-	} while (ctx->iov_len > 0 && ctx->iov[ctx->iov_len-1].iov_len == 0);
-}
-
-static off_t o_buffer_sendfile(_OBuffer *outbuf, IBuffer *inbuf)
-{
-	FileOBuffer *foutbuf = (FileOBuffer *) outbuf;
-        IOLoopWriteContext ctx;
-	uoff_t offset, send_size;
-	ssize_t s_ret;
-	int in_fd;
+	int in_fd, first;
 
 	in_fd = i_buffer_get_fd(inbuf);
 	if (in_fd == -1) {
@@ -616,57 +511,125 @@
 		return -1;
 	}
 
+	/* set timeout time before flushing existing buffer which may block */
+	timeout_time = GET_TIMEOUT_TIME(foutbuf);
+        start_offset = inbuf->v_offset;
+
 	/* flush out any data in buffer */
 	if (buffer_flush(foutbuf) < 0)
 		return -1;
 
-	/* first try if we can do it with a single sendfile() call */
-	offset = inbuf->start_offset + inbuf->v_offset;
-	send_size = inbuf->v_limit - inbuf->v_offset;
-
-	s_ret = safe_sendfile(foutbuf->fd, in_fd, &offset,
-			      MAX_SSIZE_T(send_size));
-	if (s_ret < 0) {
-		if (errno != EINTR && errno != EAGAIN) {
-			outbuf->obuffer.buf_errno = errno;
-			if (errno != EINVAL) {
-				/* close only if error wasn't because
-				   sendfile() isn't supported */
-				buffer_closed(foutbuf);
-			}
+	first = TRUE;
+	for (;;) {
+		if (first)
+			first = FALSE;
+		else if (time(NULL) > timeout_time) {
+			/* timeouted */
+			if (foutbuf->timeout_func != NULL)
+				foutbuf->timeout_func(foutbuf->timeout_context);
+			outbuf->obuffer.buf_errno = EAGAIN;
 			return -1;
 		}
-		s_ret = 0;
+
+		offset = inbuf->start_offset + inbuf->v_offset;
+		send_size = inbuf->v_limit - inbuf->v_offset;
+
+		ret = safe_sendfile(foutbuf->fd, in_fd, &offset,
+				    MAX_SSIZE_T(send_size));
+		if (ret < 0) {
+			if (errno != EINTR && errno != EAGAIN) {
+				outbuf->obuffer.buf_errno = errno;
+				if (errno != EINVAL) {
+					/* close only if error wasn't because
+					   sendfile() isn't supported */
+					buffer_closed(foutbuf);
+				}
+				return -1;
+			}
+
+			if (!BUFFER_IS_BLOCKING(foutbuf)) {
+				/* don't block */
+				break;
+			}
+			ret = 0;
+		}
+
+		i_buffer_skip(inbuf, (size_t)ret);
+		outbuf->obuffer.offset += ret;
+
+		if ((uoff_t)ret == send_size) {
+			/* yes, all sent */
+			break;
+		}
 	}
 
-	i_buffer_skip(inbuf, (size_t)s_ret);
-	outbuf->obuffer.offset += s_ret;
+	return (off_t) (inbuf->v_offset - start_offset);
+}
+
+static off_t io_buffer_copy(_OBuffer *outbuf, IBuffer *inbuf)
+{
+	FileOBuffer *foutbuf = (FileOBuffer *) outbuf;
+	time_t timeout_time;
+	uoff_t start_offset;
+	struct iovec iov[3];
+	int iov_len;
+	const unsigned char *data;
+	size_t size;
+	ssize_t ret;
+	int pos;
+
+	timeout_time = GET_TIMEOUT_TIME(foutbuf);
+	iov_len = o_buffer_fill_iovec(foutbuf, iov);
+
+        start_offset = inbuf->v_offset;
+	for (;;) {
+		(void)i_buffer_read_data(inbuf, &data, &size,
+					 O_BUFFER_MIN_SIZE-1);
+
+		if (size == 0) {
+			/* all sent */
+			break;
+		}
 
-	if ((uoff_t)s_ret == send_size) {
-		/* yes, all sent */
-		return (off_t)s_ret;
+		pos = iov_len++;
+		iov[pos].iov_base = (void *) data;
+		iov[pos].iov_len = size;
+
+		ret = o_buffer_writev(foutbuf, iov, iov_len);
+		if (ret < 0) {
+			/* error */
+			return -1;
+		}
+
+		if (ret == 0 && !BUFFER_IS_BLOCKING(foutbuf)) {
+			/* don't block */
+			break;
+		}
+
+		if (time(NULL) > timeout_time) {
+			/* timeouted */
+			if (foutbuf->timeout_func != NULL)
+				foutbuf->timeout_func(foutbuf->timeout_context);
+			return -1;
+		}
+
+		i_buffer_skip(inbuf, size - iov[pos].iov_len);
+		iov_len--;
+
+		/* if we already sent the iov[0] and iov[1], we
+		   can just remove them from future calls */
+		while (iov_len > 0 && iov[0].iov_len == 0) {
+			iov[0] = iov[1];
+			if (iov_len > 1) iov[1] = iov[2];
+			iov_len--;
+		}
 	}
 
-	memset(&ctx, 0, sizeof(ctx));
-
-	ctx.fbuf = foutbuf;
-	ctx.inbuf = inbuf;
-
-	if (o_buffer_ioloop(foutbuf, &ctx, ioloop_sendfile) < 0) {
-		if (outbuf->obuffer.buf_errno == EINVAL) {
-			/* this shouldn't happen, must be a bug. It would also
-			   mess up later if we let this pass. */
-			i_panic("o_buffer_sendfile() failed: %m");
-		}
-		return -1;
-	} else {
-		return (off_t)ctx.sent;
-	}
+	return (off_t) (inbuf->v_offset - start_offset);
 }
 
 static off_t _send_ibuffer(_OBuffer *outbuf, IBuffer *inbuf)
 {
-        IOLoopWriteContext ctx;
 	off_t ret;
 
 	i_assert(inbuf->v_limit <= OFF_T_MAX);
@@ -675,27 +638,15 @@
 	if (inbuf->v_offset == inbuf->v_limit)
 		return 0;
 
-	ret = o_buffer_sendfile(outbuf, inbuf);
+	ret = io_buffer_sendfile(outbuf, inbuf);
 	if (ret >= 0 || outbuf->obuffer.buf_errno != EINVAL)
 		return ret;
 
-	/* sendfile() not supported, reset error */
-	outbuf->obuffer.buf_errno = 0;
-
 	/* sendfile() not supported (with this fd), fallback to
 	   regular sending */
 
-	/* create blocking send loop */
-	memset(&ctx, 0, sizeof(ctx));
-
-	ctx.fbuf = (FileOBuffer *) outbuf;
-	ctx.iov_len = o_buffer_fill_iovec(ctx.fbuf, ctx.iov);
-	ctx.inbuf = inbuf;
-
-	if (o_buffer_ioloop(ctx.fbuf, &ctx, ioloop_copy) < 0)
-		return -1;
-	else
-		return (off_t)ctx.sent;
+	outbuf->obuffer.buf_errno = 0;
+	return io_buffer_copy(outbuf, inbuf);
 }
 
 OBuffer *o_buffer_create_file(int fd, Pool pool, size_t max_buffer_size,
--- a/src/lib/obuffer.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib/obuffer.c	Mon Oct 28 06:18:26 2002 +0200
@@ -49,7 +49,7 @@
 }
 
 void o_buffer_set_blocking(OBuffer *buf, int timeout_msecs,
-			   TimeoutFunc timeout_func, void *context)
+			   void (*timeout_func)(void *), void *context)
 {
 	_io_buffer_set_blocking(buf->real_buffer, timeout_msecs,
 				timeout_func, context);
--- a/src/lib/obuffer.h	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/lib/obuffer.h	Mon Oct 28 06:18:26 2002 +0200
@@ -1,8 +1,6 @@
 #ifndef __OBUFFER_H
 #define __OBUFFER_H
 
-#include "ioloop.h" /* TimeoutFunc */
-
 struct _OBuffer {
 	uoff_t offset;
 
@@ -27,11 +25,10 @@
 void o_buffer_set_max_size(OBuffer *buf, size_t max_size);
 /* Buffer is made to be flushed out whenever it gets full (assumes max_size
    is already set), ie. writes will never be partial. Also makes any blocking
-   writes to fail after specified timeout, also calling timeout_func if it's
-   set. The blocking state in file descriptor isn't changed, but for timeout
-   to work it must be in non-blocking state. */
+   writes to fail after specified timeout, calling timeout_func if it's
+   set. This call changes non-blocking state of file descriptor. */
 void o_buffer_set_blocking(OBuffer *buf, int timeout_msecs,
-			   TimeoutFunc timeout_func, void *context);
+			   void (*timeout_func)(void *), void *context);
 
 /* Delays sending as far as possible, writing only full buffers. Also sets
    TCP_CORK on if supported. o_buffer_flush() removes the cork. */
@@ -50,7 +47,7 @@
 ssize_t o_buffer_send(OBuffer *buf, const void *data, size_t size);
 /* Send data from input buffer to output buffer using the fastest
    possible method. Returns number of bytes sent, or -1 if error.
-   Note that this function may block. */
+   Note that this function may block if either inbuf or outbuf is blocking. */
 off_t o_buffer_send_ibuffer(OBuffer *outbuf, IBuffer *inbuf);
 
 #endif
--- a/src/login/auth-connection.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/login/auth-connection.c	Mon Oct 28 06:18:26 2002 +0200
@@ -2,6 +2,7 @@
 
 #include "common.h"
 #include "hash.h"
+#include "ioloop.h"
 #include "network.h"
 #include "ibuffer.h"
 #include "obuffer.h"
--- a/src/login/client-authenticate.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/login/client-authenticate.c	Mon Oct 28 06:18:26 2002 +0200
@@ -2,6 +2,7 @@
 
 #include "common.h"
 #include "base64.h"
+#include "ioloop.h"
 #include "ibuffer.h"
 #include "obuffer.h"
 #include "temp-string.h"
--- a/src/login/client.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/login/client.c	Mon Oct 28 06:18:26 2002 +0200
@@ -2,6 +2,7 @@
 
 #include "common.h"
 #include "hash.h"
+#include "ioloop.h"
 #include "ibuffer.h"
 #include "obuffer.h"
 #include "client.h"
--- a/src/master/auth-process.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/master/auth-process.c	Mon Oct 28 06:18:26 2002 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "common.h"
+#include "ioloop.h"
 #include "network.h"
 #include "obuffer.h"
 #include "restrict-access.h"
--- a/src/master/login-process.c	Mon Oct 28 06:08:35 2002 +0200
+++ b/src/master/login-process.c	Mon Oct 28 06:18:26 2002 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "common.h"
+#include "ioloop.h"
 #include "network.h"
 #include "obuffer.h"
 #include "fdpass.h"