}
}
-void io_loop_handle_remove(struct ioloop *ioloop, struct io_file *io)
+void io_loop_handle_remove(struct ioloop *ioloop, struct io_file *io,
+ bool closed)
{
struct ioloop_handler_context *ctx = ioloop->handler_context;
struct io_list **list;
list = array_idx_modifiable(&ctx->fd_index, io->fd);
last = ioloop_iolist_del(*list, io);
- memset(&event, 0, sizeof(event));
- event.data.ptr = *list;
- event.events = epoll_event_mask(*list);
+ if (!closed) {
+ memset(&event, 0, sizeof(event));
+ event.data.ptr = *list;
+ event.events = epoll_event_mask(*list);
- op = last ? EPOLL_CTL_DEL : EPOLL_CTL_MOD;
+ op = last ? EPOLL_CTL_DEL : EPOLL_CTL_MOD;
- if (epoll_ctl(ctx->epfd, op, io->fd, &event) < 0) {
- i_error("io_loop_handle_remove: epoll_ctl(%d, %d): %m",
- op, io->fd);
+ if (epoll_ctl(ctx->epfd, op, io->fd, &event) < 0) {
+ i_error("io_loop_handle_remove: epoll_ctl(%d, %d): %m",
+ op, io->fd);
+ }
}
-
if (last) {
/* since we're not freeing memory in any case, just increase
deleted counter so next handle_add() can just decrease it
/* I/O handler calls */
void io_loop_handle_add(struct ioloop *ioloop, struct io_file *io);
-void io_loop_handle_remove(struct ioloop *ioloop, struct io_file *io);
+void io_loop_handle_remove(struct ioloop *ioloop, struct io_file *io,
+ bool closed);
void io_loop_handler_init(struct ioloop *ioloop);
void io_loop_handler_deinit(struct ioloop *ioloop);
(void)array_append_space(&ctx->events);
}
-void io_loop_handle_remove(struct ioloop *ioloop, struct io_file *io)
+void io_loop_handle_remove(struct ioloop *ioloop, struct io_file *io,
+ bool closed)
{
struct ioloop_handler_context *ctx = ioloop->handler_context;
struct kevent ev;
- if ((io->io.condition & (IO_READ | IO_ERROR)) != 0) {
+ if ((io->io.condition & (IO_READ | IO_ERROR)) != 0 && !closed) {
MY_EV_SET(&ev, io->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0)
i_error("kevent(EV_DELETE, %d) failed: %m", io->fd);
}
- if ((io->io.condition & IO_WRITE) != 0) {
+ if ((io->io.condition & IO_WRITE) != 0 && !closed) {
MY_EV_SET(&ev, io->fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0)
i_error("kevent(EV_DELETE, %d) failed: %m", io->fd);
ctx->fds[index].events |= IO_POLL_ERROR;
}
-void io_loop_handle_remove(struct ioloop *ioloop, struct io_file *io)
+void io_loop_handle_remove(struct ioloop *ioloop, struct io_file *io,
+ bool closed ATTR_UNUSED)
{
struct ioloop_handler_context *ctx = ioloop->handler_context;
enum io_condition condition = io->io.condition;
i_assert(index >= 0 && (unsigned int) index < ctx->fds_count);
#ifdef DEBUG
- /* io_remove() is required to be called before fd is closed.
- This is required by kqueue, but since poll is more commonly used
- while developing, this check here should catch the error early
- enough not to cause problems for kqueue users. */
- if (fcntl(io->fd, F_GETFD, 0) < 0) {
- if (errno == EBADF)
- i_panic("io_remove(%d) called too late", io->fd);
- else
- i_error("fcntl(%d, F_GETFD) failed: %m", io->fd);
+ if (!closed) {
+ /* io_remove() is required to be called before fd is closed.
+ This is required by epoll/kqueue, but since poll is more
+ commonly used while developing, this check here should catch
+ the error early enough not to cause problems for kqueue
+ users. */
+ if (fcntl(io->fd, F_GETFD, 0) < 0) {
+ if (errno == EBADF)
+ i_panic("io_remove(%d) called too late", io->fd);
+ else
+ i_error("fcntl(%d, F_GETFD) failed: %m", io->fd);
+ }
}
#endif
i_free(io);
ctx->highest_fd = io->fd;
}
-void io_loop_handle_remove(struct ioloop *ioloop, struct io_file *io)
+void io_loop_handle_remove(struct ioloop *ioloop, struct io_file *io,
+ bool closed ATTR_UNUSED)
{
struct ioloop_handler_context *ctx = ioloop->handler_context;
enum io_condition condition = io->io.condition;
current_ioloop->next_io_file = io->next;
}
-void io_remove(struct io **_io)
+static void io_remove_full(struct io **_io, bool closed)
{
struct io *io = *_io;
struct io_file *io_file = (struct io_file *)io;
io_file_unlink(io_file);
- io_loop_handle_remove(current_ioloop, io_file);
+ io_loop_handle_remove(current_ioloop, io_file, closed);
}
}
+void io_remove(struct io **io)
+{
+ io_remove_full(io, FALSE);
+}
+
+void io_remove_closed(struct io **io)
+{
+ i_assert(((*io)->condition & IO_NOTIFY) == 0);
+
+ io_remove_full(io, TRUE);
+}
+
static void timeout_update_next(struct timeout *timeout, struct timeval *tv_now)
{
if (tv_now == NULL) {
/* Remove I/O handler, and set io pointer to NULL. */
void io_remove(struct io **io);
+/* Like io_remove(), but assume that the file descriptor is already closed.
+ With some backends this simply frees the memory. */
+void io_remove_closed(struct io **io);
/* Timeout handlers */
struct timeout *timeout_add(unsigned int msecs, timeout_callback_t *callback,