From: Jérémie Galarneau Date: Wed, 10 Jan 2018 19:12:35 +0000 (-0500) Subject: Add lttng_notification_channel_has_pending_notification() X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=commitdiff_plain;h=1d757b1cd3b4669b52e2d9ceafb03eafd42490ff Add lttng_notification_channel_has_pending_notification() This new API allows notification channel users to check for pending notifications without necessarily blocking until a new notification is ready. Moreoever, the pending notification is not consumed by this new API. lttng_notification_channel_get_next_notification() must still be called to consume the new notification. Signed-off-by: Jérémie Galarneau --- diff --git a/include/lttng/notification/channel.h b/include/lttng/notification/channel.h index b010df12a..7708cfd56 100644 --- a/include/lttng/notification/channel.h +++ b/include/lttng/notification/channel.h @@ -18,6 +18,8 @@ #ifndef LTTNG_NOTIFICATION_CHANNEL_H #define LTTNG_NOTIFICATION_CHANNEL_H +#include + #ifdef __cplusplus extern "C" { #endif @@ -89,6 +91,26 @@ lttng_notification_channel_get_next_notification( struct lttng_notification_channel *channel, struct lttng_notification **notification); +/* + * Check whether a notification is pending on a notification channel. + * + * This call allows the user to check whether a notification is pending on + * the notification channel. + * + * If pending is set to true and the return value is + * LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + * lttng_notification_channel_get_next_notification() can be called and + * is guaranteed to not block. + * + * Returns LTTNG_NOTIFICATION_CHANNEL_STATUS_OK on success or + * LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID if an invalid parameter was + * provided. + */ +extern enum lttng_notification_channel_status +lttng_notification_channel_has_pending_notification( + struct lttng_notification_channel *channel, + bool *notification_pending); + /* * Subscribe to notifications of a condition through a notification channel. * diff --git a/src/lib/lttng-ctl/channel.c b/src/lib/lttng-ctl/channel.c index f2daf0d50..16474464d 100644 --- a/src/lib/lttng-ctl/channel.c +++ b/src/lib/lttng-ctl/channel.c @@ -26,13 +26,15 @@ #include #include #include "lttng-ctl-helper.h" +#include +#include static int handshake(struct lttng_notification_channel *channel); /* * Populates the reception buffer with the next complete message. - * The caller must acquire the client's lock. + * The caller must acquire the channel's lock. */ static int receive_message(struct lttng_notification_channel *channel) @@ -40,9 +42,9 @@ int receive_message(struct lttng_notification_channel *channel) ssize_t ret; struct lttng_notification_channel_message msg; - ret = lttng_dynamic_buffer_set_size(&channel->reception_buffer, 0); - if (ret) { - goto error; + if (lttng_dynamic_buffer_set_size(&channel->reception_buffer, 0)) { + ret = -1; + goto end; } ret = lttcomm_recv_unix_sock(channel->socket, &msg, sizeof(msg)); @@ -356,6 +358,101 @@ error: goto end; } +enum lttng_notification_channel_status +lttng_notification_channel_has_pending_notification( + struct lttng_notification_channel *channel, + bool *_notification_pending) +{ + int ret; + enum lttng_notification_channel_status status = + LTTNG_NOTIFICATION_CHANNEL_STATUS_OK; + fd_set read_fds; + struct timeval timeout; + + FD_ZERO(&read_fds); + memset(&timeout, 0, sizeof(timeout)); + + if (!channel || !_notification_pending) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID; + goto end; + } + + pthread_mutex_lock(&channel->lock); + + if (channel->pending_notifications.count) { + *_notification_pending = true; + goto end_unlock; + } + + if (channel->socket < 0) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED; + goto end_unlock; + } + + /* + * Check, without blocking, if data is available on the channel's + * socket. If there is data available, it is safe to read (blocking) + * on the socket for a message from the session daemon. + * + * Since all commands wait for the session daemon's reply before + * releasing the channel's lock, the protocol only allows for + * notifications and "notification dropped" messages to come + * through. If we receive a different message type, it is + * considered a protocol error. + * + * Note that this function is not guaranteed not to block. This + * will block until our peer (the session daemon) has sent a complete + * message if we see data available on the socket. If the peer does + * not respect the protocol, this may block indefinitely. + */ + FD_SET(channel->socket, &read_fds); + do { + ret = select(channel->socket + 1, &read_fds, NULL, NULL, &timeout); + } while (ret < 0 && errno == EINTR); + + if (ret == 0) { + /* No data available. */ + *_notification_pending = false; + goto end_unlock; + } else if (ret < 0) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_ERROR; + goto end_unlock; + } + + /* Data available on socket. */ + ret = receive_message(channel); + if (ret) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_ERROR; + goto end_unlock; + } + + switch (get_current_message_type(channel)) { + case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_NOTIFICATION: + ret = enqueue_notification_from_current_message(channel); + if (ret) { + goto end; + } + *_notification_pending = true; + break; + case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_NOTIFICATION_DROPPED: + ret = enqueue_dropped_notification(channel); + if (ret) { + goto end; + } + *_notification_pending = true; + break; + default: + /* Protocol error. */ + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_ERROR; + goto end_unlock; + } + +end_unlock: + pthread_mutex_unlock(&channel->lock); +end: + return status; +} + static int receive_command_reply(struct lttng_notification_channel *channel, enum lttng_notification_channel_status *status)