Mercurial > dovecot > original-hg > dovecot-1.2
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"