[Spice-devel] [PATCH 13/22] Convert message writing from C style to C++ style

Lukáš Hrázký lhrazky at redhat.com
Thu Mar 1 13:45:20 UTC 2018


On Wed, 2018-02-28 at 16:43 +0100, Christophe de Dinechin wrote:
> From: Christophe de Dinechin <dinechin at redhat.com>
> 
> - The Stream class now deals with locking and sending messages
> - The Message<> template class deals with the general writing mechanisms
> - Classes, FormatMessage, FrameMessage, CapabilitiesMessage and
>   X11CursorMessage represent individual messages
> 
> The various classes should be moved to separate headers in a
> subsequent operation
> 
> The design uses the "curiously recurring template pattern" (CRTP) to
> defer some of the code to a derived class. This is done to avoid
> runtime overhead: all the calls are static.
> 
> Signed-off-by: Christophe de Dinechin <dinechin at redhat.com>
> ---
>  src/spice-streaming-agent.cpp | 279 +++++++++++++++++++-----------------------
>  1 file changed, 128 insertions(+), 151 deletions(-)
> 
> diff --git a/src/spice-streaming-agent.cpp b/src/spice-streaming-agent.cpp
> index 23ee824..8bbd457 100644
> --- a/src/spice-streaming-agent.cpp
> +++ b/src/spice-streaming-agent.cpp
> @@ -48,24 +48,6 @@ namespace spice
>  namespace streaming_agent
>  {
>  
> -struct FormatMessage
> -{
> -    StreamDevHeader hdr;
> -    StreamMsgFormat msg;
> -};
> -
> -struct DataMessage
> -{
> -    StreamDevHeader hdr;
> -    StreamMsgData msg;
> -};
> -
> -struct CursorMessage
> -{
> -    StreamDevHeader hdr;
> -    StreamMsgCursorSet msg;
> -};
> -
>  class Stream
>  {
>      typedef std::set<SpiceVideoCodecType> codecs_t;
> @@ -87,13 +69,17 @@ public:
>      const codecs_t &client_codecs() { return codecs; }
>      bool streaming_requested() { return is_streaming; }
>  
> +    template <typename Message, typename ...PayloadArgs>
> +    void send(PayloadArgs... payload)

I'd use perfect forwarding here.

> +    {
> +        Message message(payload...);
> +        std::lock_guard<std::mutex> stream_guard(mutex);
> +        message.write_header(*this);
> +        message.write_message_body(*this, payload...);
> +    }
> +
>      int read_command(bool blocking);
> -    size_t write_all(const void *buf, const size_t len);
> -    int send_format(unsigned w, unsigned h, uint8_t c);
> -    int send_frame(const void *buf, const unsigned size);
> -    void send_cursor(uint16_t width, uint16_t height,
> -                     uint16_t hotspot_x, uint16_t hotspot_y,
> -                     std::function<void(uint32_t *)> fill_cursor);
> +    void write_all(const char *operation, const void *buf, const size_t len);
>  
>  private:
>      int have_something_to_read(int timeout);
> @@ -109,6 +95,117 @@ private:
>      bool is_streaming = false;
>  };
>  
> +template <typename Payload, typename Info, unsigned Type>
> +class Message
> +{
> +public:
> +    template <typename ...PayloadArgs>
> +    Message(PayloadArgs... payload)

Also perfect forwarding. And I'd still rather see "PayloadArgs...
payload_args" here :) (and to be consistent, in Stream::send(...)
above, too)

> +        : hdr(StreamDevHeader {
> +              .protocol_version = STREAM_DEVICE_PROTOCOL,
> +              .padding = 0,     // Workaround GCC bug "sorry: not implemented"
> +              .type = Type,
> +              .size = (uint32_t) Info::size(payload...)
> +          })
> +    { }
> +    void write_header(Stream &stream)
> +    {
> +        stream.write_all("header", &hdr, sizeof(hdr));
> +    }
> +
> +protected:
> +    StreamDevHeader hdr;
> +    typedef Payload payload_t;
> +};
> +
> +class FormatMessage : public Message<StreamMsgFormat, FormatMessage, STREAM_TYPE_FORMAT>
> +{
> +public:
> +    FormatMessage(unsigned w, unsigned h, uint8_t c) : Message(w, h, c) {}
> +    static size_t size(unsigned width, unsigned height, uint8_t codec)
> +    {
> +        return sizeof(payload_t);
> +    }
> +    void write_message_body(Stream &stream, unsigned w, unsigned h, uint8_t c)
> +    {
> +        StreamMsgFormat msg = { .width = w, .height = h, .codec = c, .padding1 = {} };
> +        stream.write_all("format", &msg, sizeof(msg));
> +    }
> +};
> +
> +class FrameMessage : public Message<StreamMsgData, FrameMessage, STREAM_TYPE_DATA>
> +{
> +public:
> +    FrameMessage(const void *frame, size_t length) : Message(frame, length) {}
> +    static size_t size(const void *frame, size_t length)
> +    {
> +        return sizeof(payload_t) + length;
> +    }
> +    void write_message_body(Stream &stream, const void *frame, size_t length)
> +    {
> +        stream.write_all("frame", frame, length);
> +    }
> +};
> +
> +class CapabilitiesMessage : public Message<StreamMsgData, CapabilitiesMessage, STREAM_TYPE_CAPABILITIES>
> +{
> +public:
> +    CapabilitiesMessage() : Message() {}
> +    static size_t size()
> +    {
> +        return sizeof(payload_t);
> +    }
> +    void write_message_body(Stream &stream)
> +    {
> +        /* No body for capabilities message */
> +    }
> +};
> +
> +class X11CursorMessage : public Message<StreamMsgCursorSet, X11CursorMessage, STREAM_TYPE_CURSOR_SET>
> +{
> +public:
> +    X11CursorMessage(XFixesCursorImage *cursor): Message(cursor) {}
> +    static size_t size(XFixesCursorImage *cursor)
> +    {
> +        return sizeof(payload_t) + sizeof(uint32_t) * pixel_count(cursor);
> +    }
> +
> +    void write_message_body(Stream &stream, XFixesCursorImage *cursor)
> +    {
> +        StreamMsgCursorSet msg = {
> +            .width = cursor->width,
> +            .height = cursor->height,
> +            .hot_spot_x = cursor->xhot,
> +            .hot_spot_y = cursor->yhot,
> +            .type = SPICE_CURSOR_TYPE_ALPHA,
> +            .padding1 = { },
> +            .data = { }
> +        };
> +
> +        size_t pixcount = pixel_count(cursor);
> +        size_t pixsize = pixcount * sizeof(uint32_t);
> +        std::unique_ptr<uint32_t[]> pixels(new uint32_t[pixcount]);
> +        uint32_t *pixbuf = pixels.get();
> +        fill_pixels(cursor, pixcount, pixbuf);
> +
> +        stream.write_all("cursor message", &msg, sizeof(msg));
> +        stream.write_all("cursor pixels", pixbuf, pixsize);
> +    }
> +
> +private:
> +    static size_t pixel_count(XFixesCursorImage *cursor)
> +    {
> +        return cursor->width * cursor->height;
> +    }
> +
> +    void fill_pixels(XFixesCursorImage *cursor, unsigned count, uint32_t *pixbuf)
> +    {
> +        for (unsigned i = 0; i < count; ++i) {
> +            pixbuf[i] = cursor->pixels[i];
> +        }
> +    }

Both methods above const. fill_pixels() used only once and very short,
perhaps drop the method and use the code directly?

> +};

I like how the message classes came out nice and terse :)

> +
>  }} // namespace spice::streaming_agent
>  
>  static bool quit_requested = false;
> @@ -166,15 +263,7 @@ void Stream::handle_stream_capabilities(uint32_t len)
>      }
>  
>      // we currently do not support extensions so just reply so
> -    StreamDevHeader hdr = {
> -        STREAM_DEVICE_PROTOCOL,
> -        0,
> -        STREAM_TYPE_CAPABILITIES,
> -        0
> -    };
> -    if (write_all(&hdr, sizeof(hdr)) != sizeof(hdr)) {
> -        throw std::runtime_error("error writing capabilities");
> -    }
> +    send<CapabilitiesMessage>();
>  }
>  
>  void Stream::handle_stream_error(uint32_t len)
> @@ -228,7 +317,7 @@ int Stream::read_command(bool blocking)
>      return 1;
>  }
>  
> -size_t Stream::write_all(const void *buf, const size_t len)
> +void Stream::write_all(const char *operation, const void *buf, const size_t len)
>  {
>      size_t written = 0;
>      while (written < len) {
> @@ -237,73 +326,11 @@ size_t Stream::write_all(const void *buf, const size_t len)
>              if (errno == EINTR) {
>                  continue;
>              }
> -            syslog(LOG_ERR, "write failed - %m");
> -            return l;
> +            throw WriteError("write failed", operation, errno).syslog();
>          }
>          written += l;
>      }
> -    syslog(LOG_DEBUG, "write_all -- %u bytes written\n", (unsigned)written);
> -    return written;
> -}
> -
> -int Stream::send_format(unsigned w, unsigned h, uint8_t c)
> -{
> -    const size_t msgsize = sizeof(FormatMessage);
> -    const size_t hdrsize  = sizeof(StreamDevHeader);
> -    FormatMessage msg = {
> -        .hdr = {
> -            .protocol_version = STREAM_DEVICE_PROTOCOL,
> -            .padding = 0,       // Workaround GCC "not implemented" bug
> -            .type = STREAM_TYPE_FORMAT,
> -            .size = msgsize - hdrsize
> -        },
> -        .msg = {
> -            .width = w,
> -            .height = h,
> -            .codec = c,
> -            .padding1 = { }
> -        }
> -    };
> -    syslog(LOG_DEBUG, "writing format\n");
> -    std::lock_guard<std::mutex> stream_guard(mutex);
> -    if (write_all(&msg, msgsize) != msgsize) {
> -        return EXIT_FAILURE;
> -    }
> -    return EXIT_SUCCESS;
> -}
> -
> -int Stream::send_frame(const void *buf, const unsigned size)
> -{
> -    ssize_t n;
> -    const size_t msgsize = sizeof(FormatMessage);
> -    DataMessage msg = {
> -        .hdr = {
> -            .protocol_version = STREAM_DEVICE_PROTOCOL,
> -            .padding = 0,       // Workaround GCC "not implemented" bug
> -            .type = STREAM_TYPE_DATA,
> -            .size = size  /* includes only the body? */
> -        },
> -        .msg = {}
> -    };
> -
> -    std::lock_guard<std::mutex> stream_guard(mutex);
> -    n = write_all(&msg, msgsize);
> -    syslog(LOG_DEBUG,
> -           "wrote %ld bytes of header of data msg with frame of size %u bytes\n",
> -           n, msg.hdr.size);
> -    if (n != msgsize) {
> -        syslog(LOG_WARNING, "write_all header: wrote %ld expected %lu\n",
> -               n, msgsize);
> -        return EXIT_FAILURE;
> -    }
> -    n = write_all(buf, size);
> -    syslog(LOG_DEBUG, "wrote data msg body of size %ld\n", n);
> -    if (n != size) {
> -        syslog(LOG_WARNING, "write_all header: wrote %ld expected %u\n",
> -               n, size);
> -        return EXIT_FAILURE;
> -    }
> -    return EXIT_SUCCESS;
> +    syslog(LOG_DEBUG, "write_all -- %zu bytes written\n", written);
>  }
>  
>  /* returns current time in micro-seconds */
> @@ -350,46 +377,6 @@ static void usage(const char *progname)
>      exit(1);
>  }
>  
> -void
> -Stream::send_cursor(uint16_t width, uint16_t height,
> -                    uint16_t hotspot_x, uint16_t hotspot_y,
> -                    std::function<void(uint32_t *)> fill_cursor)
> -{
> -    if (width >= STREAM_MSG_CURSOR_SET_MAX_WIDTH || height >= STREAM_MSG_CURSOR_SET_MAX_HEIGHT) {
> -        return;
> -    }
> -
> -    const uint32_t msgsize = sizeof(CursorMessage) + width * height * sizeof(uint32_t);
> -    const uint32_t hdrsize  = sizeof(StreamDevHeader);
> -
> -    std::unique_ptr<uint8_t[]> storage(new uint8_t[msgsize]);
> -
> -    CursorMessage *cursor_msg =
> -        new(storage.get()) CursorMessage {
> -        .hdr = {
> -            .protocol_version = STREAM_DEVICE_PROTOCOL,
> -            .padding = 0,       // Workaround GCC internal / not implemented compiler error
> -            .type = STREAM_TYPE_CURSOR_SET,
> -            .size = msgsize - hdrsize
> -        },
> -        .msg = {
> -            .width = width,
> -            .height = height,
> -            .hot_spot_x = hotspot_x,
> -            .hot_spot_y = hotspot_y,
> -            .type = SPICE_CURSOR_TYPE_ALPHA,
> -            .padding1 = { },
> -            .data = { }
> -        }
> -    };
> -
> -    uint32_t *pixels = reinterpret_cast<uint32_t *>(cursor_msg->msg.data);
> -    fill_cursor(pixels);
> -
> -    std::lock_guard<std::mutex> stream_guard(mutex);
> -    write_all(storage.get(), msgsize);
> -}
> -
>  static void cursor_changes(Stream *stream, Display *display, int event_base)
>  {
>      unsigned long last_serial = 0;
> @@ -411,12 +398,7 @@ static void cursor_changes(Stream *stream, Display *display, int event_base)
>          }
>  
>          last_serial = cursor->cursor_serial;
> -        auto fill_cursor = [cursor](uint32_t *pixels) {
> -            for (unsigned i = 0; i < cursor->width * cursor->height; ++i)
> -                pixels[i] = cursor->pixels[i];
> -        };
> -        stream->send_cursor(cursor->width, cursor->height,
> -                            cursor->xhot, cursor->yhot, fill_cursor);
> +        stream->send<X11CursorMessage>(cursor);
>      }
>  }
>  
> @@ -471,9 +453,7 @@ do_capture(Stream &stream, const char *streamport, FILE *f_log)
>  
>                  syslog(LOG_DEBUG, "wXh %uX%u  codec=%u\n", width, height, codec);
>  
> -                if (stream.send_format(width, height, codec) == EXIT_FAILURE) {
> -                    throw std::runtime_error("FAILED to send format message");
> -                }
> +                stream.send<FormatMessage>(width, height, codec);
>              }
>              if (f_log) {
>                  if (log_binary) {
> @@ -484,10 +464,7 @@ do_capture(Stream &stream, const char *streamport, FILE *f_log)
>                      hexdump(frame.buffer, frame.buffer_size, f_log);
>                  }
>              }
> -            if (stream.send_frame(frame.buffer, frame.buffer_size) == EXIT_FAILURE) {
> -                syslog(LOG_ERR, "FAILED to send a frame\n");
> -                break;
> -            }
> +            stream.send<FrameMessage>(frame.buffer, frame.buffer_size);
>              //usleep(1);
>              if (stream.read_command(false) < 0) {
>                  syslog(LOG_ERR, "FAILED to read command\n");


More information about the Spice-devel mailing list