static inline void vws_port_ev(struct vws *vws, port_event_t *ev) { struct sess *sp; if(ev->portev_source == PORT_SOURCE_USER) { CAST_OBJ_NOTNULL(sp, ev->portev_user, SESS_MAGIC); assert(sp->fd >= 0); AZ(sp->obj); VTAILQ_INSERT_TAIL(&vws->sesshead, sp, list); vws_add(vws, sp->fd, sp); } else { int i; assert(ev->portev_source == PORT_SOURCE_FD); CAST_OBJ_NOTNULL(sp, ev->portev_user, SESS_MAGIC); assert(sp->fd >= 0); if(ev->portev_events & POLLERR) { vws_del(vws, sp->fd); VTAILQ_REMOVE(&vws->sesshead, sp, list); SES_Delete(sp, "EOF"); return; } i = HTC_Rx(sp->htc); if (i == 0) { /* incomplete header, wait for more data */ vws_add(vws, sp->fd, sp); return; } /* * note: the original man page for port_associate(3C) states: * * When an event for a PORT_SOURCE_FD object is retrieved, * the object no longer has an association with the port. * * This can be read along the lines of sparing the * port_dissociate after port_getn(), but in fact, * port_dissociate should be used * * Ref: http://opensolaris.org/jive/thread.jspa?threadID=129476&tstart=0 */ vws_del(vws, sp->fd); VTAILQ_REMOVE(&vws->sesshead, sp, list); /* SES_Handle will also handle errors */ SES_Handle(sp, i); } return; }
static inline void vws_port_ev(struct vws *vws, struct waiter *w, port_event_t *ev, double now) { struct waited *wp; if(ev->portev_source == PORT_SOURCE_USER) { CAST_OBJ_NOTNULL(wp, ev->portev_user, WAITED_MAGIC); assert(wp->fd >= 0); vws->nwaited++; Wait_HeapInsert(vws->waiter, wp); vws_add(vws, wp->fd, wp); } else { assert(ev->portev_source == PORT_SOURCE_FD); CAST_OBJ_NOTNULL(wp, ev->portev_user, WAITED_MAGIC); assert(wp->fd >= 0); vws->nwaited--; /* * port_getn does not implicitly disassociate * * Ref: http://opensolaris.org/jive/thread.jspa?\ * threadID=129476&tstart=0 */ vws_del(vws, wp->fd); Wait_Call(w, wp, ev->portev_events & POLLERR ? WAITER_REMCLOSE : WAITER_ACTION, now); } return; }
static void * vws_thread(void *priv) { struct sess *sp; struct vws *vws; CAST_OBJ_NOTNULL(vws, priv, VWS_MAGIC); /* * timeouts: * * min_ts : Minimum timeout for port_getn * min_t : ^ equivalent in floating point representation * * max_ts : Maximum timeout for port_getn * max_t : ^ equivalent in floating point representation * * with (nevents == 1), we should always choose the correct port_getn * timeout to check session timeouts, so max is just a safety measure * (if this implementation is correct, it could be set to an "infinte" * value) * * with (nevents > 1), min and max define the acceptable range for * - additional latency of keep-alive connections and * - additional tolerance for handling session timeouts * */ static struct timespec min_ts = {0L, 100L /*ms*/ * 1000L /*us*/ * 1000L /*ns*/}; static double min_t = 0.1; /* 100 ms*/ static struct timespec max_ts = {1L, 0L}; /* 1 second */ static double max_t = 1.0; /* 1 second */ /* XXX: These should probably go in vws ? */ struct timespec ts; struct timespec *timeout; vws->dport = port_create(); assert(vws->dport >= 0); timeout = &max_ts; while (1) { port_event_t ev[MAX_EVENTS]; int nevents, ei, ret; double now, deadline; /* * XXX Do we want to scale this up dynamically to increase * efficiency in high throughput situations? - would need to * start with one to keep latency low at any rate * * Note: when increasing nevents, we must lower min_ts * and max_ts */ 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, timeout); if (ret < 0) assert((errno == EINTR) || (errno == ETIME)); for (ei = 0; ei < nevents; ei++) vws_port_ev(vws, ev + ei); /* check for timeouts */ now = VTIM_real(); deadline = now - cache_param->sess_timeout; /* * This loop assumes that the oldest sessions are always at the * beginning of the list (which is the case if we guarantee to * enqueue at the tail only * */ for (;;) { sp = VTAILQ_FIRST(&vws->sesshead); if (sp == NULL) break; if (sp->t_open > deadline) { break; } VTAILQ_REMOVE(&vws->sesshead, sp, list); if(sp->fd != -1) { vws_del(vws, sp->fd); } SES_Delete(sp, "timeout"); } /* * Calculate the timeout for the next get_portn */ if (sp) { double tmo = (sp->t_open + cache_param->sess_timeout) - now; /* we should have removed all sps whose timeout has passed */ assert(tmo > 0.0); if (tmo < min_t) { timeout = &min_ts; } else if (tmo > max_t) { timeout = &max_ts; } else { ts = VTIM_timespec(tmo); timeout = &ts; } } else { timeout = &max_ts; } } }
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; }