* refcount is used to keep track of references so that
* data can be freed when not used anymore
*/
+typedef void (*event_hdl_data_free)(const void *data);
struct event_hdl_async_event_data
{
/* internal storage */
char data[EVENT_HDL_ASYNC_EVENT_DATA];
+ /* user-provided free function if event data relies on
+ * dynamic members that require specific cleanup
+ */
+ event_hdl_data_free mfree;
uint32_t refcount;
};
void *_ptr;
/* internal use: holds actual data size*/
size_t _size;
+ /* user specified freeing function for event_hdl_cb_data_type
+ * struct members
+ */
+ event_hdl_data_free _mfree;
};
/* struct provided to event_hdl_cb_* handlers
char __static_assert[(size <= EVENT_HDL_ASYNC_EVENT_DATA) ? 1 : -1];\
(size); \
})
-#define _EVENT_HDL_CB_DATA(data,size) \
+#define _EVENT_HDL_CB_DATA(data,size,mfree) \
(&(struct event_hdl_cb_data){ ._ptr = data, \
- ._size = size })
-#define EVENT_HDL_CB_DATA(data) _EVENT_HDL_CB_DATA(data, _EVENT_HDL_CB_DATA_ASSERT(sizeof(*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
{
if (HA_ATOMIC_SUB_FETCH(&data->refcount, 1) == 0) {
/* we were the last one holding a reference to event data - free required */
+ if (data->mfree) {
+ /* Some event data members are dynamically allocated and thus
+ * require specific cleanup using user-provided function.
+ * We directly pass a pointer to internal data storage but
+ * we only expect the cleanup function to typecast it in the
+ * relevant data type to give enough context to the function to
+ * perform the cleanup on data members, and not actually freeing
+ * data pointer since it is our internal buffer :)
+ */
+ data->mfree(&data->data);
+ }
pool_free(pool_head_sub_event_data, data);
}
}
new_event->sub_mgmt = EVENT_HDL_SUB_MGMT_ASYNC(cur_sub);
if (data) {
/* if this fails, please adjust EVENT_HDL_ASYNC_EVENT_DATA in
- * event_hdl-t.h file
+ * event_hdl-t.h file or consider providing dynamic struct members
+ * to reduce overall struct size
*/
BUG_ON(data->_size > sizeof(async_data->data));
if (!async_data) {
/* async data assignment */
memcpy(async_data->data, data->_ptr, data->_size);
+ async_data->mfree = data->_mfree;
/* Initialize refcount, we start at 1 to prevent async
* data from being freed by an async handler while we
* still use it. We will drop the reference when the
if (async_data) {
/* we finished publishing, drop the reference on async data */
_event_hdl_async_data_drop(async_data);
+ } else {
+ /* no async subscribers, we are responsible for calling the data
+ * member freeing function if it was provided
+ */
+ if (data && data->_mfree)
+ data->_mfree(data->_ptr);
}
if (error) {
event_hdl_report_hdl_state(ha_warning, &cur_sub->hdl, "PUBLISH", "memory error");