static int event_select_on_epoll (struct event_pool *event_pool, int fd, int idx, int poll_in, int poll_out) { int ret = -1; struct event_slot_epoll *slot = NULL; struct epoll_event epoll_event = {0, }; struct event_data *ev_data = (void *)&epoll_event.data; GF_VALIDATE_OR_GOTO ("event", event_pool, out); slot = event_slot_get (event_pool, idx); assert (slot->fd == fd); LOCK (&slot->lock); { __slot_update_events (slot, poll_in, poll_out); epoll_event.events = slot->events; ev_data->idx = idx; ev_data->gen = slot->gen; if (slot->in_handler) /* in_handler indicates at least one thread executing event_dispatch_epoll_handler() which will perform epoll_ctl(EPOLL_CTL_MOD) anyways (because of EPOLLET) This not only saves a system call, but also avoids possibility of another epoll thread parallely picking up the next event while the ongoing handler is still in progress (and resulting in unnecessary contention on rpc_transport_t->mutex). */ goto unlock; ret = epoll_ctl (event_pool->fd, EPOLL_CTL_MOD, fd, &epoll_event); if (ret == -1) { gf_msg ("epoll", GF_LOG_ERROR, errno, LG_MSG_EPOLL_FD_MODIFY_FAILED, "failed to " "modify fd(=%d) events to %d", fd, epoll_event.events); } } unlock: UNLOCK (&slot->lock); event_slot_unref (event_pool, slot, idx); out: return idx; }
int event_register_epoll (struct event_pool *event_pool, int fd, event_handler_t handler, void *data, int poll_in, int poll_out) { int idx = -1; int ret = -1; int destroy = 0; struct epoll_event epoll_event = {0, }; struct event_data *ev_data = (void *)&epoll_event.data; struct event_slot_epoll *slot = NULL; GF_VALIDATE_OR_GOTO ("event", event_pool, out); /* TODO: Even with the below check, there is a possiblity of race, * What if the destroy mode is set after the check is done. * Not sure of the best way to prevent this race, ref counting * is one possibility. * There is no harm in registering and unregistering the fd * even after destroy mode is set, just that such fds will remain * open until unregister is called, also the events on that fd will be * notified, until one of the poller thread is alive. */ pthread_mutex_lock (&event_pool->mutex); { destroy = event_pool->destroy; } pthread_mutex_unlock (&event_pool->mutex); if (destroy == 1) goto out; idx = event_slot_alloc (event_pool, fd); if (idx == -1) { gf_log ("epoll", GF_LOG_ERROR, "could not find slot for fd=%d", fd); return -1; } slot = event_slot_get (event_pool, idx); assert (slot->fd == fd); LOCK (&slot->lock); { /* make epoll 'singleshot', which means we need to re-add the fd with epoll_ctl(EPOLL_CTL_MOD) after delivery of every single event. This assures us that while a poller thread has picked up and is processing an event, another poller will not try to pick this at the same time as well. */ slot->events = EPOLLPRI | EPOLLONESHOT; slot->handler = handler; slot->data = data; __slot_update_events (slot, poll_in, poll_out); epoll_event.events = slot->events; ev_data->idx = idx; ev_data->gen = slot->gen; ret = epoll_ctl (event_pool->fd, EPOLL_CTL_ADD, fd, &epoll_event); /* check ret after UNLOCK() to avoid deadlock in event_slot_unref() */ } UNLOCK (&slot->lock); if (ret == -1) { gf_log ("epoll", GF_LOG_ERROR, "failed to add fd(=%d) to epoll fd(=%d) (%s)", fd, event_pool->fd, strerror (errno)); event_slot_unref (event_pool, slot, idx); idx = -1; } /* keep slot->ref (do not event_slot_unref) if successful */ out: return idx; }