Add lttng_notification_channel_has_pending_notification()
authorJérémie Galarneau <jeremie.galarneau@efficios.com>
Wed, 10 Jan 2018 19:12:35 +0000 (14:12 -0500)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Tue, 3 Apr 2018 16:12:29 +0000 (12:12 -0400)
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 <jeremie.galarneau@efficios.com>
include/lttng/notification/channel.h
src/lib/lttng-ctl/channel.c

index b010df12aa56b8194241fd17af947a0c8e2e1009..7708cfd56e915a293007d481f0f36c1dd8980454 100644 (file)
@@ -18,6 +18,8 @@
 #ifndef LTTNG_NOTIFICATION_CHANNEL_H
 #define LTTNG_NOTIFICATION_CHANNEL_H
 
+#include <stdbool.h>
+
 #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.
  *
index f2daf0d50e360f050e3146431b1fede07833ddf3..16474464d880a9ffaa6c0fbad6c068dbeb781407 100644 (file)
 #include <common/defaults.h>
 #include <assert.h>
 #include "lttng-ctl-helper.h"
+#include <sys/select.h>
+#include <sys/time.h>
 
 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)
This page took 0.036979 seconds and 4 git commands to generate.