diff options
Diffstat (limited to 'lib/tsocket')
-rw-r--r-- | lib/tsocket/tsocket_bsd.c | 121 |
1 files changed, 116 insertions, 5 deletions
diff --git a/lib/tsocket/tsocket_bsd.c b/lib/tsocket/tsocket_bsd.c index 72499561f2d..daba33eb9d7 100644 --- a/lib/tsocket/tsocket_bsd.c +++ b/lib/tsocket/tsocket_bsd.c @@ -1754,6 +1754,9 @@ struct tstream_bsd { void (*readable_handler)(void *private_data); void *writeable_private; void (*writeable_handler)(void *private_data); + + struct tevent_context *error_ctx; + struct tevent_timer *error_timer; }; bool tstream_bsd_optimize_readv(struct tstream_context *stream, @@ -1775,6 +1778,28 @@ bool tstream_bsd_optimize_readv(struct tstream_context *stream, return old; } +static void tstream_bsd_error_timer(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct tstream_bsd *bsds = + talloc_get_type(private_data, + struct tstream_bsd); + + TALLOC_FREE(bsds->error_timer); + + /* + * Turn on TEVENT_FD_READABLE() again + * if we have a writeable_handler that + * wants to monitor the connection + * for errors. + */ + if (bsds->writeable_handler != NULL) { + TEVENT_FD_READABLE(bsds->fde); + } +} + static void tstream_bsd_fde_handler(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, @@ -1789,11 +1814,74 @@ static void tstream_bsd_fde_handler(struct tevent_context *ev, } if (flags & TEVENT_FD_READ) { if (!bsds->readable_handler) { - if (bsds->writeable_handler) { + struct timeval recheck_time; + + /* + * In order to avoid cpu-spinning + * we no longer want to get TEVENT_FD_READ + */ + TEVENT_FD_NOT_READABLE(bsds->fde); + + if (!bsds->writeable_handler) { + return; + } + + /* + * If we have a writeable handler we + * want that to report connection errors + * early. + * + * So we check if the socket is in an + * error state. + */ + if (bsds->error == 0) { + int ret = tsocket_bsd_error(bsds->fd); + + if (ret == -1) { + bsds->error = errno; + } + } + + if (bsds->error != 0) { + /* + * Let the writeable handler report the error + */ + bsds->writeable_handler(bsds->writeable_private); + return; + } + + /* + * Here we called TEVENT_FD_NOT_READABLE() without + * calling into the writeable handler. + * + * So we may have to wait for the kernels tcp stack + * to report TEVENT_FD_WRITE in order to let + * make progress and turn on TEVENT_FD_READABLE() + * again. + * + * As a fallback we use a timer that turns on + * TEVENT_FD_READABLE() again after a timeout of + * 1 second. + */ + + if (bsds->error_timer != NULL) { + return; + } + + recheck_time = timeval_current_ofs(1, 0); + bsds->error_timer = tevent_add_timer(bsds->error_ctx, + bsds, + recheck_time, + tstream_bsd_error_timer, + bsds); + if (bsds->error_timer == NULL) { + bsds->error = ENOMEM; + /* + * Let the writeable handler report the error + */ bsds->writeable_handler(bsds->writeable_private); return; } - TEVENT_FD_NOT_READABLE(bsds->fde); return; } bsds->readable_handler(bsds->readable_private); @@ -1848,6 +1936,8 @@ static int tstream_bsd_set_readable_handler(struct tstream_bsd *bsds, TEVENT_FD_READABLE(bsds->fde); } + TALLOC_FREE(bsds->error_timer); + bsds->readable_handler = handler; bsds->readable_private = private_data; @@ -1870,7 +1960,8 @@ static int tstream_bsd_set_writeable_handler(struct tstream_bsd *bsds, bsds->writeable_handler = NULL; bsds->writeable_private = NULL; TEVENT_FD_NOT_WRITEABLE(bsds->fde); - + TALLOC_FREE(bsds->error_timer); + bsds->error_ctx = NULL; return 0; } @@ -1882,6 +1973,8 @@ static int tstream_bsd_set_writeable_handler(struct tstream_bsd *bsds, } bsds->event_ptr = NULL; TALLOC_FREE(bsds->fde); + TALLOC_FREE(bsds->error_timer); + bsds->error_ctx = NULL; } if (tevent_fd_get_flags(bsds->fde) == 0) { @@ -1907,6 +2000,7 @@ static int tstream_bsd_set_writeable_handler(struct tstream_bsd *bsds, bsds->writeable_handler = handler; bsds->writeable_private = private_data; + bsds->error_ctx = ev; return 0; } @@ -2212,7 +2306,14 @@ static void tstream_bsd_writev_handler(void *private_data) } err = tsocket_bsd_error_from_errno(ret, errno, &retry); if (retry) { - /* retry later */ + /* + * retry later... + * + * make sure we also wait readable again + * in order to notice errors early + */ + TEVENT_FD_READABLE(bsds->fde); + TALLOC_FREE(bsds->error_timer); return; } if (err != 0) { @@ -2238,7 +2339,13 @@ static void tstream_bsd_writev_handler(void *private_data) } if (state->count > 0) { - /* we have more to read */ + /* + * we have more to write + * + * make sure we also wait readable again + * in order to notice errors early + */ + TEVENT_FD_READABLE(bsds->fde); return; } @@ -2286,6 +2393,8 @@ static struct tevent_req *tstream_bsd_disconnect_send(TALLOC_CTX *mem_ctx, goto post; } + TALLOC_FREE(bsds->error_timer); + bsds->error_ctx = NULL; TALLOC_FREE(bsds->fde); ret = close(bsds->fd); bsds->fd = -1; @@ -2328,6 +2437,8 @@ static const struct tstream_context_ops tstream_bsd_ops = { static int tstream_bsd_destructor(struct tstream_bsd *bsds) { + TALLOC_FREE(bsds->error_timer); + bsds->error_ctx = NULL; TALLOC_FREE(bsds->fde); if (bsds->fd != -1) { close(bsds->fd); |