[Spice-devel,01/10] char-device: Make SpiceCharDeviceState a GObject

Submitted by Jonathon Jongsma on April 1, 2016, 8:51 p.m.

Details

Message ID 1459543904-5346-2-git-send-email-jjongsma@redhat.com
State New
Headers show
Series "Rename VDIPortState to RedCharDeviceVDIPort" ( rev: 3 2 ) in Spice

Not browsing as part of any series.

Commit Message

Jonathon Jongsma April 1, 2016, 8:51 p.m.
From: Christophe Fergeau <cfergeau@redhat.com>

Reviewed-by: Jonathon Jongsma <jjongsma@redhat.com>
---
 server/char-device.c | 353 ++++++++++++++++++++++++++++++++++++++-------------
 server/char-device.h |  43 ++++++-
 2 files changed, 304 insertions(+), 92 deletions(-)

Patch hide | download patch | download mbox

diff --git a/server/char-device.c b/server/char-device.c
index 4b15217..f492657 100644
--- a/server/char-device.c
+++ b/server/char-device.c
@@ -1,6 +1,6 @@ 
 /* spice-server char device flow control code
 
-   Copyright (C) 2012 Red Hat, Inc.
+   Copyright (C) 2012-2015 Red Hat, Inc.
 
    Red Hat Authors:
    Yonit Halperin <yhalperi@redhat.com>
@@ -74,11 +74,6 @@  struct RedCharDevicePrivate {
     SpiceServer *reds;
 };
 
-/* typedef'ed as RedCharDevice */
-struct SpiceCharDeviceState {
-    struct RedCharDevicePrivate priv[1];
-};
-
 enum {
     WRITE_BUFFER_ORIGIN_NONE,
     WRITE_BUFFER_ORIGIN_CLIENT,
@@ -86,12 +81,21 @@  enum {
     WRITE_BUFFER_ORIGIN_SERVER_NO_TOKEN,
 };
 
-/* Holding references for avoiding access violation if the char device was
- * destroyed during a callback */
-static void red_char_device_ref(RedCharDevice *char_dev);
-static void red_char_device_unref(RedCharDevice *char_dev);
-static void red_char_device_write_buffer_unref(RedCharDeviceWriteBuffer *write_buf);
 
+G_DEFINE_TYPE(RedCharDevice, red_char_device, G_TYPE_OBJECT)
+
+#define RED_CHAR_DEVICE_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RED_TYPE_CHAR_DEVICE, RedCharDevicePrivate))
+
+enum {
+    PROP_0,
+    PROP_CHAR_DEV_INSTANCE,
+    PROP_SPICE_SERVER,
+    PROP_CLIENT_TOKENS_INTERVAL,
+    PROP_SELF_TOKENS,
+    PROP_OPAQUE
+};
+
+static void red_char_device_write_buffer_unref(RedCharDeviceWriteBuffer *write_buf);
 static void red_char_device_write_retry(void *opaque);
 
 typedef struct RedCharDeviceMsgToClientItem {
@@ -99,6 +103,13 @@  typedef struct RedCharDeviceMsgToClientItem {
     RedCharDeviceMsgToClient *msg;
 } RedCharDeviceMsgToClientItem;
 
+static RedCharDevice *red_char_device_new(SpiceCharDeviceInstance *sin,
+                                          RedsState *reds,
+                                          uint32_t client_tokens_interval,
+                                          uint32_t self_tokens,
+                                          RedCharDeviceCallbacks *cbs,
+                                          void *opaque);
+
 static RedCharDeviceMsgToClient *
 red_char_device_read_one_msg_from_device(RedCharDevice *dev)
 {
@@ -365,7 +376,7 @@  static int red_char_device_read_from_device(RedCharDevice *dev)
     }
 
     max_send_tokens = red_char_device_max_send_tokens(dev);
-    red_char_device_ref(dev);
+    g_object_ref(dev);
     /*
      * Reading from the device only in case at least one of the clients have a free token.
      * All messages will be discarded if no client is attached to the device
@@ -391,7 +402,7 @@  static int red_char_device_read_from_device(RedCharDevice *dev)
     if (dev->priv->running) {
         dev->priv->active = dev->priv->active || did_read;
     }
-    red_char_device_unref(dev);
+    g_object_unref(dev);
     return did_read;
 }
 
@@ -508,7 +519,7 @@  static int red_char_device_write_to_device(RedCharDevice *dev)
         return 0;
     }
 
-    red_char_device_ref(dev);
+    g_object_ref(dev);
 
     if (dev->priv->write_to_dev_timer) {
         reds_core_timer_cancel(dev->priv->reds, dev->priv->write_to_dev_timer);
@@ -562,7 +573,7 @@  static int red_char_device_write_to_device(RedCharDevice *dev)
         dev->priv->active = dev->priv->active || total;
     }
     dev->priv->during_write_to_device = 0;
-    red_char_device_unref(dev);
+    g_object_unref(dev);
     return total;
 }
 
@@ -727,39 +738,8 @@  RedCharDevice *red_char_device_create(SpiceCharDeviceInstance *sin,
                                       RedCharDeviceCallbacks *cbs,
                                       void *opaque)
 {
-    RedCharDevice *char_dev;
-    SpiceCharDeviceInterface *sif;
-
-    spice_assert(sin);
-    spice_assert(cbs->read_one_msg_from_device && cbs->ref_msg_to_client &&
-                 cbs->unref_msg_to_client && cbs->send_msg_to_client &&
-                 cbs->send_tokens_to_client && cbs->remove_client);
-
-    char_dev = spice_new0(RedCharDevice, 1);
-    char_dev->priv->sin = sin;
-    char_dev->priv->reds = reds;
-    char_dev->priv->cbs = *cbs;
-    char_dev->priv->opaque = opaque;
-    char_dev->priv->client_tokens_interval = client_tokens_interval;
-    char_dev->priv->num_self_tokens = self_tokens;
-
-    ring_init(&char_dev->priv->write_queue);
-    ring_init(&char_dev->priv->write_bufs_pool);
-    ring_init(&char_dev->priv->clients);
-
-    sif = spice_char_device_get_interface(char_dev->priv->sin);
-    if (sif->base.minor_version <= 2 ||
-        !(sif->flags & SPICE_CHAR_DEVICE_NOTIFY_WRITABLE)) {
-        char_dev->priv->write_to_dev_timer = reds_core_timer_add(reds, red_char_device_write_retry, char_dev);
-        if (!char_dev->priv->write_to_dev_timer) {
-            spice_error("failed creating char dev write timer");
-        }
-    }
-
-    char_dev->priv->refs = 1;
-    sin->st = char_dev;
-    spice_debug("sin %p dev_state %p", sin, char_dev);
-    return char_dev;
+    return red_char_device_new(sin, reds, client_tokens_interval,
+                               self_tokens, cbs, opaque);
 }
 
 void red_char_device_reset_dev_instance(RedCharDevice *state,
@@ -775,48 +755,10 @@  void *red_char_device_opaque_get(RedCharDevice *dev)
     return dev->priv->opaque;
 }
 
-static void red_char_device_ref(RedCharDevice *char_dev)
-{
-    char_dev->priv->refs++;
-}
-
-static void red_char_device_unref(RedCharDevice *char_dev)
-{
-    /* The refs field protects the char_dev from being deallocated in
-     * case red_char_device_destroy has been called
-     * during a callabck, and we might still access the char_dev afterwards.
-     * red_char_device_unref is always coupled with a preceding
-     * red_char_device_ref. Here, refs can turn 0
-     * only when red_char_device_destroy is called in between
-     * the calls to red_char_device_ref and red_char_device_unref.*/
-    if (!--char_dev->priv->refs) {
-        free(char_dev);
-    }
-}
-
 void red_char_device_destroy(RedCharDevice *char_dev)
 {
-    reds_on_char_device_state_destroy(char_dev->priv->reds, char_dev);
-    if (char_dev->priv->write_to_dev_timer) {
-        reds_core_timer_remove(char_dev->priv->reds, char_dev->priv->write_to_dev_timer);
-        char_dev->priv->write_to_dev_timer = NULL;
-    }
-    write_buffers_queue_free(&char_dev->priv->write_queue);
-    write_buffers_queue_free(&char_dev->priv->write_bufs_pool);
-    char_dev->priv->cur_pool_size = 0;
-    red_char_device_write_buffer_free(char_dev->priv->cur_write_buf);
-    char_dev->priv->cur_write_buf = NULL;
-
-    while (!ring_is_empty(&char_dev->priv->clients)) {
-        RingItem *item = ring_get_tail(&char_dev->priv->clients);
-        RedCharDeviceClient *dev_client;
-
-        dev_client = SPICE_CONTAINEROF(item, RedCharDeviceClient, link);
-        red_char_device_client_free(char_dev, dev_client);
-    }
-    char_dev->priv->running = FALSE;
-
-    red_char_device_unref(char_dev);
+    g_return_if_fail(RED_IS_CHAR_DEVICE(char_dev));
+    g_object_unref(char_dev);
 }
 
 static RedCharDeviceClient *red_char_device_client_new(RedClient *client,
@@ -920,10 +862,10 @@  void red_char_device_start(RedCharDevice *dev)
 {
     spice_debug("dev_state %p", dev);
     dev->priv->running = TRUE;
-    red_char_device_ref(dev);
+    g_object_ref(dev);
     while (red_char_device_write_to_device(dev) ||
            red_char_device_read_from_device(dev));
-    red_char_device_unref(dev);
+    g_object_unref(dev);
 }
 
 void red_char_device_stop(RedCharDevice *dev)
@@ -1117,3 +1059,234 @@  SpiceCharDeviceInterface *spice_char_device_get_interface(SpiceCharDeviceInstanc
 {
    return SPICE_CONTAINEROF(instance->base.sif, SpiceCharDeviceInterface, base);
 }
+
+
+static void red_char_device_init_device_instance(RedCharDevice *self)
+{
+    SpiceCharDeviceInterface *sif;
+
+    g_return_if_fail(self->priv->reds);
+
+    if (self->priv->write_to_dev_timer) {
+        reds_core_timer_remove(self->priv->reds, self->priv->write_to_dev_timer);
+        self->priv->write_to_dev_timer = NULL;
+    }
+    if (self->priv->sin == NULL) {
+       return;
+    }
+
+    sif = spice_char_device_get_interface(self->priv->sin);
+    if (sif->base.minor_version <= 2 ||
+        !(sif->flags & SPICE_CHAR_DEVICE_NOTIFY_WRITABLE)) {
+        self->priv->write_to_dev_timer = reds_core_timer_add(self->priv->reds,
+                                                             red_char_device_write_retry,
+                                                             self);
+        if (!self->priv->write_to_dev_timer) {
+            spice_error("failed creating char dev write timer");
+        }
+    }
+
+    self->priv->sin->st = self;
+}
+
+static void
+red_char_device_get_property(GObject    *object,
+                             guint       property_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+    RedCharDevice *self = RED_CHAR_DEVICE(object);
+
+    switch (property_id)
+    {
+        case PROP_CHAR_DEV_INSTANCE:
+            g_value_set_pointer(value, self->priv->sin);
+            break;
+        case PROP_SPICE_SERVER:
+            g_value_set_pointer(value, self->priv->reds);
+            break;
+        case PROP_CLIENT_TOKENS_INTERVAL:
+            g_value_set_uint64(value, self->priv->client_tokens_interval);
+            break;
+        case PROP_SELF_TOKENS:
+            g_value_set_uint64(value, self->priv->num_self_tokens);
+            break;
+        case PROP_OPAQUE:
+            g_value_set_pointer(value, self->priv->opaque);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+    }
+}
+
+static void
+red_char_device_set_property(GObject      *object,
+                             guint         property_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+    RedCharDevice *self = RED_CHAR_DEVICE(object);
+
+    switch (property_id)
+    {
+        case PROP_CHAR_DEV_INSTANCE:
+            self->priv->sin = g_value_get_pointer(value);
+            break;
+        case PROP_SPICE_SERVER:
+            self->priv->reds = g_value_get_pointer(value);
+            break;
+        case PROP_CLIENT_TOKENS_INTERVAL:
+            self->priv->client_tokens_interval = g_value_get_uint64(value);
+            break;
+        case PROP_SELF_TOKENS:
+            self->priv->num_self_tokens = g_value_get_uint64(value);
+            break;
+        case PROP_OPAQUE:
+            self->priv->opaque = g_value_get_pointer(value);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+    }
+}
+
+static void
+red_char_device_on_sin_changed(GObject *object,
+                               GParamSpec *pspec G_GNUC_UNUSED,
+                               gpointer user_data G_GNUC_UNUSED)
+{
+    RedCharDevice *self = RED_CHAR_DEVICE(object);
+
+    red_char_device_init_device_instance(self);
+}
+
+static void
+red_char_device_finalize(GObject *object)
+{
+    RedCharDevice *self = RED_CHAR_DEVICE(object);
+
+    /* FIXME: replace with g_object_weak_ref () */
+    reds_on_char_device_state_destroy(self->priv->reds, self);
+    if (self->priv->write_to_dev_timer) {
+        reds_core_timer_remove(self->priv->reds, self->priv->write_to_dev_timer);
+        self->priv->write_to_dev_timer = NULL;
+    }
+    write_buffers_queue_free(&self->priv->write_queue);
+    write_buffers_queue_free(&self->priv->write_bufs_pool);
+    self->priv->cur_pool_size = 0;
+    red_char_device_write_buffer_free(self->priv->cur_write_buf);
+    self->priv->cur_write_buf = NULL;
+
+    while (!ring_is_empty(&self->priv->clients)) {
+        RingItem *item = ring_get_tail(&self->priv->clients);
+        RedCharDeviceClient *dev_client;
+
+        dev_client = SPICE_CONTAINEROF(item, RedCharDeviceClient, link);
+        red_char_device_client_free(self, dev_client);
+    }
+    self->priv->running = FALSE;
+
+    G_OBJECT_CLASS(red_char_device_parent_class)->finalize(object);
+}
+
+static void
+red_char_device_class_init(RedCharDeviceClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+    g_type_class_add_private(klass, sizeof (RedCharDevicePrivate));
+
+    object_class->get_property = red_char_device_get_property;
+    object_class->set_property = red_char_device_set_property;
+    object_class->finalize = red_char_device_finalize;
+
+    g_object_class_install_property(object_class,
+                                    PROP_CHAR_DEV_INSTANCE,
+                                    g_param_spec_pointer("sin",
+                                                         "sin",
+                                                         "Char device instance",
+                                                         G_PARAM_STATIC_STRINGS |
+                                                         G_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT));
+    g_object_class_install_property(object_class,
+                                    PROP_SPICE_SERVER,
+                                    g_param_spec_pointer("spice-server",
+                                                         "spice-server",
+                                                         "RedsState instance",
+                                                         G_PARAM_STATIC_STRINGS |
+                                                         G_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT));
+    g_object_class_install_property(object_class,
+                                    PROP_CLIENT_TOKENS_INTERVAL,
+                                    g_param_spec_uint64("client-tokens-interval",
+                                                        "client-tokens-interval",
+                                                        "Client token interval",
+                                                        0, G_MAXUINT64, 0,
+                                                        G_PARAM_STATIC_STRINGS |
+                                                        G_PARAM_READWRITE));
+    g_object_class_install_property(object_class,
+                                    PROP_SELF_TOKENS,
+                                    g_param_spec_uint64("self-tokens",
+                                                        "self-tokens",
+                                                        "Self tokens",
+                                                        0, G_MAXUINT64, 0,
+                                                        G_PARAM_STATIC_STRINGS |
+                                                        G_PARAM_READWRITE));
+    g_object_class_install_property(object_class,
+                                    PROP_OPAQUE,
+                                    g_param_spec_pointer("opaque",
+                                                         "opaque",
+                                                         "User data to pass to callbacks",
+                                                      G_PARAM_STATIC_STRINGS |
+                                                      G_PARAM_READWRITE));
+
+}
+
+static void
+red_char_device_init(RedCharDevice *self)
+{
+    self->priv = RED_CHAR_DEVICE_PRIVATE(self);
+
+    ring_init(&self->priv->write_queue);
+    ring_init(&self->priv->write_bufs_pool);
+    ring_init(&self->priv->clients);
+
+    g_signal_connect(self, "notify::sin", G_CALLBACK(red_char_device_on_sin_changed), NULL);
+}
+
+static RedCharDevice *
+red_char_device_new(SpiceCharDeviceInstance *sin,
+                    RedsState *reds,
+                    uint32_t client_tokens_interval,
+                    uint32_t self_tokens,
+                    RedCharDeviceCallbacks *cbs,
+                    void *opaque)
+{
+    RedCharDevice *char_dev;
+
+    char_dev = g_object_new(RED_TYPE_CHAR_DEVICE,
+                            "sin", sin,
+                            "reds", reds,
+                            "client-tokens-interval", (guint64) client_tokens_interval,
+                            "self-tokens", (guint64) self_tokens,
+                            "opaque", opaque,
+                            NULL);
+
+    /* TODO: redundant with the "opaque" property in g_object_new */
+    red_char_device_set_callbacks(char_dev, cbs, opaque);
+
+    return char_dev;
+}
+
+/* TODO: needs to be moved to class vfuncs once all child classes are gobjects */
+void
+red_char_device_set_callbacks(RedCharDevice *dev,
+                              RedCharDeviceCallbacks *cbs,
+                              gpointer opaque)
+{
+    g_assert(cbs->read_one_msg_from_device && cbs->ref_msg_to_client &&
+             cbs->unref_msg_to_client && cbs->send_msg_to_client &&
+             cbs->send_tokens_to_client && cbs->remove_client);
+
+    dev->priv->cbs = *cbs;
+    dev->priv->opaque = opaque;
+}
diff --git a/server/char-device.h b/server/char-device.h
index 831631b..d92cbab 100644
--- a/server/char-device.h
+++ b/server/char-device.h
@@ -18,10 +18,44 @@ 
 #ifndef CHAR_DEVICE_H_
 #define CHAR_DEVICE_H_
 
+#include <glib-object.h>
+
 #include "spice.h"
 #include "red-channel.h"
 #include "migration-protocol.h"
 
+#define RED_TYPE_CHAR_DEVICE red_char_device_get_type()
+
+#define RED_CHAR_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), RED_TYPE_CHAR_DEVICE, RedCharDevice))
+#define RED_CHAR_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), RED_TYPE_CHAR_DEVICE, RedCharDeviceClass))
+#define RED_IS_CHAR_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), RED_TYPE_CHAR_DEVICE))
+#define RED_IS_CHAR_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RED_TYPE_CHAR_DEVICE))
+#define RED_CHAR_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_CHAR_DEVICE, RedCharDeviceClass))
+
+typedef struct SpiceCharDeviceState RedCharDevice;
+typedef struct RedCharDeviceClass RedCharDeviceClass;
+typedef struct RedCharDevicePrivate RedCharDevicePrivate;
+
+/* 'SpiceCharDeviceState' name is used for consistency with what spice-char.h exports */
+struct SpiceCharDeviceState
+{
+    GObject parent;
+
+    RedCharDevicePrivate *priv;
+};
+
+struct RedCharDeviceClass
+{
+    GObjectClass parent_class;
+};
+
+GType red_char_device_get_type(void) G_GNUC_CONST;
+
+typedef struct RedCharDeviceCallbacks RedCharDeviceCallbacks;
+void red_char_device_set_callbacks(RedCharDevice *dev,
+                                   RedCharDeviceCallbacks *cbs,
+                                   gpointer opaque);
+
 /*
  * Shared code for char devices, mainly for flow control.
  *
@@ -55,6 +89,11 @@ 
  * red_char_device_stop
  * red_char_device_wakeup (for reading from the device)
  */
+/* refcounting is used to protect the char_dev from being deallocated in
+ * case red_char_device_destroy has been called
+ * during a callback, and we might still access the char_dev afterwards.
+ */
+
 
 /*
  * Note about multiple-clients:
@@ -99,7 +138,7 @@  typedef struct RedCharDeviceWriteBuffer {
 
 typedef void RedCharDeviceMsgToClient;
 
-typedef struct RedCharDeviceCallbacks {
+struct RedCharDeviceCallbacks {
     /*
      * Messages that are addressed to the client can be queued in case we have
      * multiple clients and some of them don't have enough tokens.
@@ -129,7 +168,7 @@  typedef struct RedCharDeviceCallbacks {
      * due to slow flow or due to some other error.
      * The called instance should disconnect the client, or at least the corresponding channel */
     void (*remove_client)(RedClient *client, void *opaque);
-} RedCharDeviceCallbacks;
+};
 
 RedCharDevice *red_char_device_create(SpiceCharDeviceInstance *sin,
                                       struct RedsState *reds,

Comments

Now that we inserted the rename patches before this one, the commit log is no
longer very accurate. Should be:

char-device: Make RedCharDevice a GObject

ACK with updated commit log

Acked-by: Jonathon Jongsma <jjongsma@redhat.com>


On Fri, 2016-04-01 at 15:51 -0500, Jonathon Jongsma wrote:
> From: Christophe Fergeau <cfergeau@redhat.com>
> 
> Reviewed-by: Jonathon Jongsma <jjongsma@redhat.com>
> ---
>  server/char-device.c | 353 ++++++++++++++++++++++++++++++++++++++------------
> -
>  server/char-device.h |  43 ++++++-
>  2 files changed, 304 insertions(+), 92 deletions(-)
> 
> diff --git a/server/char-device.c b/server/char-device.c
> index 4b15217..f492657 100644
> --- a/server/char-device.c
> +++ b/server/char-device.c
> @@ -1,6 +1,6 @@
>  /* spice-server char device flow control code
>  
> -   Copyright (C) 2012 Red Hat, Inc.
> +   Copyright (C) 2012-2015 Red Hat, Inc.
>  
>     Red Hat Authors:
>     Yonit Halperin <yhalperi@redhat.com>
> @@ -74,11 +74,6 @@ struct RedCharDevicePrivate {
>      SpiceServer *reds;
>  };
>  
> -/* typedef'ed as RedCharDevice */
> -struct SpiceCharDeviceState {
> -    struct RedCharDevicePrivate priv[1];
> -};
> -
>  enum {
>      WRITE_BUFFER_ORIGIN_NONE,
>      WRITE_BUFFER_ORIGIN_CLIENT,
> @@ -86,12 +81,21 @@ enum {
>      WRITE_BUFFER_ORIGIN_SERVER_NO_TOKEN,
>  };
>  
> -/* Holding references for avoiding access violation if the char device was
> - * destroyed during a callback */
> -static void red_char_device_ref(RedCharDevice *char_dev);
> -static void red_char_device_unref(RedCharDevice *char_dev);
> -static void red_char_device_write_buffer_unref(RedCharDeviceWriteBuffer
> *write_buf);
>  
> +G_DEFINE_TYPE(RedCharDevice, red_char_device, G_TYPE_OBJECT)
> +
> +#define RED_CHAR_DEVICE_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o),
> RED_TYPE_CHAR_DEVICE, RedCharDevicePrivate))
> +
> +enum {
> +    PROP_0,
> +    PROP_CHAR_DEV_INSTANCE,
> +    PROP_SPICE_SERVER,
> +    PROP_CLIENT_TOKENS_INTERVAL,
> +    PROP_SELF_TOKENS,
> +    PROP_OPAQUE
> +};
> +
> +static void red_char_device_write_buffer_unref(RedCharDeviceWriteBuffer
> *write_buf);
>  static void red_char_device_write_retry(void *opaque);
>  
>  typedef struct RedCharDeviceMsgToClientItem {
> @@ -99,6 +103,13 @@ typedef struct RedCharDeviceMsgToClientItem {
>      RedCharDeviceMsgToClient *msg;
>  } RedCharDeviceMsgToClientItem;
>  
> +static RedCharDevice *red_char_device_new(SpiceCharDeviceInstance *sin,
> +                                          RedsState *reds,
> +                                          uint32_t client_tokens_interval,
> +                                          uint32_t self_tokens,
> +                                          RedCharDeviceCallbacks *cbs,
> +                                          void *opaque);
> +
>  static RedCharDeviceMsgToClient *
>  red_char_device_read_one_msg_from_device(RedCharDevice *dev)
>  {
> @@ -365,7 +376,7 @@ static int red_char_device_read_from_device(RedCharDevice
> *dev)
>      }
>  
>      max_send_tokens = red_char_device_max_send_tokens(dev);
> -    red_char_device_ref(dev);
> +    g_object_ref(dev);
>      /*
>       * Reading from the device only in case at least one of the clients have
> a free token.
>       * All messages will be discarded if no client is attached to the device
> @@ -391,7 +402,7 @@ static int red_char_device_read_from_device(RedCharDevice
> *dev)
>      if (dev->priv->running) {
>          dev->priv->active = dev->priv->active || did_read;
>      }
> -    red_char_device_unref(dev);
> +    g_object_unref(dev);
>      return did_read;
>  }
>  
> @@ -508,7 +519,7 @@ static int red_char_device_write_to_device(RedCharDevice
> *dev)
>          return 0;
>      }
>  
> -    red_char_device_ref(dev);
> +    g_object_ref(dev);
>  
>      if (dev->priv->write_to_dev_timer) {
>          reds_core_timer_cancel(dev->priv->reds, dev->priv
> ->write_to_dev_timer);
> @@ -562,7 +573,7 @@ static int red_char_device_write_to_device(RedCharDevice
> *dev)
>          dev->priv->active = dev->priv->active || total;
>      }
>      dev->priv->during_write_to_device = 0;
> -    red_char_device_unref(dev);
> +    g_object_unref(dev);
>      return total;
>  }
>  
> @@ -727,39 +738,8 @@ RedCharDevice
> *red_char_device_create(SpiceCharDeviceInstance *sin,
>                                        RedCharDeviceCallbacks *cbs,
>                                        void *opaque)
>  {
> -    RedCharDevice *char_dev;
> -    SpiceCharDeviceInterface *sif;
> -
> -    spice_assert(sin);
> -    spice_assert(cbs->read_one_msg_from_device && cbs->ref_msg_to_client &&
> -                 cbs->unref_msg_to_client && cbs->send_msg_to_client &&
> -                 cbs->send_tokens_to_client && cbs->remove_client);
> -
> -    char_dev = spice_new0(RedCharDevice, 1);
> -    char_dev->priv->sin = sin;
> -    char_dev->priv->reds = reds;
> -    char_dev->priv->cbs = *cbs;
> -    char_dev->priv->opaque = opaque;
> -    char_dev->priv->client_tokens_interval = client_tokens_interval;
> -    char_dev->priv->num_self_tokens = self_tokens;
> -
> -    ring_init(&char_dev->priv->write_queue);
> -    ring_init(&char_dev->priv->write_bufs_pool);
> -    ring_init(&char_dev->priv->clients);
> -
> -    sif = spice_char_device_get_interface(char_dev->priv->sin);
> -    if (sif->base.minor_version <= 2 ||
> -        !(sif->flags & SPICE_CHAR_DEVICE_NOTIFY_WRITABLE)) {
> -        char_dev->priv->write_to_dev_timer = reds_core_timer_add(reds,
> red_char_device_write_retry, char_dev);
> -        if (!char_dev->priv->write_to_dev_timer) {
> -            spice_error("failed creating char dev write timer");
> -        }
> -    }
> -
> -    char_dev->priv->refs = 1;
> -    sin->st = char_dev;
> -    spice_debug("sin %p dev_state %p", sin, char_dev);
> -    return char_dev;
> +    return red_char_device_new(sin, reds, client_tokens_interval,
> +                               self_tokens, cbs, opaque);
>  }
>  
>  void red_char_device_reset_dev_instance(RedCharDevice *state,
> @@ -775,48 +755,10 @@ void *red_char_device_opaque_get(RedCharDevice *dev)
>      return dev->priv->opaque;
>  }
>  
> -static void red_char_device_ref(RedCharDevice *char_dev)
> -{
> -    char_dev->priv->refs++;
> -}
> -
> -static void red_char_device_unref(RedCharDevice *char_dev)
> -{
> -    /* The refs field protects the char_dev from being deallocated in
> -     * case red_char_device_destroy has been called
> -     * during a callabck, and we might still access the char_dev afterwards.
> -     * red_char_device_unref is always coupled with a preceding
> -     * red_char_device_ref. Here, refs can turn 0
> -     * only when red_char_device_destroy is called in between
> -     * the calls to red_char_device_ref and red_char_device_unref.*/
> -    if (!--char_dev->priv->refs) {
> -        free(char_dev);
> -    }
> -}
> -
>  void red_char_device_destroy(RedCharDevice *char_dev)
>  {
> -    reds_on_char_device_state_destroy(char_dev->priv->reds, char_dev);
> -    if (char_dev->priv->write_to_dev_timer) {
> -        reds_core_timer_remove(char_dev->priv->reds, char_dev->priv
> ->write_to_dev_timer);
> -        char_dev->priv->write_to_dev_timer = NULL;
> -    }
> -    write_buffers_queue_free(&char_dev->priv->write_queue);
> -    write_buffers_queue_free(&char_dev->priv->write_bufs_pool);
> -    char_dev->priv->cur_pool_size = 0;
> -    red_char_device_write_buffer_free(char_dev->priv->cur_write_buf);
> -    char_dev->priv->cur_write_buf = NULL;
> -
> -    while (!ring_is_empty(&char_dev->priv->clients)) {
> -        RingItem *item = ring_get_tail(&char_dev->priv->clients);
> -        RedCharDeviceClient *dev_client;
> -
> -        dev_client = SPICE_CONTAINEROF(item, RedCharDeviceClient, link);
> -        red_char_device_client_free(char_dev, dev_client);
> -    }
> -    char_dev->priv->running = FALSE;
> -
> -    red_char_device_unref(char_dev);
> +    g_return_if_fail(RED_IS_CHAR_DEVICE(char_dev));
> +    g_object_unref(char_dev);
>  }
>  
>  static RedCharDeviceClient *red_char_device_client_new(RedClient *client,
> @@ -920,10 +862,10 @@ void red_char_device_start(RedCharDevice *dev)
>  {
>      spice_debug("dev_state %p", dev);
>      dev->priv->running = TRUE;
> -    red_char_device_ref(dev);
> +    g_object_ref(dev);
>      while (red_char_device_write_to_device(dev) ||
>             red_char_device_read_from_device(dev));
> -    red_char_device_unref(dev);
> +    g_object_unref(dev);
>  }
>  
>  void red_char_device_stop(RedCharDevice *dev)
> @@ -1117,3 +1059,234 @@ SpiceCharDeviceInterface
> *spice_char_device_get_interface(SpiceCharDeviceInstanc
>  {
>     return SPICE_CONTAINEROF(instance->base.sif, SpiceCharDeviceInterface,
> base);
>  }
> +
> +
> +static void red_char_device_init_device_instance(RedCharDevice *self)
> +{
> +    SpiceCharDeviceInterface *sif;
> +
> +    g_return_if_fail(self->priv->reds);
> +
> +    if (self->priv->write_to_dev_timer) {
> +        reds_core_timer_remove(self->priv->reds, self->priv
> ->write_to_dev_timer);
> +        self->priv->write_to_dev_timer = NULL;
> +    }
> +    if (self->priv->sin == NULL) {
> +       return;
> +    }
> +
> +    sif = spice_char_device_get_interface(self->priv->sin);
> +    if (sif->base.minor_version <= 2 ||
> +        !(sif->flags & SPICE_CHAR_DEVICE_NOTIFY_WRITABLE)) {
> +        self->priv->write_to_dev_timer = reds_core_timer_add(self->priv
> ->reds,
> +                                                            
>  red_char_device_write_retry,
> +                                                             self);
> +        if (!self->priv->write_to_dev_timer) {
> +            spice_error("failed creating char dev write timer");
> +        }
> +    }
> +
> +    self->priv->sin->st = self;
> +}
> +
> +static void
> +red_char_device_get_property(GObject    *object,
> +                             guint       property_id,
> +                             GValue     *value,
> +                             GParamSpec *pspec)
> +{
> +    RedCharDevice *self = RED_CHAR_DEVICE(object);
> +
> +    switch (property_id)
> +    {
> +        case PROP_CHAR_DEV_INSTANCE:
> +            g_value_set_pointer(value, self->priv->sin);
> +            break;
> +        case PROP_SPICE_SERVER:
> +            g_value_set_pointer(value, self->priv->reds);
> +            break;
> +        case PROP_CLIENT_TOKENS_INTERVAL:
> +            g_value_set_uint64(value, self->priv->client_tokens_interval);
> +            break;
> +        case PROP_SELF_TOKENS:
> +            g_value_set_uint64(value, self->priv->num_self_tokens);
> +            break;
> +        case PROP_OPAQUE:
> +            g_value_set_pointer(value, self->priv->opaque);
> +            break;
> +        default:
> +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +    }
> +}
> +
> +static void
> +red_char_device_set_property(GObject      *object,
> +                             guint         property_id,
> +                             const GValue *value,
> +                             GParamSpec   *pspec)
> +{
> +    RedCharDevice *self = RED_CHAR_DEVICE(object);
> +
> +    switch (property_id)
> +    {
> +        case PROP_CHAR_DEV_INSTANCE:
> +            self->priv->sin = g_value_get_pointer(value);
> +            break;
> +        case PROP_SPICE_SERVER:
> +            self->priv->reds = g_value_get_pointer(value);
> +            break;
> +        case PROP_CLIENT_TOKENS_INTERVAL:
> +            self->priv->client_tokens_interval = g_value_get_uint64(value);
> +            break;
> +        case PROP_SELF_TOKENS:
> +            self->priv->num_self_tokens = g_value_get_uint64(value);
> +            break;
> +        case PROP_OPAQUE:
> +            self->priv->opaque = g_value_get_pointer(value);
> +            break;
> +        default:
> +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +    }
> +}
> +
> +static void
> +red_char_device_on_sin_changed(GObject *object,
> +                               GParamSpec *pspec G_GNUC_UNUSED,
> +                               gpointer user_data G_GNUC_UNUSED)
> +{
> +    RedCharDevice *self = RED_CHAR_DEVICE(object);
> +
> +    red_char_device_init_device_instance(self);
> +}
> +
> +static void
> +red_char_device_finalize(GObject *object)
> +{
> +    RedCharDevice *self = RED_CHAR_DEVICE(object);
> +
> +    /* FIXME: replace with g_object_weak_ref () */
> +    reds_on_char_device_state_destroy(self->priv->reds, self);
> +    if (self->priv->write_to_dev_timer) {
> +        reds_core_timer_remove(self->priv->reds, self->priv
> ->write_to_dev_timer);
> +        self->priv->write_to_dev_timer = NULL;
> +    }
> +    write_buffers_queue_free(&self->priv->write_queue);
> +    write_buffers_queue_free(&self->priv->write_bufs_pool);
> +    self->priv->cur_pool_size = 0;
> +    red_char_device_write_buffer_free(self->priv->cur_write_buf);
> +    self->priv->cur_write_buf = NULL;
> +
> +    while (!ring_is_empty(&self->priv->clients)) {
> +        RingItem *item = ring_get_tail(&self->priv->clients);
> +        RedCharDeviceClient *dev_client;
> +
> +        dev_client = SPICE_CONTAINEROF(item, RedCharDeviceClient, link);
> +        red_char_device_client_free(self, dev_client);
> +    }
> +    self->priv->running = FALSE;
> +
> +    G_OBJECT_CLASS(red_char_device_parent_class)->finalize(object);
> +}
> +
> +static void
> +red_char_device_class_init(RedCharDeviceClass *klass)
> +{
> +    GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +
> +    g_type_class_add_private(klass, sizeof (RedCharDevicePrivate));
> +
> +    object_class->get_property = red_char_device_get_property;
> +    object_class->set_property = red_char_device_set_property;
> +    object_class->finalize = red_char_device_finalize;
> +
> +    g_object_class_install_property(object_class,
> +                                    PROP_CHAR_DEV_INSTANCE,
> +                                    g_param_spec_pointer("sin",
> +                                                         "sin",
> +                                                         "Char device
> instance",
> +                                                        
>  G_PARAM_STATIC_STRINGS |
> +                                                         G_PARAM_READWRITE |
> +                                                         G_PARAM_CONSTRUCT));
> +    g_object_class_install_property(object_class,
> +                                    PROP_SPICE_SERVER,
> +                                    g_param_spec_pointer("spice-server",
> +                                                         "spice-server",
> +                                                         "RedsState
> instance",
> +                                                        
>  G_PARAM_STATIC_STRINGS |
> +                                                         G_PARAM_READWRITE |
> +                                                         G_PARAM_CONSTRUCT));
> +    g_object_class_install_property(object_class,
> +                                    PROP_CLIENT_TOKENS_INTERVAL,
> +                                    g_param_spec_uint64("client-tokens
> -interval",
> +                                                        "client-tokens
> -interval",
> +                                                        "Client token
> interval",
> +                                                        0, G_MAXUINT64, 0,
> +                                                       
>  G_PARAM_STATIC_STRINGS |
> +                                                        G_PARAM_READWRITE));
> +    g_object_class_install_property(object_class,
> +                                    PROP_SELF_TOKENS,
> +                                    g_param_spec_uint64("self-tokens",
> +                                                        "self-tokens",
> +                                                        "Self tokens",
> +                                                        0, G_MAXUINT64, 0,
> +                                                       
>  G_PARAM_STATIC_STRINGS |
> +                                                        G_PARAM_READWRITE));
> +    g_object_class_install_property(object_class,
> +                                    PROP_OPAQUE,
> +                                    g_param_spec_pointer("opaque",
> +                                                         "opaque",
> +                                                         "User data to pass
> to callbacks",
> +                                                      G_PARAM_STATIC_STRINGS
> |
> +                                                      G_PARAM_READWRITE));
> +
> +}
> +
> +static void
> +red_char_device_init(RedCharDevice *self)
> +{
> +    self->priv = RED_CHAR_DEVICE_PRIVATE(self);
> +
> +    ring_init(&self->priv->write_queue);
> +    ring_init(&self->priv->write_bufs_pool);
> +    ring_init(&self->priv->clients);
> +
> +    g_signal_connect(self, "notify::sin",
> G_CALLBACK(red_char_device_on_sin_changed), NULL);
> +}
> +
> +static RedCharDevice *
> +red_char_device_new(SpiceCharDeviceInstance *sin,
> +                    RedsState *reds,
> +                    uint32_t client_tokens_interval,
> +                    uint32_t self_tokens,
> +                    RedCharDeviceCallbacks *cbs,
> +                    void *opaque)
> +{
> +    RedCharDevice *char_dev;
> +
> +    char_dev = g_object_new(RED_TYPE_CHAR_DEVICE,
> +                            "sin", sin,
> +                            "reds", reds,
> +                            "client-tokens-interval", (guint64)
> client_tokens_interval,
> +                            "self-tokens", (guint64) self_tokens,
> +                            "opaque", opaque,
> +                            NULL);
> +
> +    /* TODO: redundant with the "opaque" property in g_object_new */
> +    red_char_device_set_callbacks(char_dev, cbs, opaque);
> +
> +    return char_dev;
> +}
> +
> +/* TODO: needs to be moved to class vfuncs once all child classes are
> gobjects */
> +void
> +red_char_device_set_callbacks(RedCharDevice *dev,
> +                              RedCharDeviceCallbacks *cbs,
> +                              gpointer opaque)
> +{
> +    g_assert(cbs->read_one_msg_from_device && cbs->ref_msg_to_client &&
> +             cbs->unref_msg_to_client && cbs->send_msg_to_client &&
> +             cbs->send_tokens_to_client && cbs->remove_client);
> +
> +    dev->priv->cbs = *cbs;
> +    dev->priv->opaque = opaque;
> +}
> diff --git a/server/char-device.h b/server/char-device.h
> index 831631b..d92cbab 100644
> --- a/server/char-device.h
> +++ b/server/char-device.h
> @@ -18,10 +18,44 @@
>  #ifndef CHAR_DEVICE_H_
>  #define CHAR_DEVICE_H_
>  
> +#include <glib-object.h>
> +
>  #include "spice.h"
>  #include "red-channel.h"
>  #include "migration-protocol.h"
>  
> +#define RED_TYPE_CHAR_DEVICE red_char_device_get_type()
> +
> +#define RED_CHAR_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
> RED_TYPE_CHAR_DEVICE, RedCharDevice))
> +#define RED_CHAR_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),
> RED_TYPE_CHAR_DEVICE, RedCharDeviceClass))
> +#define RED_IS_CHAR_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
> RED_TYPE_CHAR_DEVICE))
> +#define RED_IS_CHAR_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),
> RED_TYPE_CHAR_DEVICE))
> +#define RED_CHAR_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),
> RED_TYPE_CHAR_DEVICE, RedCharDeviceClass))
> +
> +typedef struct SpiceCharDeviceState RedCharDevice;
> +typedef struct RedCharDeviceClass RedCharDeviceClass;
> +typedef struct RedCharDevicePrivate RedCharDevicePrivate;
> +
> +/* 'SpiceCharDeviceState' name is used for consistency with what spice-char.h
> exports */
> +struct SpiceCharDeviceState
> +{
> +    GObject parent;
> +
> +    RedCharDevicePrivate *priv;
> +};
> +
> +struct RedCharDeviceClass
> +{
> +    GObjectClass parent_class;
> +};
> +
> +GType red_char_device_get_type(void) G_GNUC_CONST;
> +
> +typedef struct RedCharDeviceCallbacks RedCharDeviceCallbacks;
> +void red_char_device_set_callbacks(RedCharDevice *dev,
> +                                   RedCharDeviceCallbacks *cbs,
> +                                   gpointer opaque);
> +
>  /*
>   * Shared code for char devices, mainly for flow control.
>   *
> @@ -55,6 +89,11 @@
>   * red_char_device_stop
>   * red_char_device_wakeup (for reading from the device)
>   */
> +/* refcounting is used to protect the char_dev from being deallocated in
> + * case red_char_device_destroy has been called
> + * during a callback, and we might still access the char_dev afterwards.
> + */
> +
>  
>  /*
>   * Note about multiple-clients:
> @@ -99,7 +138,7 @@ typedef struct RedCharDeviceWriteBuffer {
>  
>  typedef void RedCharDeviceMsgToClient;
>  
> -typedef struct RedCharDeviceCallbacks {
> +struct RedCharDeviceCallbacks {
>      /*
>       * Messages that are addressed to the client can be queued in case we
> have
>       * multiple clients and some of them don't have enough tokens.
> @@ -129,7 +168,7 @@ typedef struct RedCharDeviceCallbacks {
>       * due to slow flow or due to some other error.
>       * The called instance should disconnect the client, or at least the
> corresponding channel */
>      void (*remove_client)(RedClient *client, void *opaque);
> -} RedCharDeviceCallbacks;
> +};
>  
>  RedCharDevice *red_char_device_create(SpiceCharDeviceInstance *sin,
>                                        struct RedsState *reds,