mirror of
				https://git.haproxy.org/git/haproxy.git/
				synced 2025-10-31 08:30:59 +01:00 
			
		
		
		
	Released version 2.8-dev7 with the following main changes :
    - BUG/MINOR: stats: Don't replace sc_shutr() by SE_FL_EOS flag yet
    - BUG/MEDIUM: mux-h2: Be able to detect connection error during handshake
    - BUG/MINOR: quic: Missing padding in very short probe packets
    - MINOR: proxy/pool: prevent unnecessary calls to pool_gc()
    - CLEANUP: proxy: remove stop_time related dead code
    - DOC/MINOR: reformat configuration.txt's "quoting and escaping" table
    - MINOR: http_fetch: Add support for empty delim in url_param
    - MINOR: http_fetch: add case insensitive support for smp_fetch_url_param
    - MINOR: http_fetch: Add case-insensitive argument for url_param/urlp_val
    - REGTESTS : Add test support for case insentitive for url_param
    - BUG/MEDIUM: proxy/sktable: prevent watchdog trigger on soft-stop
    - BUG/MINOR: backend: make be_usable_srv() consistent when stopping
    - BUG/MINOR: ssl: Remove dead code in cli_parse_update_ocsp_response
    - BUG/MINOR: ssl: Fix potential leak in cli_parse_update_ocsp_response
    - BUG/MINOR: ssl: ssl-(min|max)-ver parameter not duplicated for bundles in crt-list
    - BUG/MINOR: quic: Wrong use of now_ms timestamps (cubic algo)
    - MINOR: quic: Add recovery related information to "show quic"
    - BUG/MINOR: quic: Wrong use of now_ms timestamps (newreno algo)
    - BUG/MINOR: quic: Missing max_idle_timeout initialization for the connection
    - MINOR: quic: Implement cubic state trace callback
    - MINOR: quic: Adjustments for generic control congestion traces
    - MINOR: quic: Traces adjustments at proto level.
    - MEDIUM: quic: Ack delay implementation
    - BUG/MINOR: quic: Wrong rtt variance computing
    - MINOR: cli: support filtering on FD types in "show fd"
    - MINOR: quic: Add a fake congestion control algorithm named "nocc"
    - CI: run smoke tests on config syntax to check memory related issues
    - CLEANUP: assorted typo fixes in the code and comments
    - CI: exclude doc/{design-thoughts,internals} from spell check
    - BUG/MINOR: quic: Remaining useless statements in cubic slow start callback
    - BUG/MINOR: quic: Cubic congestion control window may wrap
    - MINOR: quic: Add missing traces in cubic algorithm implementation
    - BUG/MAJOR: quic: Congestion algorithms states shared between the connection
    - BUG/MINOR: ssl: Undefined reference when building with OPENSSL_NO_DEPRECATED
    - BUG/MINOR: quic: Remove useless BUG_ON() in newreno and cubic algo implementation
    - MINOR: http-act: emit a warning when a header field name contains forbidden chars
    - DOC: config: strict-sni allows to start without certificate
    - MINOR: quic: Add trace to debug idle timer task issues
    - BUG/MINOR: quic: Unexpected connection closures upon idle timer task execution
    - BUG/MINOR: quic: Wrong idle timer expiration (during 20s)
    - BUILD: quic: 32bits compilation issue in cli_io_handler_dump_quic()
    - BUG/MINOR: quic: Possible wrong PTO computing
    - BUG/MINOR: tcpcheck: Be able to expect an empty response
    - BUG/MEDIUM: stconn: Add a missing return statement in sc_app_shutr()
    - BUG/MINOR: stream: Fix test on channels flags to set clientfin/serverfin touts
    - MINOR: applet: Uninline appctx_free()
    - MEDIUM: applet/trace: Register a new trace source with its events
    - CLEANUP: stconn: Remove remaining debug messages
    - BUG/MEDIUM: channel: Improve reports for shut in co_getblk()
    - BUG/MEDIUM: dns: Properly handle error when a response consumed
    - MINOR: stconn: Remove unecessary test on SE_FL_EOS before receiving data
    - MINOR: stconn/channel: Move CF_READ_DONTWAIT into the SC and rename it
    - MINOR: stconn/channel: Move CF_SEND_DONTWAIT into the SC and rename it
    - MINOR: stconn/channel: Move CF_NEVER_WAIT into the SC and rename it
    - MINOR: stconn/channel: Move CF_EXPECT_MORE into the SC and rename it
    - MINOR: mux-pt: Report end-of-input with the end-of-stream after a read
    - BUG/MINOR: mux-h1: Properly report EOI/ERROR on read0 in h1_rcv_pipe()
    - CLEANUP: mux-h1/mux-pt: Remove useless test on SE_FL_SHR/SE_FL_SHW flags
    - MINOR: mux-h1: Report an error to the SE descriptor on truncated message
    - MINOR: stconn: Always ack EOS at the end of sc_conn_recv()
    - MINOR: stconn/applet: Handle EOI in the applet .wake callback function
    - MINOR: applet: No longer set EOI on the SC
    - MINOR: stconn/applet: Handle EOS in the applet .wake callback function
    - MEDIUM: cache: Use the sedesc to report and detect end of processing
    - MEDIUM: cli: Use the sedesc to report and detect end of processing
    - MINOR: dns: Remove the test on the opposite SC state to send requests
    - MEDIUM: dns: Use the sedesc to report and detect end of processing
    - MEDIUM: spoe: Use the sedesc to report and detect end of processing
    - MEDIUM: hlua/applet: Use the sedesc to report and detect end of processing
    - MEDIUM: log: Use the sedesc to report and detect end of processing
    - MEDIUM: peers: Use the sedesc to report and detect end of processing
    - MINOR: sink: Remove the tests on the opposite SC state to process messages
    - MEDIUM: sink: Use the sedesc to report and detect end of processing
    - MEDIUM: stats: Use the sedesc to report and detect end of processing
    - MEDIUM: promex: Use the sedesc to report and detect end of processing
    - MEDIUM: http_client: Use the sedesc to report and detect end of processing
    - MINOR: stconn/channel: Move CF_EOI into the SC and rename it
    - MEDIUM: tree-wide: Move flags about shut from the channel to the SC
    - MINOR: tree-wide: Simplifiy some tests on SHUT flags by accessing SCs directly
    - MINOR: stconn/applet: Add BUG_ON_HOT() to be sure SE_FL_EOS is never set alone
    - MINOR: server: add SRV_F_DELETED flag
    - BUG/MINOR: server/del: fix srv->next pointer consistency
    - BUG/MINOR: stats: properly handle server stats dumping resumption
    - BUG/MINOR: sink: free forward_px on deinit()
    - BUG/MINOR: log: free log forward proxies on deinit()
    - MINOR: server: always call ssl->destroy_srv when available
    - MINOR: server: correctly free servers on deinit()
    - BUG/MINOR: hlua: hook yield does not behave as expected
    - MINOR: hlua: properly handle hlua_process_task HLUA_E_ETMOUT
    - BUG/MINOR: hlua: enforce proper running context for register_x functions
    - MINOR: hlua: Fix two functions that return nothing useful
    - MEDIUM: hlua: Dynamic list of frontend/backend in Lua
    - MINOR: hlua_fcn: alternative to old proxy and server attributes
    - MEDIUM: hlua_fcn: dynamic server iteration and indexing
    - MEDIUM: hlua_fcn/api: remove some old server and proxy attributes
    - CLEANUP: hlua: fix conflicting comment in hlua_ctx_destroy()
    - MINOR: hlua: add simple hlua reference handling API
    - MINOR: hlua: fix return type for hlua_checkfunction() and hlua_checktable()
    - BUG/MINOR: hlua: fix reference leak in core.register_task()
    - BUG/MINOR: hlua: fix reference leak in hlua_post_init_state()
    - BUG/MINOR: hlua: prevent function and table reference leaks on errors
    - CLEANUP: hlua: use hlua_ref() instead of luaL_ref()
    - CLEANUP: hlua: use hlua_pushref() instead of lua_rawgeti()
    - CLEANUP: hlua: use hlua_unref() instead of luaL_unref()
    - MINOR: hlua: simplify lua locking
    - BUG/MEDIUM: hlua: prevent deadlocks with main lua lock
    - MINOR: hlua_fcn: add server->get_rid() method
    - MINOR: hlua: support for optional arguments to core.register_task()
    - DOC: lua: silence "literal block ends without a blank line" Sphinx warnings
    - DOC: lua: silence "Unexpected indentation" Sphinx warnings
    - BUG/MINOR: event_hdl: fix rid storage type
    - BUG/MINOR: event_hdl: make event_hdl_subscribe thread-safe
    - MINOR: event_hdl: global sublist management clarification
    - BUG/MEDIUM: event_hdl: clean soft-stop handling
    - BUG/MEDIUM: event_hdl: fix async data refcount issue
    - MINOR: event_hdl: normal tasks support for advanced async mode
    - MINOR: event_hdl: add event_hdl_async_equeue_isempty() function
    - MINOR: event_hdl: add event_hdl_async_equeue_size() function
    - MINOR: event_hdl: pause/resume for subscriptions
    - MINOR: proxy: add findserver_unique_id() and findserver_unique_name()
    - MEDIUM: hlua/event_hdl: initial support for event handlers
    - MINOR: hlua/event_hdl: per-server event subscription
    - EXAMPLES: add basic event_hdl lua example script
    - MINOR: http-ana: Add a HTTP_MSGF flag to state the Expect header was checked
    - BUG/MINOR: http-ana: Don't switch message to DATA when waiting for payload
    - BUG/MINOR: quic: Possible crashes in qc_idle_timer_task()
    - MINOR: quic: derive first DCID from client ODCID
    - MINOR: quic: remove ODCID dedicated tree
    - MINOR: quic: remove address concatenation to ODCID
    - BUG/MINOR: mworker: unset more internal variables from program section
    - BUG/MINOR: errors: invalid use of memprintf in startup_logs_init()
    - MINOR: applet: Use unsafe version to get stream from SC in the trace function
    - BUG/MUNOR: http-ana: Use an unsigned integer for http_msg flags
    - MINOR: compression: Make compression offload a flag
    - MINOR: compression: Prepare compression code for request compression
    - MINOR: compression: Store algo and type for both request and response
    - MINOR: compression: Count separately request and response compression
    - MEDIUM: compression: Make it so we can compress requests as well.
    - BUG/MINOR: lua: remove incorrect usage of strncat()
    - CLEANUP: tcpcheck: remove the only occurrence of sprintf() in the code
    - CLEANUP: ocsp: do no use strpcy() to copy a path!
    - CLEANUP: tree-wide: remove strpcy() from constant strings
    - CLEANUP: opentracing: remove the last two occurrences of strncat()
    - BUILD: compiler: fix __equals_1() on older compilers
    - MINOR: compiler: define a __attribute__warning() macro
    - BUILD: bug.h: add a warning in the base API when unsafe functions are used
    - BUG/MEDIUM: listeners: Use the right parameters for strlcpy2().
		
	
			
		
			
				
	
	
		
			1016 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			1016 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|                    -----------------------------------------
 | |
|                          event_hdl Guide - version 2.8
 | |
|                           ( Last update: 2022-11-14 )
 | |
|                    ------------------------------------------
 | |
| 
 | |
| ABSTRACT
 | |
| --------
 | |
| 
 | |
| The event_hdl support is a new feature of HAProxy 2.7. It is a way to easily
 | |
| handle general events in a simple to maintain fashion, while keeping core code
 | |
| impact to the bare minimum.
 | |
| 
 | |
| This document first describes how to use already supported events,
 | |
| then how to add support for your very own events.
 | |
| 
 | |
| This feature is quite new for now. The API is not frozen and will be
 | |
| updated/modified/improved/extended as needed.
 | |
| 
 | |
| SUMMARY
 | |
| -------
 | |
| 
 | |
|   1.    event_hdl introduction
 | |
|   2.    How to handle existing events
 | |
|   2.1       SYNC mode
 | |
|   2.2       ASYNC mode
 | |
|   2.2.1           normal version
 | |
|   2.2.2           task version
 | |
|   2.3       Advanced features
 | |
|   2.3.1           sub_mgmt
 | |
|   2.3.2		  subscription external lookups
 | |
|   2.3.3           subscription ptr
 | |
|   2.3.4           private_free
 | |
|   3.    How to add support for new events
 | |
|   3.1       Declaring a new event data structure
 | |
|   3.2       Publishing an event
 | |
|   4.	Subscription lists
 | |
|   5.	misc/helper functions
 | |
| 
 | |
| 
 | |
| 1. EVENT_HDL INTRODUCTION
 | |
| -----------------------
 | |
| 
 | |
| EVENT_HDL provides two complementary APIs, both are implemented
 | |
| in src/event_hdl.c and include/haproxy/event_hdl(-t).h:
 | |
| 
 | |
| One API targeting developers that want to register event
 | |
| handlers that will be notified when specific events occur in the process.
 | |
| (See section 2.)
 | |
| 
 | |
| One API targeting developers that want to notify registered handlers about
 | |
| an event that is happening in the process.
 | |
| (See section 3.)
 | |
| 
 | |
| 2. HOW TO HANDLE EXISTING EVENTS
 | |
| ---------------------
 | |
| 
 | |
| To handle existing events, you must first decide which events you're
 | |
| interested in.
 | |
| 
 | |
| event types are defined as follow:
 | |
| 
 | |
| ```
 | |
| 	/* type for storing event subscription type */
 | |
| 	typedef struct event_hdl_sub_type
 | |
| 	{
 | |
| 		/* up to 256 families, non cumulative, adjust if needed */
 | |
| 		uint8_t family;
 | |
| 		/* up to 16 sub types using bitmasks, adjust if needed */
 | |
| 		uint16_t subtype;
 | |
| 	} event_hdl_sub_type;
 | |
| ```
 | |
| 
 | |
| For an up to date list of already supported events,
 | |
| please refer to include/haproxy/event_hdl-t.h
 | |
| At the end of the file you will find existing event types.
 | |
| 
 | |
| Each event family provides an unique data structure that will
 | |
| be provided to the event handler (registered to one or more
 | |
| event subtypes) when such events occur.
 | |
| 
 | |
| An event handler can subscribe to a single event family type at a time, but
 | |
| within the family type it can subscribe to multiple event subtypes.
 | |
| 
 | |
| 	For example, let's consider the SERVER family type.
 | |
| 
 | |
| 	Let's assume it provides the event_hdl_cb_data_server data structure.
 | |
| 
 | |
| 	We can register a handler that will be notified for
 | |
| 	every SERVER event types using:
 | |
| 		EVENT_HDL_SUB_SERVER
 | |
| 
 | |
| 		This will include EVENT_HDL_SUB_SERVER_ADD,
 | |
| 				  EVENT_HDL_SUB_SERVER_DEL [...]
 | |
| 
 | |
| 	But we can also subscribe to a specific subtype only,
 | |
| 	for example server deletion:
 | |
| 		EVENT_HDL_SUB_SERVER_DEL
 | |
| 
 | |
| 	You can even combine multiple SERVER subtypes using
 | |
| 	event_hdl_sub_type_add function helper:
 | |
| 		event_hdl_sub_type_add(EVENT_HDL_SUB_SERVER_DEL,
 | |
| 				       EVENT_HDL_SUB_SERVER_ADD)
 | |
| 
 | |
| 		(will refer to server deletion as well as server addition)
 | |
| 
 | |
| Registering a handler comes into multiple flavors:
 | |
| 
 | |
| 	SYNC mode:
 | |
| 		handler is called in a blocking manner directly from the
 | |
| 		thread that publishes the event.
 | |
| 		This mode should be used with precaution because it could
 | |
| 		slow the caller or cause deadlocks if used improperly.
 | |
| 
 | |
| 	Sync mode is useful when you directly depend on data or
 | |
| 	state consistency from the caller.
 | |
| 
 | |
| 	Sync mode gives you access to unsafe elements in the data structure
 | |
| 	provided by the caller (again, see event_hdl-t.h for more details).
 | |
| 	The data structure may provide lock hints in the unsafe section
 | |
| 	so that you know which locks are already held within the
 | |
| 	calling context, hopefully preventing you from relocking
 | |
| 	an already locked element and preventing deadlocks.
 | |
| 
 | |
| 	ASYNC mode:
 | |
| 		handler is called in a non-blocking manner
 | |
| 		(in a dedicated tasklet),
 | |
| 		thus, the caller (that published the event) is not affected
 | |
| 		by the handler. (time wise and data wise)
 | |
| 
 | |
| 		This is the safest way to handle events,
 | |
| 		but it also comes with a limitation:
 | |
| 
 | |
| 		unsafe elements in the data structure provided by
 | |
| 		the caller SHOULD be used under NO circumstances.
 | |
| 		Indeed, only safe elements are meant to be used
 | |
| 		when handling the event in async mode.
 | |
| 
 | |
| 	ASYNC mode is declined in 2 different versions:
 | |
| 		normal:
 | |
| 			handler is simply a function pointer
 | |
| 			(same prototype as sync mode),
 | |
| 			that is called asynchronously with relevant data
 | |
| 			when the event is published. Only difference with
 | |
| 			sync mode here is that 'unsafe' data provided
 | |
| 			by the data structure may not be used.
 | |
| 		task:
 | |
| 			handler is a user defined task(let) that uses an event
 | |
| 			queue to consume pending events.
 | |
| 			This mode is interesting when you need to perform
 | |
| 			advanced operations or you need to handle the event
 | |
| 			in an already existing task context.
 | |
| 			It is a bit more complicated to setup, but really
 | |
| 			nothing to worry about, some examples will be
 | |
| 			provided later in this document.
 | |
| 
 | |
| event subscription is performed using the function:
 | |
| 
 | |
| 	event_hdl_subscribe(list, event, hdl);
 | |
| 
 | |
| 	The function returns 1 in case of success,
 | |
| 	and 0 in case of failure (bad arguments, or memory error)
 | |
| 
 | |
| 	The function may BUG_ON if used improperly (invalid arguments)
 | |
| 
 | |
| 	<list> is either user specified list used to store the
 | |
| 	new subscription, or NULL if you want to store the subscription
 | |
| 	in the process global list.
 | |
| 
 | |
| 	<list> is also asked when publishing an event,
 | |
| 	so specifying list could be useful, if, for example,
 | |
| 	you only want to subscribe to a specific subscription list
 | |
| 	(see this as a scope for example, NULL being full scope,
 | |
| 	and specific list being limited scope)
 | |
| 
 | |
| 	We will use server events as an example:
 | |
| 
 | |
| 	You could register to events for ALL servers by using the
 | |
| 	global list (NULL), or only to a specific server events
 | |
| 	by using the subscription list dedicated to a single server.
 | |
| 
 | |
| 	<event> are the events (family.subtypes) you're subscribing to
 | |
| 
 | |
| 	<hdl> contains required handler options, it must be provided using
 | |
| 	EVENT_HDL_(TASK_)(A)SYNC() and EVENT_HDL_ID_(TASK_)(A)SYNC()
 | |
| 	helper macros.
 | |
| 
 | |
| 	See include/haproxy/event_hdl.h or below to know which macro
 | |
| 	best suits your needs.
 | |
| 
 | |
| 	When registering a handler, you have the ability to provide an
 | |
| 	unique ID (using EVENT_HDL_ID_ macro family) that could be used
 | |
| 	later to perform lookups on the subscription.
 | |
| 	ID is stored as an uint64_t hash that is expected to be computed using
 | |
| 	general purpose event_hdl_id inline function provided by event_hdl.h.
 | |
| 	Not providing an ID (using EVENT_HDL_ macro family)
 | |
| 	results in the subscription being considered as anonymous.
 | |
| 	As the name implies, anonymous subscriptions don't support lookups.
 | |
| 
 | |
| 2.1 SYNC MODE
 | |
| ---------------------
 | |
| 
 | |
| Example, you want to register a sync handler that will be called when
 | |
| a new server is added.
 | |
| 
 | |
| Here is what the handler function will look like:
 | |
| ```
 | |
| void my_sync_handler(const struct event_hdl_cb *cb, void *private)
 | |
| {
 | |
| 	const struct event_hdl_cb_data_server *server = cb->e_data;
 | |
| 
 | |
| 	/* using EVENT_HDL_ASSERT_SYNC is a good practice to ensure
 | |
| 	 * that the function breaks if used in async mode
 | |
| 	 * (because we will access unsafe data in this function that
 | |
| 	 * is sync mode only)
 | |
| 	 */
 | |
| 	EVENT_HDL_ASSERT_SYNC(cb);
 | |
| 	printf("I've been called for '%s', private = %p\n",
 | |
| 	       event_hdl_sub_type_to_string(cb->e_type), private);
 | |
| 	printf("server name is '%s'\n", server->safe.name);
 | |
| 
 | |
| 	/* here it is safe to use unsafe data */
 | |
| 	printf("server ptr is '%p'\n", server->unsafe.ptr);
 | |
| 
 | |
| 	/* from here you have the possibility to manage the subscription
 | |
| 	 * cb->sub_mgmt->unsub(cb->sub_mgmt);
 | |
| 	 * // hdl will be removed from the subscription list
 | |
| 	 */
 | |
| }
 | |
| ```
 | |
| 
 | |
| Here is how you perform the subscription:
 | |
| 
 | |
| anonymous subscription:
 | |
| ```
 | |
| 	int private = 10;
 | |
| 
 | |
| 	event_hdl_subscribe(NULL, EVENT_HDL_SUB_SERVER_ADD,
 | |
| 			    EVENT_HDL_SYNC(my_sync_handler, &private, NULL));
 | |
| ```
 | |
| 
 | |
| identified subscription:
 | |
| ```
 | |
| 	int private = 10;
 | |
| 	uint64_t id = event_hdl_id("test", "sync");
 | |
| 
 | |
| 	event_hdl_subscribe(NULL, EVENT_HDL_SUB_SERVER_ADD,
 | |
| 			    EVENT_HDL_ID_SYNC(id,
 | |
| 					      my_sync_handler,
 | |
| 					      &private,
 | |
| 					      NULL));
 | |
| 
 | |
| ```
 | |
| 
 | |
| identified subscription where freeing private is required when subscription ends:
 | |
| (also works for anonymous)
 | |
| (more on this feature in 2.3.4)
 | |
| ```
 | |
| 	int *private = malloc(sizeof(*private));
 | |
| 	uint64_t id = event_hdl_id("test", "sync_free");
 | |
| 
 | |
| 	BUG_ON(!private);
 | |
| 	*private = 10;
 | |
| 
 | |
| 	/* passing free as 'private_free' function so that
 | |
| 	 * private can be freed when unregistering is performed
 | |
| 	 */
 | |
| 	event_hdl_subscribe(NULL, EVENT_HDL_SUB_SERVER_ADD,
 | |
| 			    EVENT_HDL_ID_SYNC(id,
 | |
| 					      my_sync_handler,
 | |
| 					      private,
 | |
| 					      free));
 | |
| 
 | |
| 
 | |
| 	/* ... */
 | |
| 
 | |
| 	// unregistering the identified hdl
 | |
| 	if (event_hdl_lookup_unsubscribe(NULL, id)) {
 | |
| 		printf("private will automatically be freed!\n");
 | |
| 	}
 | |
| ```
 | |
| 
 | |
| 2.2 ASYNC MODE
 | |
| ---------------------
 | |
| 
 | |
| As mentioned before, async mode comes in 2 flavors, normal and task.
 | |
| 
 | |
| 2.2.1 NORMAL VERSION
 | |
| ---------------------
 | |
| 
 | |
| Normal is meant to be really easy to use, and highly compatible with sync mode.
 | |
| 
 | |
| (Handler can easily be converted or copy pasted from async to sync mode
 | |
| and vice versa)
 | |
| 
 | |
| Quick warning about sync to async handler conversion:
 | |
| 
 | |
| please always use EVENT_HDL_ASSERT_SYNC whenever you develop a
 | |
| sync handler that performs unsafe data access.
 | |
| 
 | |
| This way, if the handler were to be converted or copy pasted as is to
 | |
| async mode without removing unsafe data accesses,
 | |
| the handler will forcefully fail to indicate an error so that you
 | |
| know something has to be fixed in your handler code.
 | |
| 
 | |
| Back to our async handler, let's say you want to declare an
 | |
| async handler that will be called when a new server is added.
 | |
| 
 | |
| Here is what the handler function will look like:
 | |
| ```
 | |
| void my_async_handler(const struct event_hdl_cb *cb, void *private)
 | |
| {
 | |
| 	const struct event_hdl_cb_data_server *server = cb->e_data;
 | |
| 
 | |
| 	printf("I've been called for '%s', private = %p\n",
 | |
| 	       event_hdl_sub_type_to_string(cb->e_type), private);
 | |
| 	printf("server name is '%s'\n", server->safe.name);
 | |
| 
 | |
| 	/* here it is not safe to use unsafe data */
 | |
| 
 | |
| 	/* from here you have the possibility to manage the subscription
 | |
| 	 * cb->sub_mgmt->unsub(cb->sub_mgmt);
 | |
| 	 * // hdl will be removed from the subscription list
 | |
| 	 */
 | |
| }
 | |
| ```
 | |
| 
 | |
| Note that it is pretty similar to sync handler, except
 | |
| for unsafe data access.
 | |
| 
 | |
| Here is how you declare the subscription:
 | |
| 
 | |
| anonymous subscription:
 | |
| ```
 | |
| 	int private = 10;
 | |
| 
 | |
| 	event_hdl_subscribe(NULL, EVENT_HDL_SUB_SERVER_ADD,
 | |
| 			    EVENT_HDL_ASYNC(my_async_handler, &private, NULL));
 | |
| ```
 | |
| 
 | |
| identified subscription:
 | |
| ```
 | |
| 	int private = 10;
 | |
| 	uint64_t id = event_hdl_id("test", "async");
 | |
| 
 | |
| 	event_hdl_subscribe(NULL, EVENT_HDL_SUB_SERVER_ADD,
 | |
| 			    EVENT_HDL_ID_ASYNC(id,
 | |
| 					       my_async_handler,
 | |
| 					       &private,
 | |
| 					       NULL));
 | |
| 
 | |
| ```
 | |
| 
 | |
| identified subscription where freeing private is required when subscription ends:
 | |
| (also works for anonymous)
 | |
| ```
 | |
| 	int *private = malloc(sizeof(*private));
 | |
| 	uint64_t id = event_hdl_id("test", "async_free");
 | |
| 
 | |
| 	BUG_ON(!private);
 | |
| 	*private = 10;
 | |
| 
 | |
| 	/* passing free as 'private_free' function so that
 | |
| 	 * private can be freed when unregistering is performed
 | |
| 	 */
 | |
| 	event_hdl_subscribe(NULL, EVENT_HDL_SUB_SERVER_ADD,
 | |
| 			    EVENT_HDL_ID_ASYNC(id,
 | |
| 					       my_async_handler,
 | |
| 					       private,
 | |
| 					       free));
 | |
| 
 | |
| 	/* ... */
 | |
| 
 | |
| 	// unregistering the identified hdl
 | |
| 	if (event_hdl_lookup_unsubscribe(NULL, id)) {
 | |
| 		printf("private will automatically be freed when "
 | |
| 		       "all pending events referencing private "
 | |
| 		       "are consumed!\n");
 | |
| 	}
 | |
| ```
 | |
| 
 | |
| 2.2.2 TASK VERSION
 | |
| ---------------------
 | |
| 
 | |
| task version requires a bit more setup, but it's pretty
 | |
| straightforward actually.
 | |
| 
 | |
| 
 | |
| First, you need to initialize an event queue that will be used
 | |
| by event_hdl facility to push you events according to your subscription:
 | |
| 
 | |
| ```
 | |
| 	event_hdl_async_equeue my_q;
 | |
| 
 | |
| 	event_hdl_async_equeue_init(&my_q);
 | |
| ```
 | |
| 
 | |
| 
 | |
| Then, you need to declare a task(let) (or reuse existing task(let))
 | |
| 
 | |
| It is your responsibility to make sure that the task(let) still exists
 | |
| (is not freed) when calling the subscribe function
 | |
| (and that the task remains valid as long as the subscription is).
 | |
| 
 | |
| When a subscription referencing your task is over
 | |
| (either ended because of list purge, external code or from the handler itself),
 | |
| you will receive the EVENT_HDL_SUB_END event.
 | |
| When you receive this event, you must free it as usual and you can safely
 | |
| assume that the related subscription won't be sending you any more events.
 | |
| 
 | |
| Here is what your task will look like (involving a single event queue):
 | |
| 
 | |
| ```
 | |
| struct task *event_hdl_async_task_my(struct task *task,
 | |
|                                      void *ctx, unsigned int state)
 | |
| {
 | |
| 	struct tasklet *tl = (struct tasklet *)task;
 | |
| 	event_hdl_async_equeue *queue = ctx;
 | |
| 	struct event_hdl_async_event *event;
 | |
| 	struct event_hdl_cb_data_server *srv;
 | |
| 	uint8_t done = 0;
 | |
| 
 | |
| 	while ((event = event_hdl_async_equeue_pop(queue)))
 | |
| 	{
 | |
| 		if (event_hdl_sub_type_equal(event->type, EVENT_HDL_SUB_END)) {
 | |
| 			done = 1;
 | |
| 			event_hdl_async_free_event(event);
 | |
| 			printf("no more events to come, "
 | |
| 			       "subscription is over\n");
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		srv = event->data;
 | |
| 
 | |
| 	        printf("task event %s, %d (name = %s)\n",
 | |
| 		       event_hdl_sub_type_to_string(event->type),
 | |
| 		       *((int *)event->private), srv->safe.name);
 | |
| 		event_hdl_async_free_event(event);
 | |
| 	}
 | |
| 
 | |
| 	if (done) {
 | |
| 		/* our job is done, subscription is over:
 | |
| 		 * no more events to come
 | |
| 		 */
 | |
| 		tasklet_free(tl);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	return task;
 | |
| }
 | |
| 
 | |
| ```
 | |
| 
 | |
| Here is how we would initialize the task event_hdl_async_task_my:
 | |
| ```
 | |
| 	struct tasklet *my_task;
 | |
| 
 | |
| 	my_task = tasklet_new();
 | |
| 	BUG_ON(!my_task);
 | |
| 	my_task->context = &my_q; // we declared my_q previously in this example
 | |
| 	/* we declared event_hdl_async_task_my previously
 | |
| 	 * in this example
 | |
| 	 */
 | |
| 	my_task->process = event_hdl_async_task_my;
 | |
| 
 | |
| ```
 | |
| 
 | |
| Given our task and our previously initialized event queue, here is how
 | |
| to perform the subscription:
 | |
| ```
 | |
| 	int test_val = 11;
 | |
| 	uint64_t id = event_hdl_id("test", "my_task");
 | |
| 
 | |
| 	/* anonymous variant */
 | |
| 	event_hdl_subscribe(NULL, EVENT_HDL_SUB_SERVER_ADD,
 | |
| 			    EVENT_HDL_ASYNC_TASK(&my_q,
 | |
| 						 my_task,
 | |
| 						 &test_val,
 | |
| 						 NULL));
 | |
| 	/* identified variant */
 | |
| 	event_hdl_subscribe(NULL, EVENT_HDL_SUB_SERVER_ADD,
 | |
| 			    EVENT_HDL_ID_ASYNC_TASK(id,
 | |
| 						    &my_q,
 | |
| 						    my_task,
 | |
| 						    &test_val,
 | |
| 						    NULL));
 | |
| ```
 | |
| 
 | |
| Note:  it is not recommended to perform multiple subscriptions
 | |
|        that share the same event queue or same task(let) (or both)
 | |
| 
 | |
|        That is, having more than one subscription waking a task(let)
 | |
|        and/or feeding the same event queue.
 | |
| 
 | |
|        No check is performed on this when registering, so the API
 | |
|        won't prevent you from doing it.
 | |
| 
 | |
|        If you are going to do this anyway despite this warning:
 | |
| 
 | |
|        In the case you need to stop the task prematurely
 | |
| 	(if this is not going to happen please skip this paragraph):
 | |
| 		You are responsible for acknowledging the end of every
 | |
| 		active subscriptions that refer to your task or
 | |
| 		your event queue(s).
 | |
| 		And you really don't want a subscription associated with
 | |
| 		your task or event queue to keep going when the task
 | |
| 		is not active anymore because:
 | |
| 			1: there will be memory leak
 | |
| 			   (event queue might continue to receive new events)
 | |
| 			2: there is a 100% chance of process crash in case of event
 | |
| 			   because we will try to wake a task (your task)
 | |
| 			   that might already be freed. Thus UAF will occur.
 | |
| 
 | |
| 2.3 ADVANCED FEATURES
 | |
| -----------------------
 | |
| 
 | |
| We've already covered some of these features in the previous examples.
 | |
| Here is a documented recap.
 | |
| 
 | |
| 
 | |
| 2.3.1 SUB MGMT
 | |
| -----------------------
 | |
| 
 | |
| From an event handler context, either sync or async mode:
 | |
| 	You have the ability to directly manage the subscription
 | |
| 	that provided the event.
 | |
| 
 | |
| As of today, these actions are supported:
 | |
| 	- Consulting the subscription.
 | |
| 	- Modifying the subscription (resubscribing within same family)
 | |
| 	- Unregistering the subscription (unsubscribing).
 | |
| 
 | |
| To do this, consider the following structure:
 | |
| ```
 | |
| 	struct event_hdl_sub_mgmt
 | |
| 	{
 | |
| 		/* manage subscriptions from event
 | |
| 		 * this must not be used directly because
 | |
| 		 * locking might be required
 | |
| 		 */
 | |
| 		struct event_hdl_sub *this;
 | |
| 		/* safe functions than can be used from
 | |
| 		 * event context (sync and async mode)
 | |
| 		 */
 | |
| 		struct event_hdl_sub_type (*getsub)(const struct event_hdl_sub_mgmt *);
 | |
| 		int (*resub)(const struct event_hdl_sub_mgmt *, struct event_hdl_sub_type);
 | |
| 		void (*unsub)(const struct event_hdl_sub_mgmt *);
 | |
| 	};
 | |
| 
 | |
| ```
 | |
| A reference to this structure is provided in every handler mode.
 | |
| 
 | |
| Sync mode and normal async mode (directly from the callback data pointer):
 | |
| ```
 | |
| 	const struct event_hdl_cb *cb;
 | |
| 	// cb->sub_mgmt
 | |
| 	// cb->sub_mgmt->getsub(cb->sub_mgmt);
 | |
| 	// cb->sub_mgmt->unsub(cb->sub_mgmt);
 | |
| ```
 | |
| 
 | |
| task and notify async modes (from the event):
 | |
| ```
 | |
| 	struct event_hdl_async_event *event;
 | |
| 	// event->sub_mgmt
 | |
| 	// event->sub_mgmt.getsub(&event->sub_mgmt);
 | |
| 	// event->sub_mgmt.unsub(&event->sub_mgmt);
 | |
| ```
 | |
| 
 | |
| 2.3.2 SUBSCRIPTION EXTERNAL LOOKUPS
 | |
| -----------------------
 | |
| 
 | |
| As you've seen in 2.3.1, managing the subscription directly
 | |
| from the handler is a possibility.
 | |
| 
 | |
| But for identified subscriptions, you also have the ability to
 | |
| perform lookups and management operations on specific subscriptions
 | |
| within a list based on their ID, anywhere in the code.
 | |
| 
 | |
| /!\ This feature is not available for anonymous subscriptions /!\
 | |
| 
 | |
| Here are the actions already supported:
 | |
| 
 | |
| 	- unregistering a subscription (unsubscribing)
 | |
| 	- updating a subscription (resubscribing within same family)
 | |
| 	- getting a ptr/reference to the subscription
 | |
| 
 | |
| Those functions are documented in event_hdl.h
 | |
| (search for EVENT_HDL_LOOKUP section).
 | |
| 
 | |
| To select a specific subscription, you must provide
 | |
| the unique identifier (uint64_t hash) that was provided when subscribing.
 | |
| (using event_hdl_id(scope, name) function)
 | |
| 
 | |
| Notes:
 | |
| 	"id" is only unique within a given subscription list.
 | |
| 
 | |
| 	When using event_hdl_id to provide the id:
 | |
| 		It is your responsibility to make sure that you "own"
 | |
| 		the scope if you rely on name to be "free".
 | |
| 
 | |
| 		As ID computation is backed by xxhash hash API,
 | |
| 		you should be aware that hash collisions could occur,
 | |
| 		but are extremely rare and are thus considered safe
 | |
| 		enough for this usage.
 | |
| 		(see event_hdl.h for implementation details)
 | |
| 
 | |
| 		Please consider ptr based subscription management if
 | |
| 		these limitations don't fit your requirements.
 | |
| 
 | |
| Here are some examples:
 | |
| 
 | |
| unsubscribing:
 | |
| ```
 | |
| 	/* registering "scope":"name" subscription */
 | |
| 	event_hdl_subscribe(NULL, EVENT_HDL_SUB_SERVER_ADD,
 | |
| 	  EVENT_HDL_ID_SYNC(event_hdl_id("scope", "name"),
 | |
| 			    my_sync_handler,
 | |
| 			    NULL,
 | |
| 			    NULL));
 | |
| 	/* unregistering "scope":"name" subscription */
 | |
| 	event_hdl_lookup_unsubscribe(NULL, event_hdl_id("scope", "name"));
 | |
| ```
 | |
| 
 | |
| 2.3.3 SUBSCRIPTION PTR
 | |
| -----------------------
 | |
| 
 | |
| To manage existing subscriptions from external code,
 | |
| we already talked about identified subscriptions that
 | |
| allow lookups within list.
 | |
| 
 | |
| But there is another way to accomplish this.
 | |
| 
 | |
| When subscribing, you can use the event_hdl_subscribe_ptr() function
 | |
| variant (same arguments as event_hdl_subscribe()).
 | |
| 
 | |
| What this function does, is instead of returning 1 in case of
 | |
| success and 0 in case of failure: it returns a valid subscription ptr
 | |
| for success and NULL for failure.
 | |
| 
 | |
| Returned ptr is guaranteed to remain valid even if subscription
 | |
| is ended meanwhile because the ptr is internally guarded with a refcount.
 | |
| 
 | |
| Thus, as long as you don't explicitly unregister the subscription with
 | |
| event_hdl_unsubscribe() or drop the reference using event_hdl_drop(),
 | |
| subscription ptr won't be freed.
 | |
| 
 | |
| This ptr will allow you to use the following subscription
 | |
| management functions from external code:
 | |
| 
 | |
| 	- event_hdl_take() to increment subscription ptr refcount
 | |
| 	(automatically incremented when using event_hdl_subscribe_ptr)
 | |
| 	- event_hdl_drop() to decrement subscription ptr refcount
 | |
| 	- event_hdl_resubscribe() to modify subscription subtype
 | |
| 	- event_hdl_unsubscribe() to end the subscription
 | |
| 	(refcount will be automatically decremented)
 | |
| 
 | |
| Here is an example:
 | |
| ```
 | |
| 	struct event_hdl_sub *sub_ptr;
 | |
| 
 | |
| 	/* registering a subscription with subscribe_ptr */
 | |
| 	sub_ptr = event_hdl_subscribe_ptr(NULL, EVENT_HDL_SUB_SERVER_ADD,
 | |
| 			    EVENT_HDL_SYNC(my_sync_handler,
 | |
| 					   NULL,
 | |
| 					   NULL));
 | |
| 
 | |
| 	/* ... */
 | |
| 
 | |
| 	/* unregistering the subscription */
 | |
| 	event_hdl_unsubscribe(sub_ptr);
 | |
| ```
 | |
| 
 | |
| Regarding identified subscriptions that were registered using the non ptr
 | |
| subscribe function:
 | |
| 
 | |
| You still have the ability to get a reference to the related subscription
 | |
| (if it still exists), by using event_hdl_lookup_take(list, id) function.
 | |
| event_hdl_lookup_take will return a subscription ptr in case of success
 | |
| and NULL in case of failure.
 | |
| Returned ptr reference is automatically incremented, so it is safe to use.
 | |
| 
 | |
| Please don't forget to drop the reference
 | |
| when holding the ptr is no longer needed.
 | |
| 
 | |
| Example:
 | |
| ```
 | |
| 	struct event_hdl_sub *sub_ptr = NULL;
 | |
| 
 | |
| 	/* registering subscription id "test":"ptr" with normal subscribe */
 | |
| 	if (event_hdl_subscribe(NULL, EVENT_HDL_SUB_SERVER_ADD,
 | |
| 	    EVENT_HDL_ID_SYNC(event_hdl_id("test", "ptr"),
 | |
| 			      my_sync_handler,
 | |
| 			      NULL,
 | |
| 			      NULL))) {
 | |
| 		/* fetch ref to subscription "test":"ptr" */
 | |
| 		sub_ptr = event_hdl_lookup_take(NULL,
 | |
| 						event_hdl_id("test", "ptr"));
 | |
| 
 | |
| 		/* unregister the subscription using lookup */
 | |
| 		event_hdl_lookup_unsubscribe(NULL,
 | |
| 					     event_hdl_id("test", "ptr"));
 | |
| 	}
 | |
| 
 | |
| 	/* ... */
 | |
| 
 | |
| 	/* unregistering the subscription with ptr
 | |
| 	 * will do nothing because subscription was
 | |
| 	 * already ended by lookup_unsubscribe, but
 | |
| 	 * here the catch is that sub_ptr is still
 | |
| 	 * valid so this won't crash the program
 | |
| 	 */
 | |
| 	if (sub_ptr) {
 | |
| 		event_hdl_unsubscribe(sub_ptr);
 | |
| 		/* unsubscribe will also result in subscription
 | |
| 		 * reference drop, thus subscription will be freed here
 | |
| 		 * because sub_ptr was the last active reference.
 | |
| 		 * You must not use sub_ptr anymore past this point
 | |
| 		 * or UAF could occur
 | |
| 		 */
 | |
| 	}
 | |
| 
 | |
| ```
 | |
| 
 | |
| 2.3.4 PRIVATE FREE
 | |
| -----------------------
 | |
| 
 | |
| Upon handler subscription, you have the ability to provide
 | |
| a private data pointer that will be passed to the handler
 | |
| when subscribed events occur.
 | |
| 
 | |
| Sometimes this private data pointer will rely on dynamically allocated memory.
 | |
| And in such cases, you have no way of knowing when
 | |
| freeing this pointer can be done safely.
 | |
| 
 | |
| You could be tempted to think that freeing right after performing
 | |
| the unsubscription could be safe.
 | |
| But this is not the case, remember we could be dealing with async handlers
 | |
| that might still consume pending events even though unsubscription
 | |
| has been performed from external code.
 | |
| 
 | |
| To deal with this, you may want to provide the private_free
 | |
| function pointer upon subscription.
 | |
| This way, private_free function will automatically be called
 | |
| (with private as argument) when private is no longer be used.
 | |
| 
 | |
| Example:
 | |
| First we declare our private free function:
 | |
| ```
 | |
| void my_private_free(void *my_private_data) {
 | |
| 	/* here we only call free,
 | |
| 	 * but you could do more sophisticated stuff
 | |
| 	 */
 | |
| 	free(my_private_data);
 | |
| }
 | |
| ```
 | |
| Then:
 | |
| ```
 | |
| 	char *my_private_data = strdup("this string needs to be freed");
 | |
| 
 | |
| 	BUG_ON(!my_private_data);
 | |
| 
 | |
| 	event_hdl_subscribe(NULL, EVENT_HDL_SUB_SERVER_DEL,
 | |
| 	    EVENT_HDL_ID_ASYNC(event_hdl_id("test", "private"),
 | |
| 			       my_async_handler,
 | |
| 			       my_private_data,
 | |
| 			       my_private_free));
 | |
| 
 | |
| 	/* freeing my_private_data is not required anymore,
 | |
| 	 * it will be automatically freed by our private free
 | |
| 	 * function when subscription ends
 | |
| 	 */
 | |
| 
 | |
| 	/* unregistering "test":"private" subscription */
 | |
| 	event_hdl_lookup_unsubscribe(NULL, event_hdl_id("test", "private"));
 | |
| 
 | |
| 	/* my_private_free will be automatically summoned when my_private_data
 | |
| 	 * is not referenced anymore
 | |
| 	 */
 | |
| ```
 | |
| 
 | |
| 3 HOW TO ADD SUPPORT FOR NEW EVENTS
 | |
| -----------------------
 | |
| 
 | |
| Adding support for a new event is pretty straightforward.
 | |
| 
 | |
| First, you need to declare a new event subtype in event_hdl-t.h file
 | |
| (bottom of the file).
 | |
| 
 | |
| You might want to declare a whole new event family, in which case
 | |
| you declare both the new family and the associated subtypes (if any).
 | |
| 
 | |
| ```
 | |
| 	#define EVENT_HDL_SUB_NEW_FAMILY                EVENT_HDL_SUB_FAMILY(4)
 | |
| 	#define EVENT_HDL_SUB_NEW_FAMILY_SUBTYPE_1      EVENT_HDL_SUB_TYPE(4,0)
 | |
| ```
 | |
| 
 | |
| Then, you need to update the event_hdl_sub_type_map map,
 | |
| defined in src/event_hdl.c file (top of the file)
 | |
| to add string to event type and event type to string conversion support.
 | |
| You just need to add the missing entries corresponding to
 | |
| the event family / subtypes you've defined.
 | |
| 
 | |
| Please follow this procedure:
 | |
| 	You only added a new subtype to existing family: go to section 3.2
 | |
| 	You added a new family: go to section 3.1
 | |
| 
 | |
| 3.1 DECLARING A NEW EVENT DATA STRUCTURE
 | |
| -----------------------
 | |
| 
 | |
| You have the ability to provide additional data for a given
 | |
| event family when such events occur.
 | |
| 
 | |
| Note that it is not mandatory: you could simply declare a new event family
 | |
| that does not provide any data.
 | |
| If this is your case, you can skip this section and go to 3.2 section.
 | |
| 
 | |
| Now, take a look at this event data structure template
 | |
| (also defined at the top of event_hdl-t.h file):
 | |
| ```
 | |
| 	/* event data struct are defined as followed */
 | |
| 	struct event_hdl_cb_data_template {
 | |
| 		struct {
 | |
| 			/* safe data can be safely used from both
 | |
| 			 * sync and async functions
 | |
| 			 * data consistency is guaranteed
 | |
| 			 */
 | |
| 		} safe;
 | |
| 		struct {
 | |
| 			/* unsafe data may only be used from sync functions:
 | |
| 			 * in async mode, data consistency cannot be guaranteed
 | |
| 			 * and unsafe data may already be stale, thus using
 | |
| 			 * it is highly discouraged because it
 | |
| 			 * could lead to undefined behavior
 | |
| 			 * (UAF, null dereference...)
 | |
| 			 */
 | |
| 		} unsafe;
 | |
| 	};
 | |
| ```
 | |
| 
 | |
| This structure template allows you to easily create a new event
 | |
| data structure that can be provided with your new event family.
 | |
| 
 | |
| You should name it after 'struct event_hdl_cb_data_new_family' so that it is
 | |
| easy to guess the event family it relates to.
 | |
| 
 | |
| Indeed, each event data structure is to be associated with an
 | |
| unique event family type.
 | |
| For each subtypes within a family type, the associated data structure
 | |
| should be provided when publishing the event.
 | |
| 
 | |
| The event data struct declaration should not be performed
 | |
| directly under event_hdl-t.h file:
 | |
| 
 | |
| 	It should be done in the header files of the corresponding
 | |
| 	facility that will publish/provide this event.
 | |
| 
 | |
| 	Example: struct event_hdl_cb_data_server, provided for the
 | |
| 	EVENT_HDL_SUB_SERVER event family, is going to be declared in
 | |
| 	include/haproxy/server-t.h file.
 | |
| 
 | |
| 	However, in event_hdl-t.h, where you declare event family/subtypes,
 | |
| 	you should add comments or links to the file containing the relevant
 | |
| 	data struct declaration. This way we make sure all events related
 | |
| 	information is centralized in event_hdl-t.h while keeping it clean
 | |
| 	and not depending on any additional includes (you are free to
 | |
| 	depend on specific data types within your custom event data structure).
 | |
| 
 | |
| Please make sure that EVENT_HDL_ASYNC_EVENT_DATA (defined in event_hdl-t.h)
 | |
| is greater than sizeof(event_hdl_cb_data_new_family).
 | |
| 
 | |
| It is required for async handlers to properly consume event data.
 | |
| 
 | |
| You are free to adjust EVENT_HDL_ASYNC_EVENT_DATA size if needed.
 | |
| 
 | |
| If EVENT_HDL_ASYNC_EVENT_DATA is not big enough to store your new
 | |
| event family struct, a compilation assert triggered by EVENT_HDL_CB_DATA
 | |
| will occur. In addition to this, an extra runtime BUG_ON will make
 | |
| sure the condition is met when publishing the event.
 | |
| The goal here is to force haproxy to fail explicitly so you know that
 | |
| something must be done on your side.
 | |
| 
 | |
| 3.1 PUBLISHING AN EVENT
 | |
| -----------------------
 | |
| 
 | |
| Publishing an event is really simple.
 | |
| It relies on the event_hdl_publish function.
 | |
| 
 | |
| The function is defined as follow:
 | |
| ```
 | |
| 	int event_hdl_publish(event_hdl_sub_list *sub_list,
 | |
| 	                      event_hdl_sub_type e_type,
 | |
| 	                      const struct event_hdl_cb_data *data);
 | |
| ```
 | |
| 
 | |
| We will ignore sub_list argument for now.
 | |
| In the examples below, we will use sub_list = NULL.
 | |
| Go to section 4 for a full picture about this feature.
 | |
| 
 | |
| <e_type>: the event type that should be published.
 | |
| 	  All subscriptions referring to this event within
 | |
| 	  a subscription list context will be notified about the event.
 | |
| <data>:	  data provided for the event family of <e_type>
 | |
| 	  If <e_type>.family does not provide additional data,
 | |
| 		data should be set to NULL.
 | |
| 	  If <e_type>.family does provide additional data, data should be set
 | |
| 		  using EVENT_HDL_CB_DATA macro.
 | |
| 		  (see the example below)
 | |
| 
 | |
| The function returns 1 in case of SUCCESS (handlers successfully notified)
 | |
| and 0 in case of FAILURE (no handlers notified, because of memory error).
 | |
| 
 | |
| Event publishing can be performed from anywhere in the code.
 | |
| (this example does not compile)
 | |
| ```
 | |
| 	struct event_hdl_cb_data_new_family event_data;
 | |
| 
 | |
| 	/* first we need to prepare event data
 | |
| 	 * that will be provided to event handlers
 | |
| 	 */
 | |
| 
 | |
| 	/* safe data, available from both sync and async contexts */
 | |
| 	event_data.safe.my_custom_data = x;
 | |
| 
 | |
| 	/* unsafe data, only available from sync contexts */
 | |
| 	event_data.unsafe.my_unsafe_data = y;
 | |
| 
 | |
| 	/* once data is prepared, we can publish the event */
 | |
| 	event_hdl_publish(NULL,
 | |
| 			  EVENT_HDL_SUB_NEW_FAMILY_SUBTYPE_1,
 | |
| 			  EVENT_HDL_CB_DATA(&event_data));
 | |
| 
 | |
| 	/* EVENT_HDL_SUB_NEW_FAMILY_SUBTYPE_1 event was
 | |
| 	 * successfully published in global subscription list
 | |
| 	 */
 | |
| ```
 | |
| 
 | |
| --------------------------------------------------------------------------------
 | |
| |You should know that there is currently a limitation about publish function:  |
 | |
| |The function should not be used from critical places                          |
 | |
| |(where the calling frequency is high                                          |
 | |
| |or where timing sensitivity is high).                                         |
 | |
| |                                                                              |
 | |
| |Because in current implementation, subscription list lookups are not          |
 | |
| |optimized for such uses cases.                                                |
 | |
| --------------------------------------------------------------------------------
 | |
| 
 | |
| 4 SUBSCRIPTION LISTS
 | |
| -----------------------
 | |
| 
 | |
| As you may already know, EVENT_HDL API main functions rely on
 | |
| subscription lists.
 | |
| Providing NULL where subscription list argument is required
 | |
| allows to use the implicit global subscription list.
 | |
| 
 | |
| But you can also provide a specific subscription list, example:
 | |
| 	subscription list associated with a single entity so that you only
 | |
| 	subscribe to events of this single entity
 | |
| 
 | |
| A subscription list is of type event_hdl_sub_list.
 | |
| It is defined in event_hdl-t.h
 | |
| 
 | |
| To make use of this feature, you should know about these 2 functions:
 | |
| 
 | |
| event_hdl_sub_list_init(list): use this fcn to initialize
 | |
| 			       a new subscription list.
 | |
| 
 | |
| Example:
 | |
| ```
 | |
| 	event_hdl_sub_list my_custom_list;
 | |
| 
 | |
| 	event_hdl_sub_list_init(&my_custom_list);
 | |
| ```
 | |
| 
 | |
| event_hdl_sub_list_destroy(list): use this fcn to destroy
 | |
| 				  an existing subscription list.
 | |
| 
 | |
| Example:
 | |
| ```
 | |
| 	event_hdl_sub_list_init(&my_custom_list);
 | |
| ```
 | |
| 
 | |
| 	Using this function will cause all the existing subscriptions
 | |
| 	within the provided sub_list to be properly unregistered
 | |
| 	and deleted according to their types.
 | |
| 
 | |
| Now we'll take another quick look at event_hdl_publish() function:
 | |
| 
 | |
| Remember that the function is defined as follow:
 | |
| ```
 | |
| 	int event_hdl_publish(event_hdl_sub_list *sub_list,
 | |
| 	                      event_hdl_sub_type e_type,
 | |
| 	                      const struct event_hdl_cb_data *data);
 | |
| ```
 | |
| 
 | |
| In the previous examples, we used sub_list = NULL.
 | |
| 
 | |
| if sub_list is NULL:
 | |
| 	event will be published in in global list
 | |
| else
 | |
| 	event will be published in user specified sub_list
 | |
| 
 | |
| 5 MISC/HELPER FUNCTIONS
 | |
| -----------------------
 | |
| 
 | |
| Don't forget to take a look at MISC/HELPER FUNCTIONS in
 | |
| include/haproxy/event_hdl.h (end of the file) for a
 | |
| complete list of helper functions / macros.
 | |
| 
 | |
| We've already used some, if not the vast majority
 | |
| in the examples shown in this document.
 | |
| 
 | |
| This includes, to name a few:
 | |
| 	- event types manipulation
 | |
| 	- event types comparison
 | |
| 	- lookup id computing
 | |
| 	- subscriber list management (covered in section 4)
 | |
| 	- sync/async handler helpers
 |