MINOR: listener: replace the listener's spinlock with an rwlock

We'll need to lock the listener a little bit more during accept() and
tests show that a spinlock is a massive performance killer, so let's
first switch to an rwlock for this lock.

This patch might have to be backported for the next patch to work, and
if so, the change is almost mechanical (look for LISTENER_LOCK), but do
not forget about the few HA_SPIN_INIT() in the file. There's no reference
to this lock outside of listener.c nor listener-t.h.

(cherry picked from commit 08b6f9645248405bb1cdec59c6aa4f35a8ba3add)
[wt: minor ctx adjustment]
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit 44fdf4c775f98c1c9b2b0e7a261905339586a1e1)
[wt: no clone_listener() in 2.4]
Signed-off-by: Willy Tarreau <w@1wt.eu>
diff --git a/src/listener.c b/src/listener.c
index 8031c75..4b229ca 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -264,7 +264,7 @@
  */
 void enable_listener(struct listener *listener)
 {
-	HA_SPIN_LOCK(LISTENER_LOCK, &listener->lock);
+	HA_RWLOCK_WRLOCK(LISTENER_LOCK, &listener->lock);
 
 	/* If this listener is supposed to be only in the master, close it in
 	 * the workers. Conversely, if it's supposed to be only in the workers
@@ -292,7 +292,7 @@
 		}
 	}
 
-	HA_SPIN_UNLOCK(LISTENER_LOCK, &listener->lock);
+	HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &listener->lock);
 }
 
 /*
@@ -321,7 +321,7 @@
 		HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
 
 	if (!lli)
-		HA_SPIN_LOCK(LISTENER_LOCK, &l->lock);
+		HA_RWLOCK_WRLOCK(LISTENER_LOCK, &l->lock);
 
 	if (l->state > LI_INIT) {
 		do_unbind_listener(l);
@@ -333,7 +333,7 @@
 	}
 
 	if (!lli)
-		HA_SPIN_UNLOCK(LISTENER_LOCK, &l->lock);
+		HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &l->lock);
 
 	if (!lpr)
 		HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
@@ -428,7 +428,7 @@
 	struct proxy *px = l->bind_conf->frontend;
 	int ret = 1;
 
-	HA_SPIN_LOCK(LISTENER_LOCK, &l->lock);
+	HA_RWLOCK_WRLOCK(LISTENER_LOCK, &l->lock);
 
 	if ((global.mode & (MODE_DAEMON | MODE_MWORKER)) &&
 	    !(proc_mask(l->rx.settings->bind_proc) & pid_bit))
@@ -449,7 +449,7 @@
 		send_log(px, LOG_WARNING, "Paused %s %s.\n", proxy_cap_str(px->cap), px->id);
 	}
   end:
-	HA_SPIN_UNLOCK(LISTENER_LOCK, &l->lock);
+	HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &l->lock);
 	return ret;
 }
 
@@ -469,7 +469,7 @@
 	int was_paused = px && px->li_paused;
 	int ret = 1;
 
-	HA_SPIN_LOCK(LISTENER_LOCK, &l->lock);
+	HA_RWLOCK_WRLOCK(LISTENER_LOCK, &l->lock);
 
 	/* check that another thread didn't to the job in parallel (e.g. at the
 	 * end of listen_accept() while we'd come from dequeue_all_listeners().
@@ -502,7 +502,7 @@
 		send_log(px, LOG_WARNING, "Resumed %s %s.\n", proxy_cap_str(px->cap), px->id);
 	}
   end:
-	HA_SPIN_UNLOCK(LISTENER_LOCK, &l->lock);
+	HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &l->lock);
 	return ret;
 }
 
@@ -511,7 +511,7 @@
  */
 static void listener_full(struct listener *l)
 {
-	HA_SPIN_LOCK(LISTENER_LOCK, &l->lock);
+	HA_RWLOCK_WRLOCK(LISTENER_LOCK, &l->lock);
 	if (l->state >= LI_READY) {
 		MT_LIST_DELETE(&l->wait_queue);
 		if (l->state != LI_FULL) {
@@ -519,7 +519,7 @@
 			listener_set_state(l, LI_FULL);
 		}
 	}
-	HA_SPIN_UNLOCK(LISTENER_LOCK, &l->lock);
+	HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &l->lock);
 }
 
 /* Marks a ready listener as limited so that we only try to re-enable it when
@@ -527,13 +527,13 @@
  */
 static void limit_listener(struct listener *l, struct mt_list *list)
 {
-	HA_SPIN_LOCK(LISTENER_LOCK, &l->lock);
+	HA_RWLOCK_WRLOCK(LISTENER_LOCK, &l->lock);
 	if (l->state == LI_READY) {
 		MT_LIST_TRY_APPEND(list, &l->wait_queue);
 		l->rx.proto->disable(l);
 		listener_set_state(l, LI_LIMITED);
 	}
-	HA_SPIN_UNLOCK(LISTENER_LOCK, &l->lock);
+	HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &l->lock);
 }
 
 /* Dequeues all listeners waiting for a resource the global wait queue */
@@ -615,9 +615,9 @@
  */
 void unbind_listener(struct listener *listener)
 {
-	HA_SPIN_LOCK(LISTENER_LOCK, &listener->lock);
+	HA_RWLOCK_WRLOCK(LISTENER_LOCK, &listener->lock);
 	do_unbind_listener(listener);
-	HA_SPIN_UNLOCK(LISTENER_LOCK, &listener->lock);
+	HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &listener->lock);
 }
 
 /* creates one or multiple listeners for bind_conf <bc> on sockaddr <ss> on port
@@ -663,7 +663,7 @@
 
 		l->extra_counters = NULL;
 
-		HA_SPIN_INIT(&l->lock);
+		HA_RWLOCK_INIT(&l->lock);
 		_HA_ATOMIC_INC(&jobs);
 		_HA_ATOMIC_INC(&listeners);
 	}
@@ -695,9 +695,9 @@
 void delete_listener(struct listener *listener)
 {
 	HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
-	HA_SPIN_LOCK(LISTENER_LOCK, &listener->lock);
+	HA_RWLOCK_WRLOCK(LISTENER_LOCK, &listener->lock);
 	__delete_listener(listener);
-	HA_SPIN_UNLOCK(LISTENER_LOCK, &listener->lock);
+	HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &listener->lock);
 	HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
 }