summaryrefslogtreecommitdiffstats
path: root/include/haproxy/event_hdl.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/haproxy/event_hdl.h')
-rw-r--r--include/haproxy/event_hdl.h512
1 files changed, 512 insertions, 0 deletions
diff --git a/include/haproxy/event_hdl.h b/include/haproxy/event_hdl.h
new file mode 100644
index 0000000..5a7ee66
--- /dev/null
+++ b/include/haproxy/event_hdl.h
@@ -0,0 +1,512 @@
+/*
+ * include/haproxy/event_hdl.h
+ * event handlers management
+ *
+ * Copyright 2022 HAProxy Technologies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _HAPROXY_EVENT_HDL_H
+# define _HAPROXY_EVENT_HDL_H
+
+#include <haproxy/event_hdl-t.h>
+#include <haproxy/list.h>
+
+/* preprocessor trick to extract function calling place
+ * __FILE__:__LINE__
+ */
+#define _EVENT_HDL_CALLING_PLACE2(line) #line
+#define _EVENT_HDL_CALLING_PLACE1(line) _EVENT_HDL_CALLING_PLACE2(line)
+#define _EVENT_HDL_CALLING_PLACE __FILE__":"_EVENT_HDL_CALLING_PLACE1(__LINE__)
+
+/* ------ PUBLIC EVENT_HDL API ------ */
+
+/* You will find a lot of useful information/comments in this file, but if you're looking
+ * for a step by step documentation please check out 'doc/internals/api/event_hdl.txt'
+ */
+
+/* Note: API helper macros are used in this file to make event_hdl functions usage
+ * simpler, safer and more consistent between sync mode and async mode
+ */
+
+/* ======================================= EVENT_HDL_SYNC handlers =====================================
+ * must be used only with extreme precautions
+ * sync handlers are directly called under the function that published the event.
+ * Hence, all the processing done within such function will impact the caller.
+ *
+ * For this reason, you must be extremely careful when using sync mode, because trying to lock something
+ * that is already held by the caller, or depending on something external to the current thread will
+ * prevent the caller from running.
+ *
+ * Please consider using async handlers in this case, they are specifically made to solve this limitation.
+ *
+ * On the other hand, sync handlers are really useful when you directly depend on callers' provided data
+ * (example: pointer to data) or you need to perform something before the caller keeps going.
+ * A good example could be a cleanup function that will take care of freeing data, closing fds... related
+ * to event data before caller's flow keeps going (interrupting the process while dealing with the event).
+ */
+
+
+/* ===================================== EVENT_HDL_ASYNC handlers ======================================
+ * async handlers are run in independent tasks, so that the caller (that published the event) can safely
+ * return to its own processing.
+ *
+ * async handlers may access safe event data safely with guaranteed consistency.
+ */
+
+
+/* ================================ IDENTIFIED vs ANONYMOUS EVENT_HDL =================================
+ * When registering a sync or async event handler, you are free to provide a unique identifier (hash).
+ *
+ * id can be computed using event_hdl_id function.
+ *
+ * Not providing an id results in the subscription being considered as anonymous subscription.
+ * 0 is not a valid identifier (should be > 0)
+ *
+ * Identified subscription is guaranteed to be unique for a given subscription list,
+ * whereas anonymous subscriptions don't provide such guarantees.
+ *
+ * Identified subscriptions provide the ability to be later queried or unregistered from external code
+ * using dedicated id/hash for the lookups.
+ *
+ * On the other hand, anonymous subscriptions don't, the only other way to reference an anonymous subscription
+ * is to use a subscription pointer.
+ *
+ */
+
+/* general purpose hashing function when you want to compute
+ * an ID based on <scope> x <name>
+ * It is your responsibility to make sure <scope> is not used
+ * elsewhere in the code (or that you are fine with sharing
+ * the scope).
+ */
+uint64_t event_hdl_id(const char *scope, const char *name);
+
+/* ------ EVENT SUBSCRIPTIONS FUNCTIONS ------ */
+
+/* macro helper:
+ * sync version
+ *
+ * identified subscription
+ *
+ * <_id>: subscription id that could be used later
+ * to perform subscription lookup by id
+ * <func>: pointer to 'event_hdl_cb_sync' prototyped function
+ * <_private>: pointer to private data that will be handled to <func>
+ * <_private_free>: pointer to 'event_hdl_private_free' prototyped function
+ * that will be called with <private> when unsubscription is performed
+ */
+#define EVENT_HDL_ID_SYNC(_id, func, _private, _private_free) \
+ (struct event_hdl){ .id = _id, \
+ .dorigin = _EVENT_HDL_CALLING_PLACE, \
+ .async = 0, \
+ .sync_ptr = func, \
+ .private = _private, \
+ .private_free = _private_free }
+
+/* macro helper:
+ * sync version
+ *
+ * anonymous subscription (no lookup by id)
+ *
+ * <func>: pointer to 'event_hdl_cb_sync' prototyped function
+ * <_private>: pointer to private data that will be handled to <func>
+ * <_private_free>: pointer to 'event_hdl_private_free' prototyped function
+ * that will be called with <private> when unsubscription is performed
+ */
+#define EVENT_HDL_SYNC(func, _private, _private_free) \
+ EVENT_HDL_ID_SYNC(0, func, _private, _private_free)
+
+/* macro helper:
+ * async version
+ *
+ * identified subscription
+ *
+ * <_id>: subscription id that could be used later
+ * to perform subscription lookup by id
+ * <func>: pointer to 'event_hdl_cb_sync' prototyped function
+ * <_private>: pointer to private data that will be handled to <func>
+ * <_private_free>: pointer to 'event_hdl_private_free' prototyped function
+ * that will be called with <private> after unsubscription is performed,
+ * when no more events can refer to <private>.
+ */
+#define EVENT_HDL_ID_ASYNC(_id, func, _private, _private_free) \
+ (struct event_hdl){ .id = _id, \
+ .dorigin = _EVENT_HDL_CALLING_PLACE, \
+ .async = EVENT_HDL_ASYNC_MODE_NORMAL, \
+ .async_ptr = func, \
+ .private = _private, \
+ .private_free = _private_free }
+
+/* macro helper:
+ * async version
+ *
+ * anonymous subscription (no lookup by id)
+ *
+ * <func>: pointer to 'event_hdl_cb_sync' prototyped function
+ * <_private>: pointer to private data that will be handled to <func>
+ * <_private_free>: pointer to 'event_hdl_private_free' prototyped function
+ * that will be called with <private> after unsubscription is performed,
+ * when no more events can refer to <private>.
+ */
+#define EVENT_HDL_ASYNC(func, _private, _private_free) \
+ EVENT_HDL_ID_ASYNC(0, func, _private, _private_free)
+
+/* macro helper:
+ * async version
+ * same than EVENT_HDL_ID_ASYNC - advanced mode:
+ * you directly provide task and event_queue list.
+ *
+ * identified subscription
+ *
+ * <_id>: subscription id that could be used later
+ * to perform subscription lookup by id
+ * <equeue>: pointer to event_hdl_async_event queue where the pending
+ * events will be pushed. Cannot be NULL.
+ * <task>: pointer to task(let) responsible for consuming the events.
+* Cannot be NULL.
+ * <_private>: pointer to private data that will be handled to <func>
+ * <_private_free>: pointer to 'event_hdl_private_free' prototyped function
+ * that will be called with <private> after unsubscription is performed,
+ * when no more events can refer to <private>.
+ */
+#define EVENT_HDL_ID_ASYNC_TASK(_id, equeue, task, _private, _private_free) \
+ (struct event_hdl){ .id = _id, \
+ .dorigin = _EVENT_HDL_CALLING_PLACE, \
+ .async = EVENT_HDL_ASYNC_MODE_ADVANCED, \
+ .async_task = (struct tasklet *)task, \
+ .async_equeue = equeue, \
+ .private = _private, \
+ .private_free = _private_free }
+
+/* macro helper:
+ * async version
+ * same than EVENT_HDL_ASYNC - advanced mode:
+ * you directly provide task and event_queue list.
+ *
+ * anonymous subscription (no lookup by id)
+ *
+ * <equeue>: pointer to event_hdl_async_event queue where the pending
+ * events will be pushed. Cannot be NULL.
+ * <task>: pointer to task(let) responsible for consuming the events
+ * Cannot be NULL.
+ * <_private>: pointer to private data that will be handled to <func>
+ * <_private_free>: pointer to 'event_hdl_private_free' prototyped function
+ * that will be called with <private> after unsubscription is performed,
+ * when no more events can refer to <private>.
+ */
+#define EVENT_HDL_ASYNC_TASK(equeue, task, _private, _private_free) \
+ EVENT_HDL_ID_ASYNC_TASK(0, equeue, task, _private, _private_free)
+
+/* register a new event subscription in <sub_list>
+ * that will handle <e_type> events
+ *
+ * This function requires you to use
+ * EVENT_HDL_(TASK_)(A)SYNC() EVENT_HDL_ID_(TASK_)(A)SYNC() (choose wisely)
+ * macro helpers to provide <hdl> argument
+ *
+ * If <sub_list> is not specified (equals NULL):
+ * global subscription list (process wide) will be used.
+ *
+ * For identified subscriptions (EVENT_HDL_ID_*), the function is safe against
+ * concurrent subscriptions attempts with the same ID: the ID will only be
+ * inserted once in the list and subsequent attempts will yield an error.
+ * However, trying to register the same ID multiple times is considered as
+ * an error (no specific error code is returned in this case) so the check should
+ * be performed by the caller if it is expected. (The caller must ensure that the ID
+ * is unique to prevent the error from being raised)
+ *
+ * Returns 1 in case of success, 0 in case of failure (invalid argument / memory error)
+ */
+int event_hdl_subscribe(event_hdl_sub_list *sub_list,
+ struct event_hdl_sub_type e_type, struct event_hdl hdl);
+
+/* same as event_hdl_subscribe, but
+ * returns the subscription ptr in case of success
+ * or NULL in case of failure
+ * subscription refcount is automatically incremented by 1
+ * so that ptr remains valid while you use it.
+ * You must call event_hdl_drop() when you no longer
+ * use it or event_hdl_unsubscribe() to unregister the
+ * subscription
+ */
+struct event_hdl_sub *event_hdl_subscribe_ptr(event_hdl_sub_list *sub_list,
+ struct event_hdl_sub_type e_type, struct event_hdl hdl);
+
+/* update subscription type:
+ * if new type family does not match current family, does nothing
+ * only subtype update is supported
+ * Returns 1 for SUCCESS and 0 for FAILURE (update not supported)
+ */
+int event_hdl_resubscribe(struct event_hdl_sub *cur_sub, struct event_hdl_sub_type type);
+
+/* unregister an existing subscription <sub>
+ * will automatically call event_hdl_drop()
+ */
+void event_hdl_unsubscribe(struct event_hdl_sub *sub);
+
+/* decrease subscription refcount by 1
+ * use this when you no longer use sub ptr
+ * provided by event_hdl_subscribe_ptr or
+ * to cancel previous event_hdl_take()
+ */
+void event_hdl_drop(struct event_hdl_sub *sub);
+
+/* increase subscription refcount by 1
+ * event_hdl_drop is needed when ptr
+ * is not longer used
+ * or event_hdl_unsubscribe to end the subscription
+ */
+void event_hdl_take(struct event_hdl_sub *sub);
+
+/* ------ EVENT_HDL_LOOKUP: subscription lookup operations from external code ------ */
+
+/* use this function to unregister 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_unsubscribe(event_hdl_sub_list *sub_list,
+ uint64_t lookup_id);
+
+/* use this function to update subscription by <lookup_id> within <sub_list> list
+ * if new type family does not match current family, does nothing
+ * only subtype update is supported
+ * If <sub_list> is NULL, global subscription list will be used.
+ * Returns 1 for SUCCESS and 0 if not found or not supported
+ */
+int event_hdl_lookup_resubscribe(event_hdl_sub_list *sub_list,
+ uint64_t lookup_id, struct event_hdl_sub_type type);
+
+/* use this function to get a new reference ptr to the subscription
+ * identified by <id>
+ * or event_hdl_unsubscribe to end the subscription
+ * If <sub_list> is NULL, global subscription list will be used.
+ * returns NULL if not found
+ * returned ptr should be called with event_hdl_drop when no longer used
+ */
+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 to automatically populate
+ * data for fixed length structs as required by event_hdl publish function
+ */
+#define _EVENT_HDL_CB_DATA_ASSERT(size) \
+ ({ \
+ /* if this fails to compile \
+ * it means you need to fix \
+ * EVENT_HDL_ASYNC_EVENT_DATA \
+ * size in event_hdl-t.h \
+ */ \
+ __attribute__((unused)) \
+ char __static_assert[(size <= EVENT_HDL_ASYNC_EVENT_DATA) ? 1 : -1];\
+ (size); \
+ })
+#define _EVENT_HDL_CB_DATA(data,size,mfree) \
+ (&(struct event_hdl_cb_data){ ._ptr = data, \
+ ._size = size, \
+ ._mfree = mfree })
+
+/* Use this when 'safe' data is completely standalone */
+#define EVENT_HDL_CB_DATA(data) \
+ _EVENT_HDL_CB_DATA(data, \
+ _EVENT_HDL_CB_DATA_ASSERT(sizeof(*data)), \
+ NULL)
+/* Use this when 'safe' data points to dynamically allocated members
+ * that require freeing when the event is completely consumed
+ * (data in itself may be statically allocated as with
+ * EVENT_HDL_CB_DATA since the publish function will take
+ * care of copying it for async handlers)
+ *
+ * mfree function will be called with data as argument
+ * (or copy of data in async context) when the event is completely
+ * consumed (sync and async handlers included). This will give you
+ * enough context to perform the required cleanup steps.
+ *
+ * mfree should be prototyped like this:
+ * void (*mfree)(const void *data)
+ */
+#define EVENT_HDL_CB_DATA_DM(data, mfree) \
+ _EVENT_HDL_CB_DATA(data, \
+ _EVENT_HDL_CB_DATA_ASSERT(sizeof(*data)), \
+ mfree)
+
+/* event publishing function
+ * this function should be called from anywhere in the code to notify
+ * about an <e_type> and provide some relevant <data>
+ * that will be provided to subscriptions in <sub_list>
+ * that are subscribed to <e_type>.
+ * <data> should be provided using EVENT_HDL_CB_DATA helper macro
+ *
+ * Example:
+ * struct event_hdl_cb_data_server cb_data;
+ *
+ * /...
+ * cb_data initialization
+ * .../
+ *
+ * event_hdl_publish(NULL, EVENT_HDL_SUB_SERVER_UP, EVENT_HDL_CB_DATA(&cb_data));
+ */
+int event_hdl_publish(event_hdl_sub_list *sub_list,
+ struct event_hdl_sub_type e_type, const struct event_hdl_cb_data *data);
+
+/* ------ MISC/HELPER FUNCTIONS ------ */
+
+/* returns a statically allocated string that is
+ * the printable representation of <sub_type>
+ * or "N/A" if <sub_type> does not exist
+ */
+const char *event_hdl_sub_type_to_string(struct event_hdl_sub_type sub_type);
+
+/* returns the internal sub_type corresponding
+ * to the printable representation <name>
+ * or EVENT_HDL_SUB_NONE if no such event exists
+ * (see event_hdl-t.h for the complete list of supported types)
+ */
+struct event_hdl_sub_type event_hdl_string_to_sub_type(const char *name);
+
+/* Use this from sync hdl to ensure the function is executed
+ * in sync mode (and thus unsafe data is safe to use from this ctx)
+ * This macro is meant to prevent unsafe data access
+ * if code from sync function is copy pasted into
+ * async function (or if sync handler is changed
+ * to async handler without adapting the code)
+ * FIXME: do we BUG_ON, or simply warn and return from the function?
+ */
+#define EVENT_HDL_ASSERT_SYNC(cb) BUG_ON(!cb->_sync)
+
+/* check if a and b sub types are part of the same family */
+static inline int event_hdl_sub_family_equal(struct event_hdl_sub_type a, struct event_hdl_sub_type b)
+{
+ return (a.family == b.family);
+}
+
+/* compares 2 event_hdl_sub_type structs
+ * returns 1 if equal, 0 if not equal
+ */
+static inline int event_hdl_sub_type_equal(struct event_hdl_sub_type a, struct event_hdl_sub_type b)
+{
+ return (a.family == b.family && a.subtype == b.subtype);
+}
+
+/* performs subtraction between A and B event_hdl_sub_type
+ */
+static inline struct event_hdl_sub_type event_hdl_sub_type_del(struct event_hdl_sub_type a, struct event_hdl_sub_type b)
+{
+ if (unlikely(!a.family))
+ a.family = b.family;
+ if (unlikely(a.family != b.family))
+ return a;
+ a.subtype &= ~b.subtype;
+
+ return a;
+}
+
+/* performs addition between A and B event_hdl_sub_type
+ */
+static inline struct event_hdl_sub_type event_hdl_sub_type_add(struct event_hdl_sub_type a, struct event_hdl_sub_type b)
+{
+ if (unlikely(!a.family))
+ a.family = b.family;
+ if (unlikely(a.family != b.family))
+ return a;
+ a.subtype |= b.subtype;
+
+ return a;
+}
+
+/* use this function when you consumed an event in async handler
+ * (this will free the event so you must ensure that the event
+ * is already removed from the event queue and that you
+ * no longer make use of it)
+ */
+void event_hdl_async_free_event(struct event_hdl_async_event *e);
+
+/* use this for advanced async mode to initialize event queue */
+static inline void event_hdl_async_equeue_init(event_hdl_async_equeue *queue)
+{
+ MT_LIST_INIT(&queue->head);
+ queue->size = 0;
+}
+
+/* use this for advanced async mode to pop an event from event queue */
+static inline struct event_hdl_async_event *event_hdl_async_equeue_pop(event_hdl_async_equeue *queue)
+{
+ struct event_hdl_async_event *event;
+
+ event = MT_LIST_POP(&queue->head, struct event_hdl_async_event *, mt_list);
+ if (event)
+ HA_ATOMIC_DEC(&queue->size);
+ return event;
+}
+
+/* use this for advanced async mode to check if the event queue is empty */
+static inline int event_hdl_async_equeue_isempty(event_hdl_async_equeue *queue)
+{
+ return MT_LIST_ISEMPTY(&queue->head);
+}
+
+/* use this for advanced async mode to check if the event queue size */
+static inline uint32_t event_hdl_async_equeue_size(event_hdl_async_equeue *queue)
+{
+ return HA_ATOMIC_LOAD(&queue->size);
+}
+
+/* use this to initialize <sub_list> event subscription list */
+void event_hdl_sub_list_init(event_hdl_sub_list *sub_list);
+
+/* use this function when you need to destroy <sub_list>
+ * event subscription list
+ * All subscriptions will be removed and properly freed according
+ * to their types
+ */
+void event_hdl_sub_list_destroy(event_hdl_sub_list *sub_list);
+
+/* event_hdl tunables */
+extern struct event_hdl_tune event_hdl_tune;
+
+#endif /* _HAPROXY_EVENT_HDL_H */