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); }
ban_lurker(struct worker *wrk, void *priv) { struct vsl_log vsl; volatile double d; unsigned gen = ban_generation + 1; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); AZ(priv); VSL_Setup(&vsl, NULL, 0); while (!ban_shutdown) { d = ban_lurker_work(wrk, &vsl); ban_cleantail(); if (DO_DEBUG(DBG_LURKER)) VSLb(&vsl, SLT_Debug, "lurker: sleep = %lf", d); d += VTIM_real(); Lck_Lock(&ban_mtx); if (gen == ban_generation) { (void)Lck_CondWait(&ban_lurker_cond, &ban_mtx, d); ban_batch = 0; } gen = ban_generation; Lck_Unlock(&ban_mtx); } pthread_exit(0); NEEDLESS_RETURN(NULL); }
vbp_thread(struct worker *wrk, void *priv) { double now, nxt; struct vbp_target *vt; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); AZ(priv); Lck_Lock(&vbp_mtx); while (1) { now = VTIM_real(); vt = binheap_root(vbp_heap); if (vt == NULL) { nxt = 8.192 + now; (void)Lck_CondWait(&vbp_cond, &vbp_mtx, nxt); } else if (vt->due > now) { nxt = vt->due; vt = NULL; (void)Lck_CondWait(&vbp_cond, &vbp_mtx, nxt); } else { binheap_delete(vbp_heap, vt->heap_idx); vt->due = now + vt->interval; if (!vt->running) { vt->running = 1; vt->task.func = vbp_task; vt->task.priv = vt; if (Pool_Task_Any(&vt->task, TASK_QUEUE_REQ)) vt->running = 0; } binheap_insert(vbp_heap, vt); } } Lck_Unlock(&vbp_mtx); NEEDLESS_RETURN(NULL); }
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 FILE * getdst(enum ocx_chan chan) { if (chan == OCX_DIAG) return (stderr); if (chan == OCX_TRACE) return (tracefile); if (chan == OCX_DEBUG) return (stdout); WRONG("Wrong ocx_chan"); 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 * server_bgthread(struct worker *wrk, void *priv) { struct vmod_fsdirector_file_system *fs; struct sockaddr_storage addr_s; socklen_t len; struct http_conn *htc; int fd; enum htc_status_e htc_status; CAST_OBJ_NOTNULL(fs, priv, VMOD_FSDIRECTOR_MAGIC); assert(fs->sock >= 0); htc = &fs->htc; fs->wrk = wrk; WS_Init(wrk->aws, fs->ws_name, malloc(WS_LEN), WS_LEN); while (1) { do { fd = accept(fs->sock, (void*)&addr_s, &len); } while (fd < 0 && errno == EAGAIN); if (fd < 0) { continue; } HTTP1_Init(htc, wrk->aws, fd, NULL, HTTP1_BUF, HTTP1_MAX_HDR); htc_status = HTTP1_Rx(htc); switch (htc_status) { case HTTP1_OVERFLOW: case HTTP1_ERROR_EOF: case HTTP1_ALL_WHITESPACE: case HTTP1_NEED_MORE: prepare_answer(htc, 400); prepare_body(htc); break; case HTTP1_COMPLETE: answer_appropriate(fs); break; } WS_Reset(wrk->aws, NULL); close(fd); } pthread_exit(0); NEEDLESS_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 * 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 enum fetch_step vbf_stp_done(void) { WRONG("Just plain wrong"); NEEDLESS_RETURN(F_STP_NOTYET); }
static enum fetch_step vbf_stp_notyet(void) { WRONG("Patience, grashopper, patience..."); NEEDLESS_RETURN(F_STP_NOTYET); }
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* pool_herder(void *priv) { struct pool *pp; struct pool_task *pt; double t_idle; struct worker *wrk; CAST_OBJ_NOTNULL(pp, priv, POOL_MAGIC); while (1) { /* Make more threads if needed and allowed */ if (pp->nthr < cache_param->wthread_min || (pp->dry && pp->nthr < cache_param->wthread_max)) { pool_breed(pp); continue; } assert(pp->nthr >= cache_param->wthread_min); if (pp->nthr > cache_param->wthread_min) { t_idle = VTIM_real() - cache_param->wthread_timeout; Lck_Lock(&pp->mtx); /* XXX: unsafe counters */ VSC_C_main->sess_queued += pp->nqueued; VSC_C_main->sess_dropped += pp->ndropped; pp->nqueued = pp->ndropped = 0; wrk = NULL; pt = VTAILQ_LAST(&pp->idle_queue, taskhead); if (pt != NULL) { AZ(pt->func); CAST_OBJ_NOTNULL(wrk, pt->priv, WORKER_MAGIC); if (wrk->lastused < t_idle || pp->nthr > cache_param->wthread_max) { /* Give it a kiss on the cheek... */ VTAILQ_REMOVE(&pp->idle_queue, &wrk->task, list); wrk->task.func = pool_kiss_of_death; AZ(pthread_cond_signal(&wrk->cond)); } else wrk = NULL; } Lck_Unlock(&pp->mtx); if (wrk != NULL) { pp->nthr--; Lck_Lock(&pool_mtx); VSC_C_main->threads--; VSC_C_main->threads_destroyed++; Lck_Unlock(&pool_mtx); VTIM_sleep(cache_param->wthread_destroy_delay); continue; } } Lck_Lock(&pp->mtx); if (!pp->dry) { (void)Lck_CondWait(&pp->herder_cond, &pp->mtx, VTIM_real() + 5); } else { /* XXX: unsafe counters */ VSC_C_main->threads_limited++; pp->dry = 0; } Lck_Unlock(&pp->mtx); } NEEDLESS_RETURN(NULL); }
static void * vca_main(void *arg) { struct sess *sp; /* * 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 */ struct timespec ts; struct timespec *timeout; (void)arg; solaris_dport = port_create(); assert(solaris_dport >= 0); timeout = &max_ts; while (1) { port_event_t ev[MAX_EVENTS]; uint_t nevents; int 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(solaris_dport, ev, MAX_EVENTS, &nevents, timeout); if (ret < 0) assert((errno == EINTR) || (errno == ETIME)); for (ei=0; ei<nevents; ei++) { vca_port_ev(ev + ei); } /* check for timeouts */ now = TIM_real(); deadline = now - params->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(&sesshead); if (sp == NULL) break; if (sp->t_open > deadline) { break; } VTAILQ_REMOVE(&sesshead, sp, list); if(sp->fd != -1) { vca_del(sp->fd); } vca_close_session(sp, "timeout"); SES_Delete(sp); } /* * Calculate the timeout for the next get_portn */ if (sp) { double tmo = (sp->t_end + params->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 = TIM_timespec(tmo); timeout = &ts; } } else { timeout = &max_ts; } } NEEDLESS_RETURN(NULL); }