[MEDIUM] listeners: queue proxy-bound listeners at the proxy's

All listeners that are limited by a proxy-specific resource are now
queued at the proxy's and not globally. This allows finer-grained
wakeups when releasing resource.
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 6e38962..21ef3d9 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1041,6 +1041,7 @@
 	LIST_INIT(&p->tcp_req.l4_rules);
 	LIST_INIT(&p->req_add);
 	LIST_INIT(&p->rsp_add);
+	LIST_INIT(&p->listener_queue);
 
 	/* Timeouts are defined as -1 */
 	proxy_reset_timeouts(p);
diff --git a/src/proxy.c b/src/proxy.c
index 2184a1b..25398a9 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -499,6 +499,10 @@
 				goto do_block;
 			}
 
+			/* The proxy is not limited so we can re-enable any waiting listener */
+			if (!LIST_ISEMPTY(&p->listener_queue))
+				dequeue_all_listeners(&p->listener_queue);
+
 			/* OK we have no reason to block, so let's unblock if we were blocking */
 			if (p->state == PR_STIDLE) {
 				for (l = p->listen; l != NULL; l = l->next)
diff --git a/src/session.c b/src/session.c
index 750cf40..65c92db 100644
--- a/src/session.c
+++ b/src/session.c
@@ -2096,6 +2096,9 @@
 	if (!LIST_ISEMPTY(&global_listener_queue))
 		dequeue_all_listeners(&global_listener_queue);
 
+	if (!LIST_ISEMPTY(&s->fe->listener_queue))
+		dequeue_all_listeners(&s->fe->listener_queue);
+
 	if (unlikely((global.mode & MODE_DEBUG) &&
 		     (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)))) {
 		int len;
diff --git a/src/stream_sock.c b/src/stream_sock.c
index fa03a62..a0b2c11 100644
--- a/src/stream_sock.c
+++ b/src/stream_sock.c
@@ -1192,7 +1192,6 @@
 	int max_accept = global.tune.maxaccept;
 	int cfd;
 	int ret;
-	int loops = 0;
 
 	if (unlikely(l->nbconn >= l->maxconn)) {
 		listener_full(l);
@@ -1201,15 +1200,31 @@
 
 	if (p && p->fe_sps_lim) {
 		int max = freq_ctr_remain(&p->fe_sess_per_sec, p->fe_sps_lim, 0);
+
+		if (unlikely(!max)) {
+			/* frontend accept rate limit was reached */
+			limit_listener(l, &p->listener_queue);
+			return 0;
+		}
+
 		if (max_accept > max)
 			max_accept = max;
 	}
 
-	while ((!p || p->feconn < p->maxconn) && actconn < global.maxconn && max_accept--) {
+	while (max_accept--) {
 		struct sockaddr_storage addr;
 		socklen_t laddr = sizeof(addr);
 
+		if (unlikely(actconn >= global.maxconn)) {
+			limit_listener(l, &global_listener_queue);
+			return 0;
+		}
+
+		if (unlikely(p && p->feconn >= p->maxconn)) {
+			limit_listener(l, &p->listener_queue);
+			return 0;
+		}
+
-		loops++;
 		cfd = accept(fd, (struct sockaddr *)&addr, &laddr);
 		if (unlikely(cfd == -1)) {
 			switch (errno) {
@@ -1287,10 +1302,6 @@
 
 	} /* end of while (p->feconn < p->maxconn) */
 
-	/* if we did not even enter the loop, we've reached resource limits */
-	if (!loops && max_accept)
-		limit_listener(l, &global_listener_queue);
-
 	return 0;
 }