[Spice-devel] [RFC 3/3] server/red_worker: reuse dispatcher

Alon Levy alevy at redhat.com
Wed Nov 2 10:01:45 PDT 2011


On Wed, Nov 02, 2011 at 06:00:46PM +0200, Yonit Halperin wrote:
> On 11/01/2011 10:10 AM, Alon Levy wrote:
> >This patch reuses Dispatcher in RedDispatcher. It adds two helpers
> >to red_worker to keep RedWorker opaque to the outside. The dispatcher is
> >abused in three places that use the underlying socket directly:
> >  once sending a READY after red_init completes
> >  once for each channel creation, replying with the RedChannel instance
> >   for cursor and display.
> >
> exploit this opportunity to rename red_dispatcher?
> ->worker_dispatcher? display_dispatcher?
> >Code is being added, not removed.
> >
> >Readability?
> wrote a script to review the patch :)

Should have added it in the CC.

> 
> >---
> >  server/red_dispatcher.c |  585 +++++++++++++++++++++++++++------------
> >  server/red_dispatcher.h |  182 ++++++++++++
> >  server/red_worker.c     |  716 +++++++++++++++++++++++++----------------------
> >  server/red_worker.h     |    2 +
> >  4 files changed, 979 insertions(+), 506 deletions(-)
> >
> >diff --git a/server/red_dispatcher.c b/server/red_dispatcher.c
> >index 7a0033f..4680b50 100644
> >--- a/server/red_dispatcher.c
> >+++ b/server/red_dispatcher.c
> >@@ -37,6 +37,7 @@
> >  #include "reds_gl_canvas.h"
> >  #endif // USE_OPENGL
> >  #include "reds.h"
> >+#include "dispatcher.h"
> >  #include "red_dispatcher.h"
> >  #include "red_parse_qxl.h"
> >
> >@@ -53,7 +54,7 @@ struct AsyncCommand {
> >  struct RedDispatcher {
> >      QXLWorker base;
> >      QXLInstance *qxl;
> >-    int channel;
> >+    Dispatcher dispatcher;
> >      pthread_t worker_thread;
> >      uint32_t pending;
> >      int primary_active;
> >@@ -83,24 +84,196 @@ extern spice_wan_compression_t zlib_glz_state;
> >
> >  static RedDispatcher *dispatchers = NULL;
> >
> >+static void register_callbacks(RedDispatcher *dispatcher)
> >+{
> >+    dispatcher_register_handler(&dispatcher->dispatcher,
> >+                                RED_WORKER_MESSAGE_DISPLAY_CONNECT,
> >+                                handle_dev_display_connect,
> >+                                sizeof(RedWorkerMessageDisplayConnect),
> >+                                0);
> >+    dispatcher_register_handler(&dispatcher->dispatcher,
> >+                                RED_WORKER_MESSAGE_DISPLAY_DISCONNECT,
> >+                                handle_dev_display_disconnect,
> >+                                sizeof(RedWorkerMessageDisplayDisconnect),
> >+                                1);
> >+    dispatcher_register_handler(&dispatcher->dispatcher,
> >+                                RED_WORKER_MESSAGE_DISPLAY_MIGRATE,
> >+                                handle_dev_display_migrate,
> >+                                sizeof(RedWorkerMessageDisplayMigrate),
> >+                                0);
> <snip>
> .
> .
> .
> >+    dispatcher_register_handler(&dispatcher->dispatcher,
> >+                                RED_WORKER_MESSAGE_RESET_MEMSLOTS,
> >+                                handle_dev_reset_memslots,
> >+                                sizeof(RedWorkerMessageResetMemslots),
> >+                                0);
> >+}
> >+
> What do you think about moving the registering the cbs to
> red_worker? Then you will not need to add all the cbs prototype to
> red_dispatcher.h (but you will need to expose Dispatcher to
> red_worker)
> 

I'll do it. I think I'll either expose Dispatcher or add a
"red_dispatcher_register_callback" that hides it.

> >  static void red_dispatcher_set_display_peer(RedChannel *channel, RedClient *client,
> >                                              RedsStream *stream, int migration,
> >                                              int num_common_caps, uint32_t *common_caps, int num_caps,
> >                                              uint32_t *caps)
> >  {
> >+    RedWorkerMessageDisplayConnect payload;
> >      RedDispatcher *dispatcher;
> >
> >      red_printf("");
> >      dispatcher = (RedDispatcher *)channel->data;
> >-    RedWorkerMessage message = RED_WORKER_MESSAGE_DISPLAY_CONNECT;
> >-    write_message(dispatcher->channel,&message);
> >-    send_data(dispatcher->channel,&client, sizeof(RedClient *));
> >-    send_data(dispatcher->channel,&stream, sizeof(RedsStream *));
> >-    send_data(dispatcher->channel,&migration, sizeof(int));
> >+    payload.client = client;
> >+    payload.stream = stream;
> >+    payload.migration = migration;
> >+    dispatcher_send_message(&dispatcher->dispatcher,
> >+                            RED_WORKER_MESSAGE_DISPLAY_CONNECT,
> >+&payload);
> >  }
> >
> <snip>
> 
> >diff --git a/server/red_worker.c b/server/red_worker.c
> >index 00efc05..6e5a238 100644
> >--- a/server/red_worker.c
> >+++ b/server/red_worker.c
> >@@ -866,8 +866,8 @@ typedef struct RedWorker {
> >      CursorChannel *cursor_channel;
> >      QXLInstance *qxl;
> >      RedDispatcher *dispatcher;
> >-    int id;
> >      int channel;
> >+    int id;
> >      int running;
> >      uint32_t *pending;
> >      int epoll;
> >@@ -10102,21 +10102,19 @@ static void surface_dirty_region_to_rects(RedSurface *surface,
> >      free(dirty_rects);
> >  }
> >
> >-static inline void handle_dev_update_async(RedWorker *worker)
> >+void handle_dev_update_async(void *opaque, void *payload)
> >  {
> >-    QXLRect qxl_rect;
> >+    RedWorker *worker = opaque;
> >+    RedWorkerMessageUpdateAsync *msg = payload;
> >      SpiceRect rect;
> >-    uint32_t surface_id;
> >-    uint32_t clear_dirty_region;
> >      QXLRect *qxl_dirty_rects;
> >      uint32_t num_dirty_rects;
> >      RedSurface *surface;
> >+    uint32_t surface_id = msg->surface_id;
> >+    QXLRect qxl_area = msg->qxl_area;
> >+    uint32_t clear_dirty_region = msg->clear_dirty_region;
> >
> >-    receive_data(worker->channel,&surface_id, sizeof(uint32_t));
> >-    receive_data(worker->channel,&qxl_rect, sizeof(QXLRect));
> >-    receive_data(worker->channel,&clear_dirty_region, sizeof(uint32_t));
> >-
> >-    red_get_rect_ptr(&rect,&qxl_rect);
> >+    red_get_rect_ptr(&rect,&qxl_area);
> >      flush_display_commands(worker);
> >
> >      ASSERT(worker->running);
> >@@ -10124,12 +10122,12 @@ static inline void handle_dev_update_async(RedWorker *worker)
> >      validate_surface(worker, surface_id);
> >      red_update_area(worker,&rect, surface_id);
> >      if (!worker->qxl->st->qif->update_area_complete) {
> >-        return;
> >+        goto complete;
> >      }
> >      surface =&worker->surfaces[surface_id];
> >      num_dirty_rects = pixman_region32_n_rects(&surface->draw_dirty_region);
> >      if (num_dirty_rects == 0) {
> >-        return;
> >+        goto complete;
> >      }
> >      qxl_dirty_rects = spice_new0(QXLRect, num_dirty_rects);
> >      surface_dirty_region_to_rects(surface, qxl_dirty_rects, num_dirty_rects,
> >@@ -10137,26 +10135,25 @@ static inline void handle_dev_update_async(RedWorker *worker)
> >      worker->qxl->st->qif->update_area_complete(worker->qxl, surface_id,
> >                                            qxl_dirty_rects, num_dirty_rects);
> >      free(qxl_dirty_rects);
> >+
> >+complete:
> >+    red_dispatcher_async_complete(worker->dispatcher, msg->cmd);
> 
> You can register only one callback to all the async messages, and then
> handle each one differently, but call red_dispatcher_async_complete
> for all of them.

I thought about it, but the whole point of the table is to avoid a
switch, seems kind of counterproductive. Another option would have been
to register a second callback, or to add a second table. Too many ways
to solve a little problem. I think the second callback registration is
the least magical / complex. So Dispatcher will just:

 if handler:
    call handler
 if ack:
    send ack
 else:
    if async_complete:
        call async_complete

And I'll add dispatcher_register_async_complete

> 
> 
> Thanks,
> Yonit.


More information about the Spice-devel mailing list