MINOR: event_hdl: pause/resume for subscriptions

While working on event handling from lua, the need for a pause/resume
function to temporarily disable a subscription was raised.

We solve this by introducing the EHDL_SUB_F_PAUSED flag for
subscriptions.

The flag is set via _pause() and cleared via _resume(), and it is
checked prior to notifying the subscription in publish function.

Pause and Resume functions are also available for via lookups for
identified subscriptions.

If 68e692da0 ("MINOR: event_hdl: add event handler base api")
is being backported, then this commit should be backported with it.
diff --git a/include/haproxy/event_hdl-t.h b/include/haproxy/event_hdl-t.h
index eb5d057..308f670 100644
--- a/include/haproxy/event_hdl-t.h
+++ b/include/haproxy/event_hdl-t.h
@@ -206,12 +206,16 @@
 	void				*private;
 };
 
+/* flags for event_hdl_sub struct (32 bits) */
+#define EHDL_SUB_F_PAUSED        0x0001  /* subscription will temporarily ignore events */
+
 /* list elem: subscription (handler subscribed to specific events)
  */
 struct event_hdl_sub {
 	struct mt_list			mt_list;
 	/* event type subscription */
 	struct event_hdl_sub_type	sub;
+	uint32_t                        flags;
 	/* event handler */
 	struct event_hdl		hdl;
 	/* used to guarantee that END event will be delivered
diff --git a/include/haproxy/event_hdl.h b/include/haproxy/event_hdl.h
index 41dc446..7cadb55 100644
--- a/include/haproxy/event_hdl.h
+++ b/include/haproxy/event_hdl.h
@@ -301,6 +301,35 @@
 struct event_hdl_sub *event_hdl_lookup_take(event_hdl_sub_list *sub_list,
                                             uint64_t lookup_id);
 
+/* pause an existing subscription <sub>
+ * the subscription will no longer receive events (reversible)
+ * This can be reverted thanks to _resume() function
+ */
+void event_hdl_pause(struct event_hdl_sub *sub);
+
+/* resume an existing subscription <sub>
+ * that was previously paused using _pause() function
+ */
+void event_hdl_resume(struct event_hdl_sub *sub);
+
+/* Same as event_hdl_pause() for identified subscriptions:
+ * use this function to pause the subscription <lookup_ip>
+ * within <sub_list> list.
+ * If <sub_list> is NULL, global subscription list will be used.
+ * Returns 1 for SUCCESS and 0 if not found
+ */
+int event_hdl_lookup_pause(event_hdl_sub_list *sub_list,
+                           uint64_t lookup_id);
+
+/* Same as event_hdl_resume() for identified subscriptions:
+ * use this function to resume the subscription <lookup_ip>
+ * within <sub_list> list.
+ * If <sub_list> is NULL, global subscription list will be used.
+ * Returns 1 for SUCCESS and 0 if not found
+ */
+int event_hdl_lookup_resume(event_hdl_sub_list *sub_list,
+                            uint64_t lookup_id);
+
 /* ------ PUBLISHING FUNCTIONS ------ */
 
 /* this macro is provided as an internal helper for EVENT_HDL_TRIGGER to automatically
diff --git a/src/event_hdl.c b/src/event_hdl.c
index aecca87..14aed7a 100644
--- a/src/event_hdl.c
+++ b/src/event_hdl.c
@@ -444,6 +444,7 @@
 	/* assignments */
 	new_sub->sub.family = e_type.family;
 	new_sub->sub.subtype = e_type.subtype;
+	new_sub->flags = 0;
 	new_sub->hdl = hdl;
 
 	if (hdl.async) {
@@ -596,6 +597,38 @@
 	return _event_hdl_resub_async(cur_sub, type);
 }
 
+void _event_hdl_pause(struct event_hdl_sub *cur_sub)
+{
+	cur_sub->flags |= EHDL_SUB_F_PAUSED;
+}
+
+void event_hdl_pause(struct event_hdl_sub *cur_sub)
+{
+	struct mt_list lock;
+
+	lock = MT_LIST_LOCK_ELT(&cur_sub->mt_list);
+	if (lock.next != &cur_sub->mt_list)
+		_event_hdl_pause(cur_sub);
+	// else already removed
+	MT_LIST_UNLOCK_ELT(&cur_sub->mt_list, lock);
+}
+
+void _event_hdl_resume(struct event_hdl_sub *cur_sub)
+{
+	cur_sub->flags &= ~EHDL_SUB_F_PAUSED;
+}
+
+void event_hdl_resume(struct event_hdl_sub *cur_sub)
+{
+	struct mt_list lock;
+
+	lock = MT_LIST_LOCK_ELT(&cur_sub->mt_list);
+	if (lock.next != &cur_sub->mt_list)
+		_event_hdl_resume(cur_sub);
+	// else already removed
+	MT_LIST_UNLOCK_ELT(&cur_sub->mt_list, lock);
+}
+
 void event_hdl_unsubscribe(struct event_hdl_sub *del_sub)
 {
 	_event_hdl_unsubscribe_async(del_sub);
@@ -660,6 +693,48 @@
 	return status;
 }
 
+int event_hdl_lookup_pause(event_hdl_sub_list *sub_list,
+                           uint64_t lookup_id)
+{
+	struct event_hdl_sub *cur_sub = NULL;
+	struct mt_list *elt1, elt2;
+	int found = 0;
+
+	if (!sub_list)
+		sub_list = &global_event_hdl_sub_list; /* fall back to global list */
+
+	mt_list_for_each_entry_safe(cur_sub, &sub_list->head, mt_list, elt1, elt2) {
+		if (lookup_id == cur_sub->hdl.id) {
+			/* we found matching registered hdl */
+			_event_hdl_pause(cur_sub);
+			found = 1;
+			break; /* id is unique, stop searching */
+		}
+	}
+	return found;
+}
+
+int event_hdl_lookup_resume(event_hdl_sub_list *sub_list,
+                            uint64_t lookup_id)
+{
+	struct event_hdl_sub *cur_sub = NULL;
+	struct mt_list *elt1, elt2;
+	int found = 0;
+
+	if (!sub_list)
+		sub_list = &global_event_hdl_sub_list; /* fall back to global list */
+
+	mt_list_for_each_entry_safe(cur_sub, &sub_list->head, mt_list, elt1, elt2) {
+		if (lookup_id == cur_sub->hdl.id) {
+			/* we found matching registered hdl */
+			_event_hdl_resume(cur_sub);
+			found = 1;
+			break; /* id is unique, stop searching */
+		}
+	}
+	return found;
+}
+
 struct event_hdl_sub *event_hdl_lookup_take(event_hdl_sub_list *sub_list,
                                             uint64_t lookup_id)
 {
@@ -694,9 +769,10 @@
 	int error = 0;
 
 	mt_list_for_each_entry_safe(cur_sub, &sub_list->head, mt_list, elt1, elt2) {
-		/* notify each function that has subscribed to sub_family.type */
+		/* notify each function that has subscribed to sub_family.type, unless paused */
 		if ((cur_sub->sub.family == e_type.family) &&
-		    ((cur_sub->sub.subtype & e_type.subtype) == e_type.subtype)) {
+		    ((cur_sub->sub.subtype & e_type.subtype) == e_type.subtype) &&
+		    !(cur_sub->flags & EHDL_SUB_F_PAUSED)) {
 			/* hdl should be notified */
 			if (!cur_sub->hdl.async) {
 				/* sync mode: simply call cb pointer