MINOR: proxy: maintain per-state counters of listeners

The proxy state tries to be synthetic but that doesn't work well with
many listeners, especially for transition phases or after a failed
pause/resume.

In order to address this, we'll instead rely on counters of listeners in
a given state for the 3 major states (ready, paused, listen) and a total
counter. We'll now be able to determine a proxy's state by comparing these
counters only.
diff --git a/src/listener.c b/src/listener.c
index 1ad017d..c4ee3c3 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -218,9 +218,55 @@
 
 #endif // USE_THREAD
 
-/* adjust the listener's state */
+/* adjust the listener's state and its proxy's listener counters if needed.
+ * It must be called under the listener's lock, but uses atomic ops to change
+ * the proxy's counters so that the proxy lock is not needed.
+ */
 void listener_set_state(struct listener *l, enum li_state st)
 {
+	struct proxy *px = l->bind_conf->frontend;
+
+	if (px) {
+		/* from state */
+		switch (l->state) {
+		case LI_NEW: /* first call */
+			_HA_ATOMIC_ADD(&px->li_all, 1);
+			break;
+		case LI_INIT:
+		case LI_ASSIGNED:
+			break;
+		case LI_PAUSED:
+			_HA_ATOMIC_SUB(&px->li_paused, 1);
+			break;
+		case LI_LISTEN:
+			_HA_ATOMIC_SUB(&px->li_bound, 1);
+			break;
+		case LI_READY:
+		case LI_FULL:
+		case LI_LIMITED:
+			_HA_ATOMIC_SUB(&px->li_ready, 1);
+			break;
+		}
+
+		/* to state */
+		switch (st) {
+		case LI_NEW:
+		case LI_INIT:
+		case LI_ASSIGNED:
+			break;
+		case LI_PAUSED:
+			_HA_ATOMIC_ADD(&px->li_paused, 1);
+			break;
+		case LI_LISTEN:
+			_HA_ATOMIC_ADD(&px->li_bound, 1);
+			break;
+		case LI_READY:
+		case LI_FULL:
+		case LI_LIMITED:
+			_HA_ATOMIC_ADD(&px->li_ready, 1);
+			break;
+		}
+	}
 	l->state = st;
 }