Mercurial > dovecot > core-2.2
changeset 164:2660a5684515 HEAD
Added io_buffer_read_blocking() which can be used to read data blockingly,
but with a specified timeout. Client fds are now nonblocking and use it with
APPEND fixing a possible infinite wait if client didn't send any data.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 06 Sep 2002 20:27:11 +0300 |
parents | aeb4e8d48aa2 |
children | 628b12562e4b |
files | src/imap/client.c src/lib-storage/index/index-save.c src/lib/iobuffer.c src/lib/iobuffer.h |
diffstat | 4 files changed, 129 insertions(+), 35 deletions(-) [+] |
line wrap: on
line diff
--- a/src/imap/client.c Fri Sep 06 18:11:31 2002 +0300 +++ b/src/imap/client.c Fri Sep 06 20:27:11 2002 +0300 @@ -1,6 +1,7 @@ /* Copyright (C) 2002 Timo Sirainen */ #include "common.h" +#include "network.h" #include "iobuffer.h" #include "commands.h" @@ -12,6 +13,10 @@ /* If we can't send a buffer in a minute, disconnect the client */ #define CLIENT_OUTPUT_TIMEOUT (60*1000) +/* If we don't soon receive expected data from client while processing + a command, disconnect the client */ +#define CLIENT_CMDINPUT_TIMEOUT CLIENT_OUTPUT_TIMEOUT + /* Disconnect client when it sends too many bad commands */ #define CLIENT_MAX_BAD_COMMANDS 20 @@ -23,12 +28,25 @@ static void client_input(Client *client); -static void client_send_timeout(Client *client) +static void client_output_timeout(void *context, + Timeout timeout __attr_unused__) { + Client *client = context; + io_buffer_close(client->inbuf); io_buffer_close(client->outbuf); } +static void client_input_timeout(void *context, + Timeout timeout __attr_unused__) +{ + Client *client = context; + + client_send_line(my_client, "* BYE Disconnected for inactivity " + "while waiting for command data."); + io_buffer_close(client->outbuf); +} + Client *client_create(int hin, int hout, int socket, MailStorage *storage) { Client *client; @@ -39,9 +57,18 @@ MAX_INBUF_SIZE); client->outbuf = io_buffer_create(hout, default_pool, 0, 0); - io_buffer_set_send_blocking(client->outbuf, 4096, - CLIENT_OUTPUT_TIMEOUT, - (TimeoutFunc) client_send_timeout, client); + /* always use nonblocking I/O */ + net_set_nonblock(hin, TRUE); + net_set_nonblock(hout, TRUE); + + /* set timeout for sending data */ + io_buffer_set_blocking(client->outbuf, 4096, CLIENT_OUTPUT_TIMEOUT, + client_output_timeout, client); + + /* set timeout for reading expected data (eg. APPEND). This is + different from the actual idle time. */ + io_buffer_set_blocking(client->inbuf, 0, CLIENT_CMDINPUT_TIMEOUT, + client_input_timeout, client); client->inbuf->file = !socket; client->outbuf->file = !socket;
--- a/src/lib-storage/index/index-save.c Fri Sep 06 18:11:31 2002 +0300 +++ b/src/lib-storage/index/index-save.c Fri Sep 06 20:27:11 2002 +0300 @@ -47,9 +47,7 @@ last_cr = FALSE; while (data_size > 0) { - /* FIXME: we're using nonblocking I/O, meaning if there's no - data we'll eat all CPU! */ - ret = io_buffer_read(buf); + ret = io_buffer_read_blocking(buf, (unsigned int)-1); if (ret < 0) { mail_storage_set_critical(storage, "Error reading mail: %m");
--- a/src/lib/iobuffer.c Fri Sep 06 18:11:31 2002 +0300 +++ b/src/lib/iobuffer.c Fri Sep 06 20:27:11 2002 +0300 @@ -32,6 +32,18 @@ #include <unistd.h> +typedef struct { + IOLoop ioloop; + IOBuffer *outbuf; + + const char *data; + unsigned int size; + IOBuffer *inbuf; + + int timeout; + int last_block; +} IOBufferBlockContext; + static unsigned int mmap_pagesize = 0; static unsigned int mmap_pagemask = 0; @@ -183,18 +195,17 @@ buf->max_buffer_size = max_size; } -void io_buffer_set_send_blocking(IOBuffer *buf, unsigned int max_size, - int timeout_msecs, TimeoutFunc timeout_func, - void *context) +void io_buffer_set_blocking(IOBuffer *buf, unsigned int max_size, + int timeout_msecs, TimeoutFunc timeout_func, + void *context) { - i_assert(!buf->receive); - - buf->transmit = TRUE; buf->timeout_msecs = timeout_msecs; buf->timeout_func = timeout_func; buf->timeout_context = context; buf->blocking = max_size > 0; - buf->max_buffer_size = max_size; + + if (max_size != 0) + buf->max_buffer_size = max_size; } static int my_write(int fd, const void *buf, unsigned int size) @@ -263,18 +274,6 @@ return TRUE; } -typedef struct { - IOLoop ioloop; - IOBuffer *outbuf; - - const char *data; - unsigned int size; - IOBuffer *inbuf; - - int timeout; - int last_block; -} IOBufferBlockContext; - static void block_loop_send(IOBufferBlockContext *ctx) { int ret; @@ -301,6 +300,8 @@ io_loop_stop(ctx->ioloop); } +/* this can be called with both io_buffer_ioloop() or + io_buffer_read_blocking() */ static void block_loop_timeout(void *context, Timeout timeout __attr_unused__) { IOBufferBlockContext *ctx = context; @@ -660,6 +661,11 @@ return buf->buffer_size; } +int io_buffer_read(IOBuffer *buf) +{ + return io_buffer_read_max(buf, UINT_MAX); +} + int io_buffer_read_max(IOBuffer *buf, unsigned int size) { int ret; @@ -714,9 +720,58 @@ return ret; } -int io_buffer_read(IOBuffer *buf) +static void io_read_data(void *context, int fd __attr_unused__, + IO io __attr_unused__) +{ + IOBufferBlockContext *ctx = context; + + if (io_buffer_read_max(ctx->inbuf, ctx->size) != 0) { + /* got data / error */ + io_loop_stop(ctx->ioloop); + } +} + +int io_buffer_read_blocking(IOBuffer *buf, unsigned int size) { - return io_buffer_read_max(buf, UINT_MAX); + IOBufferBlockContext ctx; + Timeout to; + int ret; + + /* first check if we can get some data */ + ret = io_buffer_read_max(buf, size); + if (ret != 0) + return ret; + + /* blocking now */ + + /* create a new I/O loop */ + memset(&ctx, 0, sizeof(ctx)); + ctx.ioloop = io_loop_create(); + ctx.inbuf = buf; + ctx.size = size; + + buf->io = io_add(buf->fd, IO_READ, io_read_data, &ctx); + to = buf->timeout_msecs <= 0 ? NULL : + timeout_add(buf->timeout_msecs, block_loop_timeout, &ctx); + + io_loop_run(ctx.ioloop); + + if (buf->io != NULL) { + io_remove(buf->io); + buf->io = NULL; + } + + if (to != NULL) { + if (ctx.timeout && buf->timeout_func != NULL) { + /* call user-given timeout function */ + buf->timeout_func(buf->timeout_context, to); + } + timeout_remove(to); + } + + io_loop_destroy(ctx.ioloop); + + return buf->pos > buf->skip ? 1 : -1; } void io_buffer_skip(IOBuffer *buf, uoff_t size)
--- a/src/lib/iobuffer.h Fri Sep 06 18:11:31 2002 +0300 +++ b/src/lib/iobuffer.h Fri Sep 06 20:27:11 2002 +0300 @@ -70,13 +70,22 @@ IOBuffer *io_buffer_set_pool(IOBuffer *buf, Pool pool); /* Change the maximum size for buffer to grow. */ void io_buffer_set_max_size(IOBuffer *buf, unsigned int max_size); -/* Change output buffer's blocking state. When buffer reaches max_size, - it will block until all the data has been sent or timeout has been - reached. Setting max_size to 0 disables this (default). Setting - timeout_msecs to 0 may block infinitely. */ -void io_buffer_set_send_blocking(IOBuffer *buf, unsigned int max_size, - int timeout_msecs, TimeoutFunc timeout_func, - void *context); +/* Change buffer's blocking state. The blocking state in fd itself isn't + changed, and it's not needed to be blocking. This affects two things: + + When buffer reaches max_size, it will block until all the data has been + sent or timeout has been reached. Setting max_size to 0 disables this + (default). Setting timeout_msecs to 0 may block infinitely, or until + socket is closed. + + Sets the timeout for io_buffer_read_blocking(). If max_size is non-zero, + it acts the same as io_buffer_set_max_size(). + + timeout_func is called with both cases when timeout occurs. +*/ +void io_buffer_set_blocking(IOBuffer *buf, unsigned int max_size, + int timeout_msecs, TimeoutFunc timeout_func, + void *context); /* Set TCP_CORK on if supported, ie. don't send out partial frames. io_buffer_send_flush() removes the cork. */ @@ -103,6 +112,11 @@ int io_buffer_read(IOBuffer *buf); /* Like io_buffer_read(), but don't read more than specified size. */ int io_buffer_read_max(IOBuffer *buf, unsigned int size); +/* Blocking read, doesn't return until at least one byte is read, or until + socket is disconnected or timeout has occured. Note that the fd must be + nonblocking, or the timeout doesn't work. If you don't want limit size, + set it to (unsigned int)-1. Returns 1 if data was found, -1 if error. */ +int io_buffer_read_blocking(IOBuffer *buf, unsigned int size); /* Skip forward a number of bytes */ void io_buffer_skip(IOBuffer *buf, uoff_t size); /* Seek to specified position from beginning of file. This works only for