REGPRM3 static void _do_poll(struct poller *p, int exp, int wake) { int i; int wait_time; struct timespec timeout_ts; unsigned int nevlist; int fd, old_fd; int status; /* * Scan the list of file descriptors with an updated status: */ for (i = 0; i < fd_nbupdt; i++) { fd = fd_updt[i]; _HA_ATOMIC_AND(&fdtab[fd].update_mask, ~tid_bit); if (fdtab[fd].owner == NULL) { activity[tid].poll_drop++; continue; } _update_fd(fd); } fd_nbupdt = 0; /* Scan the global update list */ for (old_fd = fd = update_list.first; fd != -1; fd = fdtab[fd].update.next) { if (fd == -2) { fd = old_fd; continue; } else if (fd <= -3) fd = -fd -4; if (fd == -1) break; if (fdtab[fd].update_mask & tid_bit) done_update_polling(fd); else continue; if (!fdtab[fd].owner) continue; _update_fd(fd); } thread_harmless_now(); /* * Determine how long to wait for events to materialise on the port. */ wait_time = wake ? 0 : compute_poll_timeout(exp); tv_entering_poll(); activity_count_runtime(); do { int timeout = (global.tune.options & GTUNE_BUSY_POLLING) ? 0 : wait_time; int interrupted = 0; nevlist = 1; /* desired number of events to be retrieved */ timeout_ts.tv_sec = (timeout / 1000); timeout_ts.tv_nsec = (timeout % 1000) * 1000000; status = port_getn(evports_fd[tid], evports_evlist, evports_evlist_max, &nevlist, /* updated to the number of events retrieved */ &timeout_ts); if (status != 0) { int e = errno; switch (e) { case ETIME: /* * Though the manual page has not historically made it * clear, port_getn() can return -1 with an errno of * ETIME and still have returned some number of events. */ /* nevlist >= 0 */ break; default: nevlist = 0; interrupted = 1; break; } } tv_update_date(timeout, nevlist); if (nevlist || interrupted) break; if (timeout || !wait_time) break; if (signal_queue_len || wake) break; if (tick_isset(exp) && tick_is_expired(exp, now_ms)) break; } while(1); tv_leaving_poll(wait_time, nevlist); thread_harmless_end(); for (i = 0; i < nevlist; i++) { unsigned int n = 0; int events, rebind_events; fd = evports_evlist[i].portev_object; events = evports_evlist[i].portev_events; if (fdtab[fd].owner == NULL) { activity[tid].poll_dead++; continue; } if (!(fdtab[fd].thread_mask & tid_bit)) { activity[tid].poll_skip++; continue; } /* * By virtue of receiving an event for this file descriptor, it * is no longer associated with the port in question. Store * the previous event mask so that we may reassociate after * processing is complete. */ rebind_events = evports_state_to_events(fdtab[fd].state); /* rebind_events != 0 */ /* * Set bits based on the events we received from the port: */ if (events & POLLIN) n |= FD_POLL_IN; if (events & POLLOUT) n |= FD_POLL_OUT; if (events & POLLERR) n |= FD_POLL_ERR; if (events & POLLHUP) n |= FD_POLL_HUP; /* * Call connection processing callbacks. Note that it's * possible for this processing to alter the required event * port assocation; i.e., the "state" member of the "fdtab" * entry. If it changes, the fd will be placed on the updated * list for processing the next time we are called. */ fd_update_events(fd, n); /* * This file descriptor was closed during the processing of * polled events. No need to reassociate. */ if (fdtab[fd].owner == NULL) continue; /* * Reassociate with the port, using the same event mask as * before. This call will not result in a dissociation as we * asserted that _some_ events needed to be rebound above. * * Reassociating with the same mask allows us to mimic the * level-triggered behaviour of poll(2). In the event that we * are interested in the same events on the next turn of the * loop, this represents no extra work. * * If this additional port_associate(3C) call becomes a * performance problem, we would need to verify that we can * correctly interact with the file descriptor cache and update * list (see "src/fd.c") to avoid reassociating here, or to use * a different events mask. */ evports_resync_fd(fd, rebind_events); } }
/* * Linux epoll() poller */ REGPRM2 static void _do_poll(struct poller *p, int exp) { int status; int fd; int count; int updt_idx; int wait_time; int old_fd; /* first, scan the update list to find polling changes */ for (updt_idx = 0; updt_idx < fd_nbupdt; updt_idx++) { fd = fd_updt[updt_idx]; HA_ATOMIC_AND(&fdtab[fd].update_mask, ~tid_bit); if (!fdtab[fd].owner) { activity[tid].poll_drop++; continue; } _update_fd(fd); } fd_nbupdt = 0; /* Scan the global update list */ for (old_fd = fd = update_list.first; fd != -1; fd = fdtab[fd].update.next) { if (fd == -2) { fd = old_fd; continue; } else if (fd <= -3) fd = -fd -4; if (fd == -1) break; if (fdtab[fd].update_mask & tid_bit) done_update_polling(fd); else continue; if (!fdtab[fd].owner) continue; _update_fd(fd); } thread_harmless_now(); /* compute the epoll_wait() timeout */ if (!exp) wait_time = MAX_DELAY_MS; else if (tick_is_expired(exp, now_ms)) { activity[tid].poll_exp++; wait_time = 0; } else { wait_time = TICKS_TO_MS(tick_remain(now_ms, exp)) + 1; if (wait_time > MAX_DELAY_MS) wait_time = MAX_DELAY_MS; } /* now let's wait for polled events */ gettimeofday(&before_poll, NULL); status = epoll_wait(epoll_fd[tid], epoll_events, global.tune.maxpollevents, wait_time); tv_update_date(wait_time, status); measure_idle(); thread_harmless_end(); /* process polled events */ for (count = 0; count < status; count++) { unsigned int n; unsigned int e = epoll_events[count].events; fd = epoll_events[count].data.fd; if (!fdtab[fd].owner) { activity[tid].poll_dead++; continue; } if (!(fdtab[fd].thread_mask & tid_bit)) { /* FD has been migrated */ activity[tid].poll_skip++; epoll_ctl(epoll_fd[tid], EPOLL_CTL_DEL, fd, &ev); HA_ATOMIC_AND(&polled_mask[fd], ~tid_bit); continue; } /* it looks complicated but gcc can optimize it away when constants * have same values... In fact it depends on gcc :-( */ if (EPOLLIN == FD_POLL_IN && EPOLLOUT == FD_POLL_OUT && EPOLLPRI == FD_POLL_PRI && EPOLLERR == FD_POLL_ERR && EPOLLHUP == FD_POLL_HUP) { n = e & (EPOLLIN|EPOLLOUT|EPOLLPRI|EPOLLERR|EPOLLHUP); } else { n = ((e & EPOLLIN ) ? FD_POLL_IN : 0) | ((e & EPOLLPRI) ? FD_POLL_PRI : 0) | ((e & EPOLLOUT) ? FD_POLL_OUT : 0) | ((e & EPOLLERR) ? FD_POLL_ERR : 0) | ((e & EPOLLHUP) ? FD_POLL_HUP : 0); } /* always remap RDHUP to HUP as they're used similarly */ if (e & EPOLLRDHUP) { HA_ATOMIC_OR(&cur_poller.flags, HAP_POLL_F_RDHUP); n |= FD_POLL_HUP; } fd_update_events(fd, n); } /* the caller will take care of cached events */ }