[Spice-devel] [PATCH spice-streaming-agent 3/3] Wrap the polling mechanism in a Poll class

Lukáš Hrázký lhrazky at redhat.com
Tue Jul 10 14:52:00 UTC 2018


Replaces the polling function with a generic class that allows to
register multiple file descriptors for polling, along with a callback to
call when an event occurs on the descriptor.

The patch also effectively replaces the while loop which was in
read_command. The loop only ever looped more than once if we didn't have
anything to read and we were in blocking mode, which was only in the
case we got EINTR. In case of EINTR the loop around poll.poll() will
take care of polling again.

Signed-off-by: Lukáš Hrázký <lhrazky at redhat.com>
---
 src/Makefile.am               |  2 ++
 src/poll.cpp                  | 51 +++++++++++++++++++++++++++++++
 src/poll.hpp                  | 56 +++++++++++++++++++++++++++++++++++
 src/spice-streaming-agent.cpp | 51 +++++++------------------------
 4 files changed, 120 insertions(+), 40 deletions(-)
 create mode 100644 src/poll.cpp
 create mode 100644 src/poll.hpp

diff --git a/src/Makefile.am b/src/Makefile.am
index 40544ba..56a8cda 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -62,6 +62,8 @@ spice_streaming_agent_SOURCES = \
 	mjpeg-fallback.hpp \
 	jpeg.cpp \
 	jpeg.hpp \
+	poll.cpp \
+	poll.hpp \
 	stream-port.cpp \
 	stream-port.hpp \
 	$(NULL)
diff --git a/src/poll.cpp b/src/poll.cpp
new file mode 100644
index 0000000..dd9f48a
--- /dev/null
+++ b/src/poll.cpp
@@ -0,0 +1,51 @@
+/* A poll() encapsulation class.
+ *
+ * \copyright
+ * Copyright 2018 Red Hat Inc. All rights reserved.
+ */
+
+#include "poll.hpp"
+
+#include "error.hpp"
+
+
+namespace spice {
+namespace streaming_agent {
+
+void Poll::add(const pollfd& poll_fd, Callback&& callback)
+{
+    poll_fds.push_back(poll_fd);
+    callbacks.push_back(std::move(callback));
+}
+
+bool Poll::poll(bool blocking)
+{
+    if (poll_fds.empty()) {
+        return false;
+    }
+
+    int res = ::poll(poll_fds.data(), poll_fds.size(), blocking ? -1 : 0);
+
+    if (res == 0) {
+        return false;
+    }
+
+    if (res < 0) {
+        if (errno == EINTR) {
+            // do nothing, rely on another loop iteration to retry
+            return false;
+        }
+
+        throw IOError("poll failed", errno);
+    }
+
+    for (size_t i = 0; i < poll_fds.size(); ++i) {
+        if (poll_fds[i].revents) {
+            callbacks[i](poll_fds[i]);
+        }
+    }
+
+    return true;
+}
+
+}} // namespace spice::streaming_agent
diff --git a/src/poll.hpp b/src/poll.hpp
new file mode 100644
index 0000000..a215159
--- /dev/null
+++ b/src/poll.hpp
@@ -0,0 +1,56 @@
+/* A poll() encapsulation class.
+ *
+ * \copyright
+ * Copyright 2018 Red Hat Inc. All rights reserved.
+ */
+
+#ifndef SPICE_STREAMING_AGENT_POLL_HPP
+#define SPICE_STREAMING_AGENT_POLL_HPP
+
+#include <functional>
+#include <vector>
+#include <poll.h>
+
+
+namespace spice {
+namespace streaming_agent {
+
+/**
+ * A class that implements the poll() mechanism on a collection of file
+ * descriptors. The fds are added one by one with the add() method, along with
+ * the callback that should be called when an event occurs on the descriptor.
+ * In case events occur on more than one descriptor, the callbacks are called
+ * in the order in which they were added.
+ */
+class Poll
+{
+public:
+    using Callback = std::function<void(const pollfd&)>;
+
+    /**
+     * Adds a file descriptor to the list of fds to poll on.
+     *
+     * \param poll_fd The pollfd struct for the file descriptor.
+     * \param callback The callback to call on an event on the file descriptor.
+     *                 The signature is void(const pollfd&).
+     */
+    void add(const pollfd& poll_fd, Callback&& callback);
+
+    /**
+     * Calls poll() on the descriptors registered with add().
+     *
+     * \param blocking If true, sets indefinite timeout on the poll. If false,
+     * doesn't block and returns immediately.
+     *
+     * \return true if there was an event on any fd, false otherwise.
+     */
+    bool poll(bool blocking = true);
+
+private:
+    std::vector<pollfd> poll_fds;
+    std::vector<Callback> callbacks;
+};
+
+}} // namespace spice::streaming_agent
+
+#endif // SPICE_STREAMING_AGENT_POLL_HPP
diff --git a/src/spice-streaming-agent.cpp b/src/spice-streaming-agent.cpp
index 39c53bd..0e383de 100644
--- a/src/spice-streaming-agent.cpp
+++ b/src/spice-streaming-agent.cpp
@@ -9,6 +9,7 @@
 #include "cursor-updater.hpp"
 #include "frame-log.hpp"
 #include "stream-port.hpp"
+#include "poll.hpp"
 #include "error.hpp"
 
 #include <spice/stream-device.h>
@@ -27,7 +28,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/time.h>
-#include <poll.h>
 #include <syslog.h>
 #include <signal.h>
 #include <exception>
@@ -92,27 +92,7 @@ static bool streaming_requested = false;
 static bool quit_requested = false;
 static std::set<SpiceVideoCodecType> client_codecs;
 
-static bool have_something_to_read(StreamPort &stream_port, bool blocking)
-{
-    struct pollfd pollfd = {stream_port.fd, POLLIN, 0};
-
-    if (poll(&pollfd, 1, blocking ? -1 : 0) < 0) {
-        if (errno == EINTR) {
-            // report nothing to read, next iteration of the enclosing loop will retry
-            return false;
-        }
-
-        throw IOError("poll failed on the device", errno);
-    }
-
-    if (pollfd.revents & POLLIN) {
-        return true;
-    }
-
-    return false;
-}
-
-static void read_command_from_device(StreamPort &stream_port)
+static void receive_stream_port_message(StreamPort& stream_port)
 {
     InboundMessage in_message = stream_port.receive();
 
@@ -139,22 +119,6 @@ static void read_command_from_device(StreamPort &stream_port)
     throw std::runtime_error("UNKNOWN msg of type " + std::to_string(in_message.header.type));
 }
 
-static void read_command(StreamPort &stream_port, bool blocking)
-{
-    while (!quit_requested) {
-        if (have_something_to_read(stream_port, blocking)) {
-            read_command_from_device(stream_port);
-            break;
-        }
-
-        if (!blocking) {
-            break;
-        }
-
-        sleep(1);
-    }
-}
-
 static void handle_interrupt(int intr)
 {
     syslog(LOG_INFO, "Got signal %d, exiting", intr);
@@ -193,9 +157,16 @@ static void
 do_capture(StreamPort &stream_port, FrameLog &frame_log)
 {
     unsigned int frame_count = 0;
+
+    Poll poll;
+
+    poll.add({stream_port.fd, POLLIN, 0}, [&stream_port](const pollfd &pollfd) {
+        receive_stream_port_message(stream_port);
+    });
+
     while (!quit_requested) {
         while (!quit_requested && !streaming_requested) {
-            read_command(stream_port, true);
+            poll.poll();
         }
 
         if (quit_requested) {
@@ -253,7 +224,7 @@ do_capture(StreamPort &stream_port, FrameLog &frame_log)
             }
             frame_log.log_stat("Sent frame");
 
-            read_command(stream_port, false);
+            poll.poll(false);
         }
     }
 }
-- 
2.17.1



More information about the Spice-devel mailing list