[master] 51e6ded88 Use a separate condvar for connection-level flow control updates

Dag Haavi Finstad daghf at varnish-software.com
Wed Jun 27 11:41:12 UTC 2018


commit 51e6ded883b7655091bd6a8ffb8869fa7d40b071
Author: Dag Haavi Finstad <daghf at varnish-software.com>
Date:   Wed Jun 27 13:27:34 2018 +0200

    Use a separate condvar for connection-level flow control updates
    
    The current flow control code's use of h2->cond is racy.
    
    h2->cond is already used for handing over a DATA frame to a stream
    thread. In the event that we have both streams waiting on this condvar
    for window updates and at the same time the rxthread gets signaled for a
    DATA frame, we could end up waking up the wrong thread and the rxthread
    gets stuck forever.
    
    This commit addresses this by using a separate condvar for window
    updates.
    
    An alternative would be to always issue a broadcast on h2->cond instead
    of signal, but I found this approach much cleaner.
    
    Probably fixes: #2623

diff --git a/bin/varnishd/http2/cache_http2.h b/bin/varnishd/http2/cache_http2.h
index a1d6f849b..fbc190535 100644
--- a/bin/varnishd/http2/cache_http2.h
+++ b/bin/varnishd/http2/cache_http2.h
@@ -145,6 +145,7 @@ struct h2_sess {
 	pthread_t			rxthr;
 	struct h2_req			*mailcall;
 	pthread_cond_t			*cond;
+	pthread_cond_t			winupd_cond[1];
 
 	struct sess			*sess;
 	int				refcnt;
diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c
index 9dede792e..e5106e8a4 100644
--- a/bin/varnishd/http2/cache_http2_proto.c
+++ b/bin/varnishd/http2/cache_http2_proto.c
@@ -369,7 +369,7 @@ h2_rx_window_update(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 	Lck_Lock(&h2->sess->mtx);
 	r2->t_window += wu;
 	if (r2 == h2->req0)
-		AZ(pthread_cond_broadcast(h2->cond));
+		AZ(pthread_cond_broadcast(h2->winupd_cond));
 	else if (r2->cond != NULL)
 		AZ(pthread_cond_signal(r2->cond));
 	Lck_Unlock(&h2->sess->mtx);
diff --git a/bin/varnishd/http2/cache_http2_send.c b/bin/varnishd/http2/cache_http2_send.c
index cfbb51be7..7c290bf9d 100644
--- a/bin/varnishd/http2/cache_http2_send.c
+++ b/bin/varnishd/http2/cache_http2_send.c
@@ -213,7 +213,7 @@ h2_do_window(struct worker *wrk, struct h2_req *r2,
 			r2->cond = NULL;
 		}
 		while (h2->req0->t_window <= 0 && h2_errcheck(r2, h2) == 0) {
-			AZ(Lck_CondWait(h2->cond, &h2->sess->mtx, 0));
+			AZ(Lck_CondWait(h2->winupd_cond, &h2->sess->mtx, 0));
 		}
 
 		if (h2_errcheck(r2, h2) == 0) {
diff --git a/bin/varnishd/http2/cache_http2_session.c b/bin/varnishd/http2/cache_http2_session.c
index b350f05c3..c0545265c 100644
--- a/bin/varnishd/http2/cache_http2_session.c
+++ b/bin/varnishd/http2/cache_http2_session.c
@@ -123,6 +123,7 @@ h2_init_sess(const struct worker *wrk, struct sess *sp,
 		h2->htc->rfd = &sp->fd;
 		h2->sess = sp;
 		h2->rxthr = pthread_self();
+		AZ(pthread_cond_init(h2->winupd_cond, NULL));
 		VTAILQ_INIT(&h2->streams);
 		VTAILQ_INIT(&h2->txqueue);
 		h2_local_settings(&h2->local_settings);
@@ -150,6 +151,7 @@ h2_del_sess(struct worker *wrk, struct h2_sess *h2, enum sess_close reason)
 	assert(VTAILQ_EMPTY(&h2->streams));
 
 	VHT_Fini(h2->dectbl);
+	AZ(pthread_cond_destroy(h2->winupd_cond));
 	req = h2->srq;
 	AZ(req->ws->r);
 	sp = h2->sess;
@@ -404,7 +406,7 @@ h2_new_session(struct worker *wrk, void *arg)
 		if (r2->cond != NULL)
 			AZ(pthread_cond_signal(r2->cond));
 	}
-	AZ(pthread_cond_broadcast(h2->cond));
+	AZ(pthread_cond_broadcast(h2->winupd_cond));
 	Lck_Unlock(&h2->sess->mtx);
 	while (1) {
 		again = 0;


More information about the varnish-commit mailing list