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);
}
Beispiel #2
0
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);
}
Beispiel #3
0
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");
}
Beispiel #4
0
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);
}
Beispiel #7
0
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;
}
Beispiel #11
0
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);
}
Beispiel #13
0
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);
}
Beispiel #14
0
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);
}
Beispiel #15
0
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);
}
Beispiel #18
0
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);
}