[dovecot-cvs] dovecot/src/lib alarm-hup.c,NONE,1.1 alarm-hup.h,NONE,1.1 Makefile.am,1.9,1.10 file-lock.c,1.2,1.3 ibuffer-data.c,1.3,1.4 ibuffer-file.c,1.3,1.4 ibuffer-mmap.c,1.7,1.8 ibuffer.c,1.6,1.7 ibuffer.h,1.2,1.3 iobuffer-internal.h,1.1,1.2 iobuffer.c,1.35,1.36 Message-Id: <20021028041828.CF3C22382D@danu.procontrol.fi>

cras at procontrol.fi cras at procontrol.fi
Mon Oct 28 06:18:28 EET 2002


Update of /home/cvs/dovecot/src/lib
In directory danu:/tmp/cvs-serv7362/lib

Modified Files:
	Makefile.am file-lock.c ibuffer-data.c ibuffer-file.c 
	ibuffer-mmap.c ibuffer.c ibuffer.h iobuffer-internal.h 
	iobuffer.c lib.c obuffer-file.c obuffer.c obuffer.h 
Added Files:
	alarm-hup.c alarm-hup.h 
Log Message:
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..



--- NEW FILE: alarm-hup.c ---
/*
 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
}

--- NEW FILE: alarm-hup.h ---
#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

Index: Makefile.am
===================================================================
RCS file: /home/cvs/dovecot/src/lib/Makefile.am,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- Makefile.am	24 Oct 2002 00:15:38 -0000	1.9
+++ Makefile.am	28 Oct 2002 04:18:26 -0000	1.10
@@ -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 \

Index: file-lock.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/file-lock.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- file-lock.c	23 Oct 2002 20:41:35 -0000	1.2
+++ file-lock.c	28 Oct 2002 04:18:26 -0000	1.3
@@ -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;
 }

Index: ibuffer-data.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/ibuffer-data.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- ibuffer-data.c	20 Oct 2002 02:53:01 -0000	1.3
+++ ibuffer-data.c	28 Oct 2002 04:18:26 -0000	1.4
@@ -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__)
 {
 }

Index: ibuffer-file.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/ibuffer-file.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- ibuffer-file.c	25 Oct 2002 01:58:43 -0000	1.3
+++ ibuffer-file.c	28 Oct 2002 04:18:26 -0000	1.4
@@ -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;
 
-	do {
-		if (ret == 0 && fbuf->timeout_msecs > 0) {
-			/* blocking read */
-			ret = i_buffer_read_blocking(buf);
+		ret = read(buf->fd, buf->w_buffer + buf->pos, size);
+		if (ret == 0) {
+			/* EOF */
+			buf->ibuffer.buf_errno = 0;
+			return -1;
+		}
+
+		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;
 }
 

Index: ibuffer-mmap.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/ibuffer-mmap.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- ibuffer-mmap.c	26 Oct 2002 20:35:56 -0000	1.7
+++ ibuffer-mmap.c	28 Oct 2002 04:18:26 -0000	1.8
@@ -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 */

Index: ibuffer.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/ibuffer.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- ibuffer.c	26 Oct 2002 20:06:27 -0000	1.6
+++ ibuffer.c	28 Oct 2002 04:18:26 -0000	1.7
@@ -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);

Index: ibuffer.h
===================================================================
RCS file: /home/cvs/dovecot/src/lib/ibuffer.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- ibuffer.h	20 Oct 2002 02:53:01 -0000	1.2
+++ ibuffer.h	28 Oct 2002 04:18:26 -0000	1.3
@@ -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. */

Index: iobuffer-internal.h
===================================================================
RCS file: /home/cvs/dovecot/src/lib/iobuffer-internal.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- iobuffer-internal.h	13 Oct 2002 23:49:11 -0000	1.1
+++ iobuffer-internal.h	28 Oct 2002 04:18:26 -0000	1.2
@@ -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

Index: iobuffer.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/iobuffer.c,v
retrieving revision 1.35
retrieving revision 1.36
diff -u -d -r1.35 -r1.36
--- iobuffer.c	13 Oct 2002 23:49:11 -0000	1.35
+++ iobuffer.c	28 Oct 2002 04:18:26 -0000	1.36
@@ -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);
 }

Index: lib.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/lib.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- lib.c	9 Oct 2002 17:44:04 -0000	1.5
+++ lib.c	28 Oct 2002 04:18:26 -0000	1.6
@@ -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();

Index: obuffer-file.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/obuffer-file.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- obuffer-file.c	21 Oct 2002 22:35:02 -0000	1.10
+++ obuffer-file.c	28 Oct 2002 04:18:26 -0000	1.11
@@ -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,135 +496,140 @@
 	}
 }
 
-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;
+	int in_fd, first;
 
-	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;
+	in_fd = i_buffer_get_fd(inbuf);
+	if (in_fd == -1) {
+		outbuf->obuffer.buf_errno = EINVAL;
+		return -1;
 	}
 
-	i_buffer_skip(ctx->inbuf, (size_t)ret);
-	outbuf->offset += ret;
+	/* set timeout time before flushing existing buffer which may block */
+	timeout_time = GET_TIMEOUT_TIME(foutbuf);
+        start_offset = inbuf->v_offset;
 
-	if (outbuf->closed || (size_t)ret == send_size)
-		io_loop_stop(ctx->ioloop);
-}
+	/* flush out any data in buffer */
+	if (buffer_flush(foutbuf) < 0)
+		return -1;
 
-static void ioloop_copy(IOLoopWriteContext *ctx)
-{
-	const unsigned char *data;
-	size_t size;
-	int pos;
+	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;
+		}
 
-	i_assert(ctx->iov_len <= 2);
+		offset = inbuf->start_offset + inbuf->v_offset;
+		send_size = inbuf->v_limit - inbuf->v_offset;
 
-	(void)i_buffer_read_data(ctx->inbuf, &data, &size, O_BUFFER_MIN_SIZE-1);
+		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 (size == 0) {
-		/* all sent */
-		io_loop_stop(ctx->ioloop);
-		return;
-	}
+			if (!BUFFER_IS_BLOCKING(foutbuf)) {
+				/* don't block */
+				break;
+			}
+			ret = 0;
+		}
 
-	pos = ctx->iov_len++;
-	ctx->iov[pos].iov_base = (void *) data;
-	ctx->iov[pos].iov_len = size;
+		i_buffer_skip(inbuf, (size_t)ret);
+		outbuf->obuffer.offset += ret;
 
-	if (o_buffer_writev(ctx->fbuf, ctx->iov, ctx->iov_len) < 0) {
-		/* error */
-		io_loop_stop(ctx->ioloop);
-		return;
+		if ((uoff_t)ret == send_size) {
+			/* yes, all sent */
+			break;
+		}
 	}
 
-	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);
+	return (off_t) (inbuf->v_offset - start_offset);
 }
 
-static off_t o_buffer_sendfile(_OBuffer *outbuf, IBuffer *inbuf)
+static off_t io_buffer_copy(_OBuffer *outbuf, IBuffer *inbuf)
 {
 	FileOBuffer *foutbuf = (FileOBuffer *) outbuf;
-        IOLoopWriteContext ctx;
-	uoff_t offset, send_size;
-	ssize_t s_ret;
-	int in_fd;
+	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;
 
-	in_fd = i_buffer_get_fd(inbuf);
-	if (in_fd == -1) {
-		outbuf->obuffer.buf_errno = EINVAL;
-		return -1;
-	}
+	timeout_time = GET_TIMEOUT_TIME(foutbuf);
+	iov_len = o_buffer_fill_iovec(foutbuf, iov);
 
-	/* flush out any data in buffer */
-	if (buffer_flush(foutbuf) < 0)
-		return -1;
+        start_offset = inbuf->v_offset;
+	for (;;) {
+		(void)i_buffer_read_data(inbuf, &data, &size,
+					 O_BUFFER_MIN_SIZE-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;
+		if (size == 0) {
+			/* all sent */
+			break;
+		}
 
-	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);
-			}
+		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;
 		}
-		s_ret = 0;
-	}
-
-	i_buffer_skip(inbuf, (size_t)s_ret);
-	outbuf->obuffer.offset += s_ret;
 
-	if ((uoff_t)s_ret == send_size) {
-		/* yes, all sent */
-		return (off_t)s_ret;
-	}
+		if (ret == 0 && !BUFFER_IS_BLOCKING(foutbuf)) {
+			/* don't block */
+			break;
+		}
 
-	memset(&ctx, 0, sizeof(ctx));
+		if (time(NULL) > timeout_time) {
+			/* timeouted */
+			if (foutbuf->timeout_func != NULL)
+				foutbuf->timeout_func(foutbuf->timeout_context);
+			return -1;
+		}
 
-	ctx.fbuf = foutbuf;
-	ctx.inbuf = inbuf;
+		i_buffer_skip(inbuf, size - iov[pos].iov_len);
+		iov_len--;
 
-	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");
+		/* 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--;
 		}
-		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,

Index: obuffer.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/obuffer.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- obuffer.c	14 Oct 2002 00:38:52 -0000	1.2
+++ obuffer.c	28 Oct 2002 04:18:26 -0000	1.3
@@ -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);

Index: obuffer.h
===================================================================
RCS file: /home/cvs/dovecot/src/lib/obuffer.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- obuffer.h	14 Oct 2002 15:13:07 -0000	1.2
+++ obuffer.h	28 Oct 2002 04:18:26 -0000	1.3
@@ -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




More information about the dovecot-cvs mailing list