[master] 0d3044d Make senders wait in H2_Send when out of window credits

Dag Haavi Finstad daghf at varnish-software.com
Wed Oct 4 11:37:05 UTC 2017


commit 0d3044d4f3c18935049ad0e954edbacafa055557
Author: Dag Haavi Finstad <daghf at varnish-software.com>
Date:   Tue Oct 3 16:59:03 2017 +0200

    Make senders wait in H2_Send when out of window credits

diff --git a/bin/varnishd/http2/cache_http2.h b/bin/varnishd/http2/cache_http2.h
index 3b5ef99..fd720e6 100644
--- a/bin/varnishd/http2/cache_http2.h
+++ b/bin/varnishd/http2/cache_http2.h
@@ -217,7 +217,7 @@ h2_error H2_Send_Frame(struct worker *, const struct h2_sess *,
     h2_frame type, uint8_t flags, uint32_t len, uint32_t stream,
     const void *);
 
-h2_error H2_Send(struct worker *, const struct h2_req *,
+h2_error H2_Send(struct worker *, struct h2_req *,
     h2_frame type, uint8_t flags, uint32_t len, const void *);
 
 /* cache_http2_proto.c */
diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c
index 32aaf6f..705aaa7 100644
--- a/bin/varnishd/http2/cache_http2_proto.c
+++ b/bin/varnishd/http2/cache_http2_proto.c
@@ -352,6 +352,10 @@ h2_rx_window_update(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 		return (0);
 	Lck_Lock(&h2->sess->mtx);
 	r2->t_window += wu;
+	if (r2 == h2->req0)
+		AZ(pthread_cond_broadcast(h2->cond));
+	else if (r2->cond != NULL)
+		AZ(pthread_cond_signal(r2->cond));
 	Lck_Unlock(&h2->sess->mtx);
 	if (r2->t_window >= (1LLU << 31))
 		return (H2SE_FLOW_CONTROL_ERROR);
diff --git a/bin/varnishd/http2/cache_http2_send.c b/bin/varnishd/http2/cache_http2_send.c
index 15cbb54..0c5344d 100644
--- a/bin/varnishd/http2/cache_http2_send.c
+++ b/bin/varnishd/http2/cache_http2_send.c
@@ -54,7 +54,6 @@ h2_send_get(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 void
 H2_Send_Get(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 {
-
 	CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
 	CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
@@ -149,14 +148,105 @@ H2_Send_Frame(struct worker *wrk, const struct h2_sess *h2,
 	return (0);
 }
 
+static int64_t
+h2_win_limit(const struct h2_req *r2, const struct h2_sess *h2)
+{
+	int64_t m;
+
+	CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(h2->req0, H2_REQ_MAGIC);
+
+	Lck_AssertHeld(&h2->sess->mtx);
+	m = r2->t_window;
+	if (m > h2->req0->t_window)
+		m = h2->req0->t_window;
+	return (m);
+}
+
+static void
+h2_win_charge(struct h2_req *r2, struct h2_sess *h2,
+    uint32_t w)
+{
+	CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(h2->req0, H2_REQ_MAGIC);
+
+	Lck_AssertHeld(&h2->sess->mtx);
+	r2->t_window -= w;
+	h2->req0->t_window -= w;
+}
+
+static h2_error
+h2_errcheck(const struct h2_req *r2, const struct h2_sess *h2)
+{
+	CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
+
+	if (r2->error)
+		return (r2->error);
+	if (h2->error && r2->stream > h2->goaway_last_stream)
+		return (h2->error);
+	return (0);
+}
+
+static int64_t
+h2_do_window(struct worker *wrk, struct h2_req *r2,
+    struct h2_sess *h2, int64_t wanted)
+{
+	int64_t w = 0;
+
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
+
+	if (wanted == 0)
+		return (0);
+
+	Lck_Lock(&h2->sess->mtx);
+	if (r2->t_window <= 0 || h2->req0->t_window <= 0) {
+		h2_send_rel(h2, r2);
+		while (r2->t_window <= 0 && h2_errcheck(r2, h2) == 0) {
+			r2->cond = &wrk->cond;
+			// XXX: timeout handling (subject to send_timeout?)
+			AZ(Lck_CondWait(r2->cond, &h2->sess->mtx, 0));
+			r2->cond = NULL;
+		}
+		while (h2->req0->t_window <= 0 && h2_errcheck(r2, h2) == 0) {
+			// XXX: timeout handling
+			AZ(Lck_CondWait(h2->cond, &h2->sess->mtx, 0));
+		}
+
+		if (h2_errcheck(r2, h2) == 0) {
+			w = h2_win_limit(r2, h2);
+			if (w > wanted)
+				w = wanted;
+			h2_win_charge(r2, h2, w);
+			assert (w > 0);
+		}
+		h2_send_get(wrk, h2, r2);
+	}
+
+	if (w == 0 && h2_errcheck(r2, h2) == 0) {
+		assert(r2->t_window > 0);
+		assert(h2->req0->t_window > 0);
+		w = h2_win_limit(r2, h2);
+		if (w > wanted)
+			w = wanted;
+		h2_win_charge(r2, h2, w);
+		assert (w > 0);
+	}
+	Lck_Unlock(&h2->sess->mtx);
+	return (w);
+}
+
 /*
  * This is the per-stream frame sender.
- * XXX: windows
  * XXX: priority
  */
 
 h2_error
-H2_Send(struct worker *wrk, const struct h2_req *r2,
+H2_Send(struct worker *wrk, struct h2_req *r2,
     h2_frame ftyp, uint8_t flags, uint32_t len, const void *ptr)
 {
 	h2_error retval;
@@ -173,11 +263,9 @@ H2_Send(struct worker *wrk, const struct h2_req *r2,
 
 	assert(VTAILQ_FIRST(&h2->txqueue) == r2);
 
-	if (r2->error)
-		return (r2->error);
-
-	if (h2->error && r2->stream > h2->goaway_last_stream)
-		return (h2->error);
+	retval = h2_errcheck(r2, h2);
+	if (retval)
+		return (retval);
 
 	AN(ftyp);
 	AZ(flags & ~(ftyp->flags));
@@ -189,7 +277,18 @@ H2_Send(struct worker *wrk, const struct h2_req *r2,
 	Lck_Lock(&h2->sess->mtx);
 	mfs = h2->remote_settings.max_frame_size;
 	Lck_Unlock(&h2->sess->mtx);
-	if (len < mfs) {
+
+	if (ftyp->respect_window) {
+		tf = h2_do_window(wrk, r2, h2,
+				  (len > mfs) ? mfs : len);
+		retval = h2_errcheck(r2, h2);
+		if (retval)
+			return (retval);
+		assert(VTAILQ_FIRST(&h2->txqueue) == r2);
+	} else
+		tf = mfs;
+
+	if (len <= tf) {
 		retval = H2_Send_Frame(wrk, h2,
 		    ftyp, flags, len, r2->stream, ptr);
 	} else {
@@ -199,11 +298,22 @@ H2_Send(struct worker *wrk, const struct h2_req *r2,
 		flags &= ~ftyp->final_flags;
 		do {
 			AN(ftyp->continuation);
-			tf = mfs;
+			if (!ftyp->respect_window)
+				tf = mfs;
+			if (ftyp->respect_window && p != ptr) {
+				tf = h2_do_window(wrk, r2, h2,
+						  (len > mfs) ? mfs : len);
+				retval = h2_errcheck(r2, h2);
+				if (retval)
+					return (retval);
+				assert(VTAILQ_FIRST(&h2->txqueue) == r2);
+			}
 			if (tf < len) {
 				retval = H2_Send_Frame(wrk, h2, ftyp,
 				    flags, tf, r2->stream, p);
 			} else {
+				if (ftyp->respect_window)
+					assert(tf == len);
 				tf = len;
 				retval = H2_Send_Frame(wrk, h2, ftyp,
 				    final_flags, tf, r2->stream, p);
diff --git a/bin/varnishd/http2/cache_http2_session.c b/bin/varnishd/http2/cache_http2_session.c
index 567e908..63f6f95 100644
--- a/bin/varnishd/http2/cache_http2_session.c
+++ b/bin/varnishd/http2/cache_http2_session.c
@@ -326,7 +326,7 @@ h2_new_session(struct worker *wrk, void *arg)
 		if (r2->cond != NULL)
 			AZ(pthread_cond_signal(r2->cond));
 	}
-	AZ(pthread_cond_signal(h2->cond));
+	AZ(pthread_cond_broadcast(h2->cond));
 	while (1) {
 		again = 0;
 		VTAILQ_FOREACH_SAFE(r2, &h2->streams, list, r22) {


More information about the varnish-commit mailing list