static void * vbp_wrk_poll_backend(void *priv) { struct vbp_target *vt; THR_SetName("backend poll"); CAST_OBJ_NOTNULL(vt, priv, VBP_TARGET_MAGIC); while (!vt->stop) { AN(vt->req); assert(vt->req_len > 0); if (!vt->disable) { vbp_start_poke(vt); vbp_poke(vt); vbp_has_poked(vt); } if (!vt->stop) VTIM_sleep(vt->probe.interval); } Lck_Delete(&vt->mtx); VTAILQ_REMOVE(&vbp_list, vt, list); VBT_Rel(&vt->tcp_pool); free(vt->req); FREE_OBJ(vt); return (NULL); }
static void * wrk_thread_real(void *priv, unsigned thread_workspace) { struct worker *w, ww; unsigned char ws[thread_workspace]; THR_SetName("cache-worker"); w = &ww; memset(w, 0, sizeof *w); w->magic = WORKER_MAGIC; w->lastused = NAN; AZ(pthread_cond_init(&w->cond, NULL)); WS_Init(w->aws, "wrk", ws, thread_workspace); VSL(SLT_WorkThread, 0, "%p start", w); Pool_Work_Thread(priv, w); AZ(w->pool); VSL(SLT_WorkThread, 0, "%p end", w); if (w->vcl != NULL) VCL_Rel(&w->vcl); AZ(pthread_cond_destroy(&w->cond)); if (w->nvbo != NULL) VBO_Free(&w->nvbo); HSH_Cleanup(w); WRK_SumStat(w); return (NULL); }
void child_main(void) { setbuf(stdout, NULL); setbuf(stderr, NULL); printf("Child starts\n"); cache_param = heritage.param; AZ(pthread_key_create(&sp_key, NULL)); AZ(pthread_key_create(&name_key, NULL)); THR_SetName("cache-main"); VSM_Init(); /* First, LCK needs it. */ LCK_Init(); /* Second, locking */ WAIT_Init(); PAN_Init(); CLI_Init(); Fetch_Init(); CNT_Init(); VCL_Init(); HTTP_Init(); VDI_Init(); VBO_Init(); VBE_InitCfg(); VBP_Init(); WRK_Init(); Pool_Init(); EXP_Init(); HSH_Init(heritage.hash); BAN_Init(); VCA_Init(); SMS_Init(); SMP_Init(); STV_open(); VMOD_Init(); BAN_Compile(); /* Wait for persistent storage to load if asked to */ if (cache_param->diag_bitmap & 0x00020000) SMP_Ready(); CLI_Run(); STV_close(); printf("Child dies\n"); }
static void * pool_poolherder(void *priv) { unsigned nwq; VTAILQ_HEAD(,pool) pools = VTAILQ_HEAD_INITIALIZER(pools); struct pool *pp; uint64_t u; THR_SetName("pool_herder"); (void)priv; nwq = 0; while (1) { if (nwq < cache_param->wthread_pools) { pp = pool_mkpool(nwq); if (pp != NULL) { VTAILQ_INSERT_TAIL(&pools, pp, list); VSC_C_main->pools++; nwq++; continue; } } /* XXX: remove pools */ if (0) SES_DeletePool(NULL); (void)sleep(1); u = 0; VTAILQ_FOREACH(pp, &pools, list) u += pp->lqueue; VSC_C_main->thread_queue_len = u; } NEEDLESS_RETURN(NULL); }
static void * vca_kqueue_main(void *arg) { struct kevent ke[NKEV], *kp; int j, n, dotimer; double deadline; struct sess *sp; THR_SetName("cache-kqueue"); (void)arg; kq = kqueue(); assert(kq >= 0); j = 0; EV_SET(&ke[j], 0, EVFILT_TIMER, EV_ADD, 0, 100, NULL); j++; EV_SET(&ke[j], vca_pipes[0], EVFILT_READ, EV_ADD, 0, 0, vca_pipes); j++; AZ(kevent(kq, ke, j, NULL, 0, NULL)); nki = 0; while (1) { dotimer = 0; n = kevent(kq, ki, nki, ke, NKEV, NULL); assert(n >= 1 && n <= NKEV); nki = 0; for (kp = ke, j = 0; j < n; j++, kp++) { if (kp->filter == EVFILT_TIMER) { dotimer = 1; continue; } assert(kp->filter == EVFILT_READ); vca_kev(kp); } if (!dotimer) continue; /* * Make sure we have no pending changes for the fd's * we are about to close, in case the accept(2) in the * other thread creates new fd's betwen our close and * the kevent(2) at the top of this loop, the kernel * would not know we meant "the old fd of this number". */ vca_kq_flush(); deadline = TIM_real() - params->sess_timeout; for (;;) { sp = VTAILQ_FIRST(&sesshead); if (sp == NULL) break; if (sp->t_open > deadline) break; VTAILQ_REMOVE(&sesshead, sp, list); // XXX: not yet (void)TCP_linger(sp->fd, 0); vca_close_session(sp, "timeout"); SES_Delete(sp); } } }
static void * vwp_main(void *priv) { int v; struct vwp *vwp; struct waiter *w; struct waited *wp; double now, then; int i; THR_SetName("cache-poll"); CAST_OBJ_NOTNULL(vwp, priv, VWP_MAGIC); w = vwp->waiter; while (1) { then = Wait_HeapDue(w, &wp); if (wp == NULL) i = -1; else i = (int)floor(1e3 * (then - VTIM_real())); assert(vwp->hpoll > 0); AN(vwp->pollfd); v = poll(vwp->pollfd, vwp->hpoll, i); assert(v >= 0); now = VTIM_real(); if (vwp->pollfd[0].revents) v--; for (i = 1; i < vwp->hpoll;) { VSL(SLT_Debug, vwp->pollfd[i].fd, "POLL loop i=%d revents=0x%x", i, vwp->pollfd[i].revents); assert(vwp->pollfd[i].fd != vwp->pipes[0]); wp = vwp->idx[i]; CHECK_OBJ_NOTNULL(wp, WAITED_MAGIC); if (v == 0 && Wait_HeapDue(w, NULL) > now) break; if (vwp->pollfd[i].revents) v--; then = Wait_When(wp); if (then <= now) { Wait_HeapDelete(w, wp); Wait_Call(w, wp, WAITER_TIMEOUT, now); vwp_del(vwp, i); } else if (vwp->pollfd[i].revents & POLLIN) { assert(wp->fd > 0); assert(wp->fd == vwp->pollfd[i].fd); Wait_HeapDelete(w, wp); Wait_Call(w, wp, WAITER_ACTION, now); vwp_del(vwp, i); } else { i++; } } if (vwp->pollfd[0].revents) vwp_dopipe(vwp); } NEEDLESS_RETURN(NULL); }
static void * vsm_cleaner(void *priv) { (void)priv; THR_SetName("vsm_cleaner"); while (1) { AZ(pthread_mutex_lock(&vsm_mtx)); VSM_common_cleaner(heritage.vsm, VSC_C_main); AZ(pthread_mutex_unlock(&vsm_mtx)); VTIM_sleep(1.1); } NEEDLESS_RETURN(NULL); }
static void * vwe_thread(void *priv) { struct epoll_event ev[NEEV], *ep; struct sess *sp; char junk; double now, deadline; int dotimer, i, n; struct vwe *vwe; CAST_OBJ_NOTNULL(vwe, priv, VWE_MAGIC); THR_SetName("cache-epoll"); vwe->epfd = epoll_create(1); assert(vwe->epfd >= 0); vwe_modadd(vwe, vwe->pipes[0], vwe->pipes, EPOLL_CTL_ADD); vwe_modadd(vwe, vwe->timer_pipes[0], vwe->timer_pipes, EPOLL_CTL_ADD); while (1) { dotimer = 0; n = epoll_wait(vwe->epfd, ev, NEEV, -1); now = VTIM_real(); for (ep = ev, i = 0; i < n; i++, ep++) { if (ep->data.ptr == vwe->timer_pipes && (ep->events == EPOLLIN || ep->events == EPOLLPRI)) { assert(read(vwe->timer_pipes[0], &junk, 1)); dotimer = 1; } else vwe_eev(vwe, ep, now); } if (!dotimer) continue; /* check for timeouts */ deadline = now - cache_param->timeout_idle; for (;;) { sp = VTAILQ_FIRST(&vwe->sesshead); if (sp == NULL) break; if (sp->t_idle > deadline) break; VTAILQ_REMOVE(&vwe->sesshead, sp, list); // XXX: not yet VTCP_linger(sp->fd, 0); SES_Delete(sp, SC_RX_TIMEOUT, now); } } return (NULL); }
static void * vca_main(void *arg) { struct epoll_event ev[NEEV], *ep; struct sess *sp; char junk; double deadline; int dotimer, i, n; THR_SetName("cache-epoll"); (void)arg; epfd = epoll_create(1); assert(epfd >= 0); vca_modadd(vca_pipes[0], vca_pipes, EPOLL_CTL_ADD); vca_modadd(dotimer_pipe[0], dotimer_pipe, EPOLL_CTL_ADD); while (1) { dotimer = 0; n = epoll_wait(epfd, ev, NEEV, -1); for (ep = ev, i = 0; i < n; i++, ep++) { if (ep->data.ptr == dotimer_pipe && (ep->events == EPOLLIN || ep->events == EPOLLPRI)) { assert(read(dotimer_pipe[0], &junk, 1)); dotimer = 1; } else vca_eev(ep); } if (!dotimer) continue; /* check for timeouts */ deadline = TIM_real() - params->sess_timeout; for (;;) { sp = VTAILQ_FIRST(&sesshead); if (sp == NULL) break; if (sp->t_open > deadline) break; VTAILQ_REMOVE(&sesshead, sp, list); // XXX: not yet VTCP_linger(sp->fd, 0); vca_close_session(sp, "timeout"); SES_Delete(sp); } } return NULL; }
static void * vca_sess_timeout_ticker(void *arg) { char ticker = 'R'; THR_SetName("cache-epoll-sess_timeout_ticker"); (void)arg; while (1) { /* ticking */ assert(write(dotimer_pipe[1], &ticker, 1)); TIM_sleep(100 * 1e-3); } return NULL; }
static void * wrk_bgthread(void *arg) { struct bgthread *bt; struct worker wrk; CAST_OBJ_NOTNULL(bt, arg, BGTHREAD_MAGIC); THR_SetName(bt->name); INIT_OBJ(&wrk, WORKER_MAGIC); (void)bt->func(&wrk, bt->priv); WRONG("BgThread terminated"); NEEDLESS_RETURN(NULL); }
static void * vwe_timeout_idle_ticker(void *priv) { char ticker = 'R'; struct vwe *vwe; CAST_OBJ_NOTNULL(vwe, priv, VWE_MAGIC); THR_SetName("cache-epoll-timeout_idle_ticker"); while (1) { /* ticking */ assert(write(vwe->timer_pipes[1], &ticker, 1)); VTIM_sleep(100 * 1e-3); } return (NULL); }
static void * wrk_thread_real(void *priv, unsigned shm_workspace, unsigned sess_workspace, uint16_t nhttp, unsigned http_space, unsigned siov) { struct worker *w, ww; uint32_t wlog[shm_workspace / 4]; /* XXX: can we trust these to be properly aligned ? */ unsigned char ws[sess_workspace]; unsigned char http0[http_space]; unsigned char http1[http_space]; unsigned char http2[http_space]; struct iovec iov[siov]; struct SHA256Context sha256; THR_SetName("cache-worker"); w = &ww; memset(w, 0, sizeof *w); w->magic = WORKER_MAGIC; w->lastused = NAN; w->wlb = w->wlp = wlog; w->wle = wlog + (sizeof wlog) / 4; w->sha256ctx = &sha256; w->bereq = HTTP_create(http0, nhttp); w->beresp = HTTP_create(http1, nhttp); w->resp = HTTP_create(http2, nhttp); w->wrw.iov = iov; w->wrw.siov = siov; w->wrw.ciov = siov; AZ(pthread_cond_init(&w->cond, NULL)); WS_Init(w->ws, "wrk", ws, sess_workspace); VSL(SLT_WorkThread, 0, "%p start", w); Pool_Work_Thread(priv, w); AZ(w->pool); VSL(SLT_WorkThread, 0, "%p end", w); if (w->vcl != NULL) VCL_Rel(&w->vcl); AZ(pthread_cond_destroy(&w->cond)); HSH_Cleanup(w); WRK_SumStat(w); return (NULL); }
void WRK_Thread(struct pool *qp, size_t stacksize, unsigned thread_workspace) { struct worker *w, ww; unsigned char ws[thread_workspace]; uintptr_t u; AN(qp); AN(stacksize); AN(thread_workspace); THR_SetName("cache-worker"); w = &ww; INIT_OBJ(w, WORKER_MAGIC); w->lastused = NAN; AZ(pthread_cond_init(&w->cond, NULL)); WS_Init(w->aws, "wrk", ws, thread_workspace); u = getpagesize(); AN(u); u -= 1U; w->stack_start = (((uintptr_t)&qp) + u) & ~u; /* XXX: assuming stack grows down. */ w->stack_end = w->stack_start - stacksize; VSL(SLT_WorkThread, 0, "%p start", w); Pool_Work_Thread(qp, w); AZ(w->pool); VSL(SLT_WorkThread, 0, "%p end", w); if (w->vcl != NULL) VCL_Rel(&w->vcl); AZ(pthread_cond_destroy(&w->cond)); if (w->nbo != NULL) VBO_Free(&w->nbo); HSH_Cleanup(w); Pool_Sumstat(w); }
static void * wrk_bgthread(void *arg) { struct bgthread *bt; struct worker ww; struct sess *sp; uint32_t logbuf[1024]; /* XXX: size ? */ CAST_OBJ_NOTNULL(bt, arg, BGTHREAD_MAGIC); THR_SetName(bt->name); sp = SES_Alloc(); XXXAN(sp); memset(&ww, 0, sizeof ww); sp->wrk = &ww; ww.magic = WORKER_MAGIC; ww.wlp = ww.wlb = logbuf; ww.wle = logbuf + (sizeof logbuf) / 4; (void)bt->func(sp, bt->priv); WRONG("BgThread terminated"); NEEDLESS_RETURN(NULL); }
static void * vws_thread(void *priv) { struct waited *wp; struct waiter *w; struct vws *vws; double now, then; struct timespec ts; const double max_t = 100.0; port_event_t ev[MAX_EVENTS]; u_int nevents; int ei, ret; CAST_OBJ_NOTNULL(vws, priv, VWS_MAGIC); w = vws->waiter; CHECK_OBJ_NOTNULL(w, WAITER_MAGIC); THR_SetName("cache-ports"); now = VTIM_real(); while (!vws->die) { while (1) { then = Wait_HeapDue(w, &wp); if (wp == NULL) { vws->next = now + max_t; break; } else if (then > now) { vws->next = then; break; } CHECK_OBJ_NOTNULL(wp, WAITED_MAGIC); vws_del(vws, wp->fd); Wait_Call(w, wp, WAITER_TIMEOUT, now); } then = vws->next - now; ts.tv_sec = (time_t)floor(then); ts.tv_nsec = (long)(1e9 * (then - ts.tv_sec)); /* * min number of events we accept. could consider to scale up * for efficiency, but as we always get all waiting events up to * the maximum, we'd only optimize the idle case sacrificing * some latency */ nevents = 1; /* * see disucssion in * - https://issues.apache.org/bugzilla/show_bug.cgi?id=47645 * - http://mail.opensolaris.org/pipermail/\ * networking-discuss/2009-August/011979.html * * comment from apr/poll/unix/port.c : * * This confusing API can return an event at the same time * that it reports EINTR or ETIME. * */ ret = port_getn(vws->dport, ev, MAX_EVENTS, &nevents, &ts); now = VTIM_real(); if (ret < 0 && errno == EBADF) { /* close on dport is our stop signal */ AN(vws->die); break; } if (ret < 0) assert((errno == EINTR) || (errno == ETIME)); for (ei = 0; ei < nevents; ei++) vws_port_ev(vws, w, &ev[ei], now); } return NULL; }
static void * vwk_thread(void *priv) { struct vwk *vwk; struct kevent ke[NKEV], *kp; int j, n, dotimer; double now, deadline; struct sess *sp; CAST_OBJ_NOTNULL(vwk, priv, VWK_MAGIC); THR_SetName("cache-kqueue"); vwk->kq = kqueue(); assert(vwk->kq >= 0); j = 0; EV_SET(&ke[j], 0, EVFILT_TIMER, EV_ADD, 0, 100, NULL); j++; EV_SET(&ke[j], vwk->pipes[0], EVFILT_READ, EV_ADD, 0, 0, vwk->pipes); j++; AZ(kevent(vwk->kq, ke, j, NULL, 0, NULL)); vwk->nki = 0; while (1) { dotimer = 0; n = kevent(vwk->kq, vwk->ki, vwk->nki, ke, NKEV, NULL); now = VTIM_real(); assert(n <= NKEV); if (n == 0) { /* This happens on OSX in m00011.vtc */ dotimer = 1; (void)usleep(10000); } vwk->nki = 0; for (kp = ke, j = 0; j < n; j++, kp++) { if (kp->filter == EVFILT_TIMER) { dotimer = 1; } else if (kp->filter == EVFILT_READ && kp->udata == vwk->pipes) { vwk_pipe_ev(vwk, kp); } else { assert(kp->filter == EVFILT_READ); vwk_sess_ev(vwk, kp, now); } } if (!dotimer) continue; /* * Make sure we have no pending changes for the fd's * we are about to close, in case the accept(2) in the * other thread creates new fd's betwen our close and * the kevent(2) at the top of this loop, the kernel * would not know we meant "the old fd of this number". */ vwk_kq_flush(vwk); deadline = now - cache_param->timeout_idle; for (;;) { sp = VTAILQ_FIRST(&vwk->sesshead); if (sp == NULL) break; if (sp->t_idle > deadline) break; VTAILQ_REMOVE(&vwk->sesshead, sp, list); // XXX: not yet (void)VTCP_linger(sp->fd, 0); SES_Delete(sp, SC_RX_TIMEOUT, now); } } NEEDLESS_RETURN(NULL); }
static void * mpl_guard(void *priv) { struct mempool *mpl; struct memitem *mi = NULL; double __state_variable__(mpl_slp); double last = 0; CAST_OBJ_NOTNULL(mpl, priv, MEMPOOL_MAGIC); THR_SetName(mpl->name); mpl_slp = 0.15; // random while (1) { VTIM_sleep(mpl_slp); mpl_slp = 0.814; // random mpl->t_now = VTIM_real(); if (mi != NULL && (mpl->n_pool > mpl->param->max_pool || mi->size < *mpl->cur_size)) { FREE_OBJ(mi); mi = NULL; } if (mi == NULL && mpl->n_pool < mpl->param->min_pool) mi = mpl_alloc(mpl); if (mpl->n_pool < mpl->param->min_pool && mi != NULL) { /* can do */ } else if (mpl->n_pool > mpl->param->max_pool && mi == NULL) { /* can do */ } else if (!VTAILQ_EMPTY(&mpl->surplus)) { /* can do */ } else if (last + .1 * mpl->param->max_age < mpl->t_now) { /* should do */ } else if (mpl->self_destruct) { /* can do */ } else { continue; /* nothing to do */ } mpl_slp = 0.314; // random if (Lck_Trylock(&mpl->mtx)) continue; if (mpl->self_destruct) { AZ(mpl->live); while (1) { if (mi == NULL) { mi = VTAILQ_FIRST(&mpl->list); if (mi != NULL) { mpl->vsc->pool = --mpl->n_pool; VTAILQ_REMOVE(&mpl->list, mi, list); } } if (mi == NULL) { mi = VTAILQ_FIRST(&mpl->surplus); if (mi != NULL) VTAILQ_REMOVE(&mpl->surplus, mi, list); } if (mi == NULL) break; FREE_OBJ(mi); mi = NULL; } VSM_Free(mpl->vsc); Lck_Unlock(&mpl->mtx); Lck_Delete(&mpl->mtx); FREE_OBJ(mpl); break; } if (mpl->n_pool < mpl->param->min_pool && mi != NULL && mi->size >= *mpl->cur_size) { CHECK_OBJ_NOTNULL(mi, MEMITEM_MAGIC); mpl->vsc->pool = ++mpl->n_pool; mi->touched = mpl->t_now; VTAILQ_INSERT_HEAD(&mpl->list, mi, list); mi = NULL; mpl_slp = .01; // random } if (mpl->n_pool > mpl->param->max_pool && mi == NULL) { mi = VTAILQ_FIRST(&mpl->list); CHECK_OBJ_NOTNULL(mi, MEMITEM_MAGIC); mpl->vsc->pool = --mpl->n_pool; mpl->vsc->surplus++; VTAILQ_REMOVE(&mpl->list, mi, list); mpl_slp = .01; // random } if (mi == NULL) { mi = VTAILQ_FIRST(&mpl->surplus); if (mi != NULL) { CHECK_OBJ_NOTNULL(mi, MEMITEM_MAGIC); VTAILQ_REMOVE(&mpl->surplus, mi, list); mpl_slp = .01; // random } } if (mi == NULL && mpl->n_pool > mpl->param->min_pool) { mi = VTAILQ_LAST(&mpl->list, memhead_s); CHECK_OBJ_NOTNULL(mi, MEMITEM_MAGIC); if (mi->touched + mpl->param->max_age < mpl->t_now) { mpl->vsc->pool = --mpl->n_pool; mpl->vsc->timeout++; VTAILQ_REMOVE(&mpl->list, mi, list); mpl_slp = .01; // random } else { mi = NULL; last = mpl->t_now; } } else if (mpl->n_pool <= mpl->param->min_pool) { last = mpl->t_now; } Lck_Unlock(&mpl->mtx); if (mi != NULL) { FREE_OBJ(mi); mi = NULL; } } return (NULL); }
void child_main(void) { setbuf(stdout, NULL); setbuf(stderr, NULL); printf("Child starts\n"); cache_param = heritage.param; AZ(pthread_key_create(&req_key, NULL)); AZ(pthread_key_create(&bo_key, NULL)); AZ(pthread_key_create(&name_key, NULL)); THR_SetName("cache-main"); VSM_Init(); /* First, LCK needs it. */ LCK_Init(); /* Second, locking */ Lck_New(&vxid_lock, lck_vxid); CLI_Init(); PAN_Init(); VFP_Init(); VCL_Init(); HTTP_Init(); VBO_Init(); VBP_Init(); VBE_InitCfg(); Pool_Init(); V1P_Init(); EXP_Init(); HSH_Init(heritage.hash); BAN_Init(); VCA_Init(); SMP_Init(); STV_open(); VMOD_Init(); BAN_Compile(); VRND_Seed(); srand48(random()); CLI_AddFuncs(debug_cmds); /* Wait for persistent storage to load if asked to */ if (FEATURE(FEATURE_WAIT_SILO)) SMP_Ready(); CLI_Run(); BAN_Shutdown(); STV_close(); printf("Child dies\n"); }
static void * vwe_thread(void *priv) { struct epoll_event ev[NEEV], *ep; struct waited *wp; struct waiter *w; double now, then; int i, n, active; struct vwe *vwe; char c; CAST_OBJ_NOTNULL(vwe, priv, VWE_MAGIC); w = vwe->waiter; CHECK_OBJ_NOTNULL(w, WAITER_MAGIC); THR_SetName("cache-epoll"); THR_Init(); now = VTIM_real(); while (1) { while (1) { Lck_Lock(&vwe->mtx); /* * XXX: We could avoid many syscalls here if we were * XXX: allowed to just close the fd's on timeout. */ then = Wait_HeapDue(w, &wp); if (wp == NULL) { vwe->next = now + 100; break; } else if (then > now) { vwe->next = then; break; } CHECK_OBJ_NOTNULL(wp, WAITED_MAGIC); AZ(epoll_ctl(vwe->epfd, EPOLL_CTL_DEL, wp->fd, NULL)); vwe->nwaited--; AN(Wait_HeapDelete(w, wp)); Lck_Unlock(&vwe->mtx); Wait_Call(w, wp, WAITER_TIMEOUT, now); } then = vwe->next - now; i = (int)ceil(1e3 * then); assert(i > 0); Lck_Unlock(&vwe->mtx); do { /* Due to a linux kernel bug, epoll_wait can return EINTR when the process is subjected to ptrace or waking from OS suspend. */ n = epoll_wait(vwe->epfd, ev, NEEV, i); } while (n < 0 && errno == EINTR); assert(n >= 0); assert(n <= NEEV); now = VTIM_real(); for (ep = ev, i = 0; i < n; i++, ep++) { if (ep->data.ptr == vwe) { assert(read(vwe->pipe[0], &c, 1) == 1); continue; } CAST_OBJ_NOTNULL(wp, ep->data.ptr, WAITED_MAGIC); Lck_Lock(&vwe->mtx); active = Wait_HeapDelete(w, wp); Lck_Unlock(&vwe->mtx); if (!active) { VSL(SLT_Debug, wp->fd, "epoll: spurious event"); continue; } AZ(epoll_ctl(vwe->epfd, EPOLL_CTL_DEL, wp->fd, NULL)); vwe->nwaited--; if (ep->events & EPOLLIN) Wait_Call(w, wp, WAITER_ACTION, now); else if (ep->events & EPOLLERR) Wait_Call(w, wp, WAITER_REMCLOSE, now); else if (ep->events & EPOLLHUP) Wait_Call(w, wp, WAITER_REMCLOSE, now); else Wait_Call(w, wp, WAITER_REMCLOSE, now); } if (vwe->nwaited == 0 && vwe->die) break; } closefd(&vwe->pipe[0]); closefd(&vwe->pipe[1]); closefd(&vwe->epfd); return (NULL); }