Previously the .free function of a callback was not called if the .callback field was NULL, because the callback as a whole would be considered to be "null".
This change allows you to register callbacks where the .callback field is NULL, but the .free field is != NULL, meaning that the callback is freed after the last time it would have been used. This is mainly convenient for language bindings where we sometimes want to register a free function to clean up a persistent buffer, but we don't need the associated completion callback to be actually called. --- docs/libnbd.pod | 15 +++++++++++++++ lib/internal.h | 18 +++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/docs/libnbd.pod b/docs/libnbd.pod index f6fd4cd..d230cb4 100644 --- a/docs/libnbd.pod +++ b/docs/libnbd.pod @@ -643,6 +643,21 @@ S<C<chunk.callback = my_fn>> function is called. The free function is only accessible in the C API as it is not needed in garbage collected programming languages. +=head2 Callbacks with C<.callback=NULL> and C<.free!=NULL> + +It is possible to register a callback like this: + + ... + (nbd_completion_callback) { .callback = NULL, + .user_data = my_data, + .free = free }, + ... + +The meaning of this is that the callback is never called, but the free +function is still called after the last time the callback would have +been called. This is useful for applying generic freeing actions when +asynchronous commands are retired. + =head2 Callbacks and locking The callbacks are invoked at a point where the libnbd lock is held; as diff --git a/lib/internal.h b/lib/internal.h index 1344d98..ee59582 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -274,20 +274,20 @@ struct command { }; /* Test if a callback is "null" or not, and set it to null. */ -#define CALLBACK_IS_NULL(cb) ((cb).callback == NULL) +#define CALLBACK_IS_NULL(cb) ((cb).callback == NULL && (cb).free == NULL) #define CALLBACK_IS_NOT_NULL(cb) (! CALLBACK_IS_NULL ((cb))) -#define SET_CALLBACK_TO_NULL(cb) ((cb).callback = NULL) +#define SET_CALLBACK_TO_NULL(cb) ((cb).callback = NULL, (cb).free = NULL) /* Call a callback. */ -#define CALL_CALLBACK(cb, ...) \ - (cb).callback ((cb).user_data, ##__VA_ARGS__) +#define CALL_CALLBACK(cb, ...) \ + ((cb).callback != NULL ? (cb).callback ((cb).user_data, ##__VA_ARGS__) : 0) /* Free a callback. */ -#define FREE_CALLBACK(cb) \ - do { \ - if (CALLBACK_IS_NOT_NULL (cb) && (cb).free != NULL) \ - (cb).free ((cb).user_data); \ - SET_CALLBACK_TO_NULL (cb); \ +#define FREE_CALLBACK(cb) \ + do { \ + if ((cb).free != NULL) \ + (cb).free ((cb).user_data); \ + SET_CALLBACK_TO_NULL (cb); \ } while (0) /* aio.c */ -- 2.22.0 _______________________________________________ Libguestfs mailing list Libguestfs@redhat.com https://www.redhat.com/mailman/listinfo/libguestfs