[Spice-commits] 3 commits - server/Makefile.am server/char-device.c server/common-graphics-channel.c server/dcc.c server/dummy-channel-client.c server/inputs-channel.c server/main-channel-client.c server/main-channel.c server/main-dispatcher.c server/red-channel-client.c server/red-channel.c server/red-channel.h server/red-client.c server/red-client.h server/reds.c server/sound.c server/stream.c

Frediano Ziglio fziglio at kemper.freedesktop.org
Wed Nov 2 19:33:17 UTC 2016


 server/Makefile.am               |    2 
 server/char-device.c             |    6 
 server/common-graphics-channel.c |    2 
 server/dcc.c                     |    1 
 server/dummy-channel-client.c    |   35 ---
 server/inputs-channel.c          |    1 
 server/main-channel-client.c     |    1 
 server/main-channel.c            |    1 
 server/main-dispatcher.c         |   16 -
 server/red-channel-client.c      |   35 ---
 server/red-channel.c             |  218 +----------------------
 server/red-channel.h             |   62 ------
 server/red-client.c              |  364 +++++++++++++++++++++++++++++++++++++++
 server/red-client.h              |   76 ++++++++
 server/reds.c                    |    7 
 server/sound.c                   |    1 
 server/stream.c                  |    1 
 17 files changed, 490 insertions(+), 339 deletions(-)

New commits:
commit fe1583a6a93bc52e89747f2f646c4514553f4498
Author: Jonathon Jongsma <jjongsma at redhat.com>
Date:   Wed Oct 26 14:47:36 2016 -0500

    Convert RedClient to GObject
    
    Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/server/main-dispatcher.c b/server/main-dispatcher.c
index fe29b2d..09ac1cc 100644
--- a/server/main-dispatcher.c
+++ b/server/main-dispatcher.c
@@ -208,7 +208,7 @@ static void main_dispatcher_handle_migrate_complete(void *opaque,
     MainDispatcherMigrateSeamlessDstCompleteMessage *mig_complete = payload;
 
     reds_on_client_seamless_migrate_complete(self->priv->reds, mig_complete->client);
-    red_client_unref(mig_complete->client);
+    g_object_unref(mig_complete->client);
 }
 
 static void main_dispatcher_handle_mm_time_latency(void *opaque,
@@ -217,7 +217,7 @@ static void main_dispatcher_handle_mm_time_latency(void *opaque,
     MainDispatcher *self = opaque;
     MainDispatcherMmTimeLatencyMessage *msg = payload;
     reds_set_client_mm_time_latency(self->priv->reds, msg->client, msg->latency);
-    red_client_unref(msg->client);
+    g_object_unref(msg->client);
 }
 
 static void main_dispatcher_handle_client_disconnect(void *opaque,
@@ -228,7 +228,7 @@ static void main_dispatcher_handle_client_disconnect(void *opaque,
 
     spice_debug("client=%p", msg->client);
     reds_client_disconnect(self->priv->reds, msg->client);
-    red_client_unref(msg->client);
+    g_object_unref(msg->client);
 }
 
 void main_dispatcher_seamless_migrate_dst_complete(MainDispatcher *self,
@@ -241,7 +241,7 @@ void main_dispatcher_seamless_migrate_dst_complete(MainDispatcher *self,
         return;
     }
 
-    msg.client = red_client_ref(client);
+    msg.client = g_object_ref(client);
     dispatcher_send_message(DISPATCHER(self), MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
                             &msg);
 }
@@ -255,7 +255,7 @@ void main_dispatcher_set_mm_time_latency(MainDispatcher *self, RedClient *client
         return;
     }
 
-    msg.client = red_client_ref(client);
+    msg.client = g_object_ref(client);
     msg.latency = latency;
     dispatcher_send_message(DISPATCHER(self), MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
                             &msg);
@@ -267,7 +267,7 @@ void main_dispatcher_client_disconnect(MainDispatcher *self, RedClient *client)
 
     if (!red_client_is_disconnecting(client)) {
         spice_debug("client %p", client);
-        msg.client = red_client_ref(client);
+        msg.client = g_object_ref(client);
         dispatcher_send_message(DISPATCHER(self), MAIN_DISPATCHER_CLIENT_DISCONNECT,
                                 &msg);
     } else {
diff --git a/server/red-client.c b/server/red-client.c
index 9b7d76e..f7f4562 100644
--- a/server/red-client.c
+++ b/server/red-client.c
@@ -27,6 +27,7 @@
     GLIST_FOREACH((_client ? (_client)->channels : NULL), _iter, RedChannelClient, _data)
 
 struct RedClient {
+    GObject parent;
     RedsState *reds;
     GList *channels;
     MainChannelClient *mcc;
@@ -45,39 +46,111 @@ struct RedClient {
     int during_target_migrate;
     int seamless_migrate;
     int num_migrated_channels; /* for seamless - number of channels that wait for migrate data*/
-    int refs;
 };
 
-RedClient *red_client_ref(RedClient *client)
+struct RedClientClass
 {
-    spice_assert(client);
-    g_atomic_int_inc(&client->refs);
-    return client;
+    GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE(RedClient, red_client, G_TYPE_OBJECT)
+
+enum {
+    PROP0,
+    PROP_SPICE_SERVER,
+    PROP_MIGRATED
+};
+
+static void
+red_client_get_property (GObject    *object,
+                         guint       property_id,
+                         GValue     *value,
+                         GParamSpec *pspec)
+{
+    RedClient *self = RED_CLIENT(object);
+
+    switch (property_id)
+    {
+        case PROP_SPICE_SERVER:
+            g_value_set_pointer(value, self->reds);
+            break;
+        case PROP_MIGRATED:
+            g_value_set_boolean(value, self->during_target_migrate);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
 }
 
-RedClient *red_client_unref(RedClient *client)
+static void
+red_client_set_property (GObject      *object,
+                         guint         property_id,
+                         const GValue *value,
+                         GParamSpec   *pspec)
 {
-    if (g_atomic_int_dec_and_test(&client->refs)) {
-        spice_debug("release client=%p", client);
-        pthread_mutex_destroy(&client->lock);
-        free(client);
-        return NULL;
+    RedClient *self = RED_CLIENT(object);
+
+    switch (property_id)
+    {
+        case PROP_SPICE_SERVER:
+            self->reds = g_value_get_pointer(value);
+            break;
+        case PROP_MIGRATED:
+            self->during_target_migrate = g_value_get_boolean(value);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
-    return client;
 }
 
-RedClient *red_client_new(RedsState *reds, int migrated)
+static void
+red_client_finalize (GObject *object)
+{
+    RedClient *self = RED_CLIENT(object);
+
+    spice_debug("release client=%p", self);
+    pthread_mutex_destroy(&self->lock);
+
+    G_OBJECT_CLASS (red_client_parent_class)->finalize (object);
+}
+
+static void
+red_client_class_init (RedClientClass *klass)
 {
-    RedClient *client;
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = red_client_get_property;
+  object_class->set_property = red_client_set_property;
+  object_class->finalize = red_client_finalize;
+
+  g_object_class_install_property(object_class,
+                                  PROP_SPICE_SERVER,
+                                  g_param_spec_pointer("spice-server",
+                                                       "Spice server",
+                                                       "The Spice Server",
+                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property(object_class,
+                                  PROP_MIGRATED,
+                                  g_param_spec_boolean("migrated",
+                                                       "migrated",
+                                                       "Whether this client was migrated",
+                                                       FALSE,
+                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
 
-    client = spice_malloc0(sizeof(RedClient));
-    client->reds = reds;
-    pthread_mutex_init(&client->lock, NULL);
-    client->thread_id = pthread_self();
-    client->during_target_migrate = migrated;
-    client->refs = 1;
+static void
+red_client_init (RedClient *self)
+{
+    pthread_mutex_init(&self->lock, NULL);
+    self->thread_id = pthread_self();
+}
 
-    return client;
+RedClient *red_client_new(RedsState *reds, int migrated)
+{
+  return g_object_new (RED_TYPE_CLIENT,
+                       "spice-server", reds,
+                       "migrated", migrated,
+                       NULL);
 }
 
 void red_client_set_migration_seamless(RedClient *client) // dest
@@ -148,9 +221,10 @@ void red_client_destroy(RedClient *client)
         spice_assert(red_channel_client_no_item_being_sent(rcc));
         red_channel_client_destroy(rcc);
     }
-    red_client_unref(client);
+    g_object_unref(client);
 }
 
+
 /* client->lock should be locked */
 RedChannelClient *red_client_get_channel(RedClient *client, int type, int id)
 {
diff --git a/server/red-client.h b/server/red-client.h
index 5b87e62..d7e9cc7 100644
--- a/server/red-client.h
+++ b/server/red-client.h
@@ -19,8 +19,26 @@
 #ifndef _H_RED_CLIENT
 #define _H_RED_CLIENT
 
+#include <glib-object.h>
+
 #include "main-channel-client.h"
 
+G_BEGIN_DECLS
+
+#define RED_TYPE_CLIENT red_client_get_type()
+
+#define RED_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RED_TYPE_CLIENT, RedClient))
+#define RED_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), RED_TYPE_CLIENT, RedClientClass))
+#define RED_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RED_TYPE_CLIENT))
+#define RED_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), RED_TYPE_CLIENT))
+#define RED_CLIENT_GET_CLASS(obj) \
+    (G_TYPE_INSTANCE_GET_CLASS ((obj), RED_TYPE_CLIENT, RedClientClass))
+
+typedef struct RedClient RedClient;
+typedef struct RedClientClass RedClientClass;
+
+GType red_client_get_type (void) G_GNUC_CONST;
+
 RedClient *red_client_new(RedsState *reds, int migrated);
 
 /*
@@ -28,15 +46,6 @@ RedClient *red_client_new(RedsState *reds, int migrated);
  */
 void red_client_destroy(RedClient *client);
 
-RedClient *red_client_ref(RedClient *client);
-
-/*
- * releases the client resources when refs == 0.
- * We assume the red_client_destroy was called before
- * we reached refs==0
- */
-RedClient *red_client_unref(RedClient *client);
-
 gboolean red_client_add_channel(RedClient *client, RedChannelClient *rcc, GError **error);
 void red_client_remove_channel(RedChannelClient *rcc);
 RedChannelClient *red_client_get_channel(RedClient *client, int type, int id);
@@ -62,4 +71,6 @@ gboolean red_client_is_disconnecting(RedClient *client);
 void red_client_set_disconnecting(RedClient *client);
 RedsState* red_client_get_server(RedClient *client);
 
+G_END_DECLS
+
 #endif /* _H_RED_CLIENT */
diff --git a/server/reds.c b/server/reds.c
index b3e2535..30b9165 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -68,7 +68,7 @@
 #include "smartcard.h"
 #endif
 #include "reds-stream.h"
-#include "utils.h"
+#include "red-client.h"
 
 #include "reds-private.h"
 #include "video-encoder.h"
commit bc8d967e67ce1b1aa89db39ba74959041d92a0a7
Author: Jonathon Jongsma <jjongsma at redhat.com>
Date:   Wed Oct 26 14:47:35 2016 -0500

    Move RedClient to a separate file
    
    Also move the RedClient struct out of the header to avoid accessing the
    internals from other files.
    
    Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/server/Makefile.am b/server/Makefile.am
index 0514a2c..ca67be1 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -101,6 +101,8 @@ libserver_la_SOURCES =				\
 	red-channel-client.c			\
 	red-channel-client.h			\
 	red-channel-client-private.h		\
+	red-client.c				\
+	red-client.h				\
 	dummy-channel.c				\
 	dummy-channel.h				\
 	dummy-channel-client.c			\
diff --git a/server/char-device.c b/server/char-device.c
index 7775c07..318bf3c 100644
--- a/server/char-device.c
+++ b/server/char-device.c
@@ -23,7 +23,7 @@
 #include <config.h>
 #include <inttypes.h>
 #include "char-device.h"
-#include "red-channel.h"
+#include "red-client.h"
 #include "reds.h"
 #include "glib-compat.h"
 
@@ -711,8 +711,10 @@ static RedCharDeviceClient *red_char_device_client_new(RedClient *client,
     dev_client->max_send_queue_size = max_send_queue_size;
     dev_client->do_flow_control = do_flow_control;
     if (do_flow_control) {
+        RedsState *reds = red_client_get_server(client);
+
         dev_client->wait_for_tokens_timer =
-            reds_core_timer_add(client->reds, device_client_wait_for_tokens_timeout,
+            reds_core_timer_add(reds, device_client_wait_for_tokens_timeout,
                                 dev_client);
         if (!dev_client->wait_for_tokens_timer) {
             spice_error("failed to create wait for tokens timer");
diff --git a/server/common-graphics-channel.c b/server/common-graphics-channel.c
index 0311cae..29aa583 100644
--- a/server/common-graphics-channel.c
+++ b/server/common-graphics-channel.c
@@ -25,7 +25,7 @@
 
 #include "common-graphics-channel.h"
 #include "dcc.h"
-#include "main-channel-client.h"
+#include "red-client.h"
 
 #define CHANNEL_RECEIVE_BUF_SIZE 1024
 
diff --git a/server/dcc.c b/server/dcc.c
index 79fc7fd..17f9300 100644
--- a/server/dcc.c
+++ b/server/dcc.c
@@ -23,6 +23,7 @@
 #include "display-channel.h"
 #include "display-channel-private.h"
 #include "red-channel-client-private.h"
+#include "red-client.h"
 #include "main-channel-client.h"
 #include "spice-server-enums.h"
 
diff --git a/server/dummy-channel-client.c b/server/dummy-channel-client.c
index c602412..61d5683 100644
--- a/server/dummy-channel-client.c
+++ b/server/dummy-channel-client.c
@@ -20,6 +20,7 @@
 
 #include "dummy-channel-client.h"
 #include "red-channel.h"
+#include "red-client.h"
 
 static void dummy_channel_client_initable_interface_init(GInitableIface *iface);
 
diff --git a/server/inputs-channel.c b/server/inputs-channel.c
index f96a64c..7c397a4 100644
--- a/server/inputs-channel.c
+++ b/server/inputs-channel.c
@@ -40,6 +40,7 @@
 #include "reds-stream.h"
 #include "red-channel.h"
 #include "red-channel-client.h"
+#include "red-client.h"
 #include "inputs-channel-client.h"
 #include "main-channel-client.h"
 #include "inputs-channel.h"
diff --git a/server/main-channel-client.c b/server/main-channel-client.c
index f933717..7304586 100644
--- a/server/main-channel-client.c
+++ b/server/main-channel-client.c
@@ -24,6 +24,7 @@
 #include "main-channel-client.h"
 #include "main-channel.h"
 #include "red-channel-client.h"
+#include "red-client.h"
 #include "reds.h"
 
 #define NET_TEST_WARMUP_BYTES 0
diff --git a/server/main-channel.c b/server/main-channel.c
index cf5ee6a..b900b62 100644
--- a/server/main-channel.c
+++ b/server/main-channel.c
@@ -24,6 +24,7 @@
 #include "red-common.h"
 #include "reds.h"
 #include "red-channel-client.h"
+#include "red-client.h"
 #include "main-channel.h"
 #include "main-channel-client.h"
 
diff --git a/server/main-dispatcher.c b/server/main-dispatcher.c
index bc0de24..fe29b2d 100644
--- a/server/main-dispatcher.c
+++ b/server/main-dispatcher.c
@@ -24,7 +24,7 @@
 #include "red-common.h"
 #include "dispatcher.h"
 #include "main-dispatcher.h"
-#include "red-channel.h"
+#include "red-client.h"
 #include "reds.h"
 
 /*
@@ -265,7 +265,7 @@ void main_dispatcher_client_disconnect(MainDispatcher *self, RedClient *client)
 {
     MainDispatcherClientDisconnectMessage msg;
 
-    if (!client->disconnecting) {
+    if (!red_client_is_disconnecting(client)) {
         spice_debug("client %p", client);
         msg.client = red_client_ref(client);
         dispatcher_send_message(DISPATCHER(self), MAIN_DISPATCHER_CLIENT_DISCONNECT,
diff --git a/server/red-channel-client.c b/server/red-channel-client.c
index bfa44b4..84dd28f 100644
--- a/server/red-channel-client.c
+++ b/server/red-channel-client.c
@@ -35,7 +35,7 @@
 
 #include "red-channel-client.h"
 #include "red-channel-client-private.h"
-#include "red-channel.h"
+#include "red-client.h"
 #include "glib-compat.h"
 
 static const SpiceDataHeaderOpaque full_header_wrapper;
diff --git a/server/red-channel.c b/server/red-channel.c
index f0f5160..9279882 100644
--- a/server/red-channel.c
+++ b/server/red-channel.c
@@ -31,9 +31,6 @@
 #include "main-dispatcher.h"
 #include "utils.h"
 
-#define FOREACH_CHANNEL_CLIENT(_client, _iter, _data) \
-    GLIST_FOREACH((_client ? (_client)->channels : NULL), _iter, RedChannelClient, _data)
-
 /*
  * Lifetime of RedChannel, RedChannelClient and RedClient:
  * RedChannel is created and destroyed by the calls to
@@ -394,29 +391,6 @@ int red_channel_test_remote_cap(RedChannel *channel, uint32_t cap)
     return TRUE;
 }
 
-/* returns TRUE If all channels are finished migrating, FALSE otherwise */
-gboolean red_client_seamless_migration_done_for_channel(RedClient *client)
-{
-    gboolean ret = FALSE;
-
-    pthread_mutex_lock(&client->lock);
-    client->num_migrated_channels--;
-    /* we assume we always have at least one channel who has migration data transfer,
-     * otherwise, this flag will never be set back to FALSE*/
-    if (!client->num_migrated_channels) {
-        client->during_target_migrate = FALSE;
-        client->seamless_migrate = FALSE;
-        /* migration completion might have been triggered from a different thread
-         * than the main thread */
-        main_dispatcher_seamless_migrate_dst_complete(reds_get_main_dispatcher(client->reds),
-                                                      client);
-        ret = TRUE;
-    }
-    pthread_mutex_unlock(&client->lock);
-
-    return ret;
-}
-
 int red_channel_is_waiting_for_migrate_data(RedChannel *channel)
 {
     RedChannelClient *rcc;
@@ -575,14 +549,6 @@ void red_channel_remove_client(RedChannel *channel, RedChannelClient *rcc)
     // TODO: should we set rcc->channel to NULL???
 }
 
-void red_client_remove_channel(RedChannelClient *rcc)
-{
-    RedClient *client = red_channel_client_get_client(rcc);
-    pthread_mutex_lock(&client->lock);
-    client->channels = g_list_remove(client->channels, rcc);
-    pthread_mutex_unlock(&client->lock);
-}
-
 void red_channel_disconnect(RedChannel *channel)
 {
     g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_disconnect, NULL);
@@ -673,201 +639,6 @@ int red_channel_no_item_being_sent(RedChannel *channel)
 }
 
 /*
- * RedClient implementation - kept in red-channel.c because they are
- * pretty tied together.
- */
-
-RedClient *red_client_new(RedsState *reds, int migrated)
-{
-    RedClient *client;
-
-    client = spice_malloc0(sizeof(RedClient));
-    client->reds = reds;
-    pthread_mutex_init(&client->lock, NULL);
-    client->thread_id = pthread_self();
-    client->during_target_migrate = migrated;
-    client->refs = 1;
-
-    return client;
-}
-
-RedClient *red_client_ref(RedClient *client)
-{
-    spice_assert(client);
-    g_atomic_int_inc(&client->refs);
-    return client;
-}
-
-RedClient *red_client_unref(RedClient *client)
-{
-    if (g_atomic_int_dec_and_test(&client->refs)) {
-        spice_debug("release client=%p", client);
-        pthread_mutex_destroy(&client->lock);
-        free(client);
-        return NULL;
-    }
-    return client;
-}
-
-void red_client_set_migration_seamless(RedClient *client) // dest
-{
-    GListIter iter;
-    RedChannelClient *rcc;
-
-    spice_assert(client->during_target_migrate);
-    pthread_mutex_lock(&client->lock);
-    client->seamless_migrate = TRUE;
-    /* update channel clients that got connected before the migration
-     * type was set. red_client_add_channel will handle newer channel clients */
-    FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
-        if (red_channel_client_set_migration_seamless(rcc))
-            client->num_migrated_channels++;
-    }
-    pthread_mutex_unlock(&client->lock);
-}
-
-void red_client_migrate(RedClient *client)
-{
-    GListIter iter;
-    RedChannelClient *rcc;
-    RedChannel *channel;
-
-    spice_printerr("migrate client with #channels %d", g_list_length(client->channels));
-    if (!pthread_equal(pthread_self(), client->thread_id)) {
-        spice_warning("client->thread_id (0x%lx) != pthread_self (0x%lx)."
-                      "If one of the threads is != io-thread && != vcpu-thread,"
-                      " this might be a BUG",
-                      client->thread_id, pthread_self());
-    }
-    FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
-        channel = red_channel_client_get_channel(rcc);
-        if (red_channel_client_is_connected(rcc)) {
-            channel->priv->client_cbs.migrate(rcc);
-        }
-    }
-}
-
-void red_client_destroy(RedClient *client)
-{
-    GListIter iter;
-    RedChannelClient *rcc;
-
-    spice_printerr("destroy client %p with #channels=%d", client, g_list_length(client->channels));
-    if (!pthread_equal(pthread_self(), client->thread_id)) {
-        spice_warning("client->thread_id (0x%lx) != pthread_self (0x%lx)."
-                      "If one of the threads is != io-thread && != vcpu-thread,"
-                      " this might be a BUG",
-                      client->thread_id,
-                      pthread_self());
-    }
-    FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
-        RedChannel *channel;
-        // some channels may be in other threads, so disconnection
-        // is not synchronous.
-        channel = red_channel_client_get_channel(rcc);
-        red_channel_client_set_destroying(rcc);
-        // some channels may be in other threads. However we currently
-        // assume disconnect is synchronous (we changed the dispatcher
-        // to wait for disconnection)
-        // TODO: should we go back to async. For this we need to use
-        // ref count for channel clients.
-        channel->priv->client_cbs.disconnect(rcc);
-        spice_assert(red_channel_client_pipe_is_empty(rcc));
-        spice_assert(red_channel_client_no_item_being_sent(rcc));
-        red_channel_client_destroy(rcc);
-    }
-    red_client_unref(client);
-}
-
-/* client->lock should be locked */
-RedChannelClient *red_client_get_channel(RedClient *client, int type, int id)
-{
-    GListIter iter;
-    RedChannelClient *rcc;
-    RedChannelClient *ret = NULL;
-
-    FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
-        RedChannel *channel;
-        channel = red_channel_client_get_channel(rcc);
-        if (channel->priv->type == type && channel->priv->id == id) {
-            ret = rcc;
-            break;
-        }
-    }
-    return ret;
-}
-
-gboolean red_client_add_channel(RedClient *client, RedChannelClient *rcc, GError **error)
-{
-    uint32_t type, id;
-    RedChannel *channel;
-    gboolean result = TRUE;
-
-    spice_assert(rcc && client);
-    channel = red_channel_client_get_channel(rcc);
-
-    pthread_mutex_lock(&client->lock);
-
-    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
-    if (red_client_get_channel(client, type, id)) {
-        g_set_error(error,
-                    SPICE_SERVER_ERROR,
-                    SPICE_SERVER_ERROR_FAILED,
-                    "Client %p: duplicate channel type %d id %d",
-                    client, type, id);
-        result = FALSE;
-        goto cleanup;
-    }
-
-    client->channels = g_list_prepend(client->channels, rcc);
-    if (client->during_target_migrate && client->seamless_migrate) {
-        if (red_channel_client_set_migration_seamless(rcc))
-            client->num_migrated_channels++;
-    }
-
-cleanup:
-    pthread_mutex_unlock(&client->lock);
-    return result;
-}
-
-MainChannelClient *red_client_get_main(RedClient *client) {
-    return client->mcc;
-}
-
-void red_client_set_main(RedClient *client, MainChannelClient *mcc) {
-    client->mcc = mcc;
-}
-
-void red_client_semi_seamless_migrate_complete(RedClient *client)
-{
-    GListIter iter;
-    RedChannelClient *rcc;
-
-    pthread_mutex_lock(&client->lock);
-    if (!client->during_target_migrate || client->seamless_migrate) {
-        spice_error("unexpected");
-        pthread_mutex_unlock(&client->lock);
-        return;
-    }
-    client->during_target_migrate = FALSE;
-    FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
-        red_channel_client_semi_seamless_migration_complete(rcc);
-    }
-    pthread_mutex_unlock(&client->lock);
-    reds_on_client_semi_seamless_migrate_complete(client->reds, client);
-}
-
-/* should be called only from the main thread */
-int red_client_during_migrate_at_target(RedClient *client)
-{
-    int ret;
-    pthread_mutex_lock(&client->lock);
-    ret = client->during_target_migrate;
-    pthread_mutex_unlock(&client->lock);
-    return ret;
-}
-
-/*
  * Functions to push the same item to multiple pipes.
  */
 
@@ -1060,3 +831,14 @@ const RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel *sel
 {
     return &self->priv->local_caps;
 }
+
+void red_channel_migrate_client(RedChannel *channel, RedChannelClient *rcc)
+{
+    channel->priv->client_cbs.migrate(rcc);
+}
+
+void red_channel_disconnect_client(RedChannel *channel, RedChannelClient *rcc)
+{
+    channel->priv->client_cbs.disconnect(rcc);
+}
+
diff --git a/server/red-channel.h b/server/red-channel.h
index 61efcd9..0aa72c5 100644
--- a/server/red-channel.h
+++ b/server/red-channel.h
@@ -325,64 +325,6 @@ OutgoingHandlerInterface* red_channel_get_outgoing_handler(RedChannel *self);
 
 const RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel *self);
 
-struct RedClient {
-    RedsState *reds;
-    GList *channels;
-    MainChannelClient *mcc;
-    pthread_mutex_t lock; // different channels can be in different threads
-
-    pthread_t thread_id;
-
-    int disconnecting;
-    /* Note that while semi-seamless migration is conducted by the main thread, seamless migration
-     * involves all channels, and thus the related variables can be accessed from different
-     * threads */
-    int during_target_migrate; /* if seamless=TRUE, migration_target is turned off when all
-                                  the clients received their migration data. Otherwise (semi-seamless),
-                                  it is turned off, when red_client_semi_seamless_migrate_complete
-                                  is called */
-    int seamless_migrate;
-    int num_migrated_channels; /* for seamless - number of channels that wait for migrate data*/
-    int refs;
-};
-
-RedClient *red_client_new(RedsState *reds, int migrated);
-
-/*
- * disconnects all the client's channels (should be called from the client's thread)
- */
-void red_client_destroy(RedClient *client);
-
-RedClient *red_client_ref(RedClient *client);
-
-/*
- * releases the client resources when refs == 0.
- * We assume the red_client_destroy was called before
- * we reached refs==0
- */
-RedClient *red_client_unref(RedClient *client);
-
-/* client->lock should be locked */
-gboolean red_client_add_channel(RedClient *client, RedChannelClient *rcc, GError **error);
-void red_client_remove_channel(RedChannelClient *rcc);
-RedChannelClient *red_client_get_channel(RedClient *client, int type, int id);
-
-MainChannelClient *red_client_get_main(RedClient *client);
-// main should be set once before all the other channels are created
-void red_client_set_main(RedClient *client, MainChannelClient *mcc);
-
-/* called when the migration handshake results in seamless migration (dst side).
- * By default we assume semi-seamless */
-void red_client_set_migration_seamless(RedClient *client);
-void red_client_semi_seamless_migrate_complete(RedClient *client); /* dst side */
-/* TRUE if the migration is seamless and there are still channels that wait from migration data.
- * Or, during semi-seamless migration, and the main channel still waits for MIGRATE_END
- * from the client.
- * Note: Call it only from the main thread */
-int red_client_during_migrate_at_target(RedClient *client);
-
-void red_client_migrate(RedClient *client);
-gboolean red_client_seamless_migration_done_for_channel(RedClient *client);
 /*
  * blocking functions.
  *
@@ -394,6 +336,10 @@ gboolean red_client_seamless_migration_done_for_channel(RedClient *client);
 int red_channel_wait_all_sent(RedChannel *channel,
                               int64_t timeout);
 
+/* wrappers for client callbacks */
+void red_channel_migrate_client(RedChannel *channel, RedChannelClient *rcc);
+void red_channel_disconnect_client(RedChannel *channel, RedChannelClient *rcc);
+
 #define CHANNEL_BLOCKED_SLEEP_DURATION 10000 //micro
 
 G_END_DECLS
diff --git a/server/red-client.c b/server/red-client.c
new file mode 100644
index 0000000..9b7d76e
--- /dev/null
+++ b/server/red-client.c
@@ -0,0 +1,290 @@
+/*
+    Copyright (C) 2009-2016 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "red-channel.h"
+#include "red-client.h"
+#include "reds.h"
+
+#define FOREACH_CHANNEL_CLIENT(_client, _iter, _data) \
+    GLIST_FOREACH((_client ? (_client)->channels : NULL), _iter, RedChannelClient, _data)
+
+struct RedClient {
+    RedsState *reds;
+    GList *channels;
+    MainChannelClient *mcc;
+    pthread_mutex_t lock; // different channels can be in different threads
+
+    pthread_t thread_id;
+
+    int disconnecting;
+    /* Note that while semi-seamless migration is conducted by the main thread, seamless migration
+     * involves all channels, and thus the related variables can be accessed from different
+     * threads */
+    /* if seamless=TRUE, migration_target is turned off when all
+     * the clients received their migration data. Otherwise (semi-seamless),
+     * it is turned off, when red_client_semi_seamless_migrate_complete
+     * is called */
+    int during_target_migrate;
+    int seamless_migrate;
+    int num_migrated_channels; /* for seamless - number of channels that wait for migrate data*/
+    int refs;
+};
+
+RedClient *red_client_ref(RedClient *client)
+{
+    spice_assert(client);
+    g_atomic_int_inc(&client->refs);
+    return client;
+}
+
+RedClient *red_client_unref(RedClient *client)
+{
+    if (g_atomic_int_dec_and_test(&client->refs)) {
+        spice_debug("release client=%p", client);
+        pthread_mutex_destroy(&client->lock);
+        free(client);
+        return NULL;
+    }
+    return client;
+}
+
+RedClient *red_client_new(RedsState *reds, int migrated)
+{
+    RedClient *client;
+
+    client = spice_malloc0(sizeof(RedClient));
+    client->reds = reds;
+    pthread_mutex_init(&client->lock, NULL);
+    client->thread_id = pthread_self();
+    client->during_target_migrate = migrated;
+    client->refs = 1;
+
+    return client;
+}
+
+void red_client_set_migration_seamless(RedClient *client) // dest
+{
+    GListIter iter;
+    RedChannelClient *rcc;
+
+    spice_assert(client->during_target_migrate);
+    pthread_mutex_lock(&client->lock);
+    client->seamless_migrate = TRUE;
+    /* update channel clients that got connected before the migration
+     * type was set. red_client_add_channel will handle newer channel clients */
+    FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
+        if (red_channel_client_set_migration_seamless(rcc)) {
+            client->num_migrated_channels++;
+        }
+    }
+    pthread_mutex_unlock(&client->lock);
+}
+
+void red_client_migrate(RedClient *client)
+{
+    GListIter iter;
+    RedChannelClient *rcc;
+    RedChannel *channel;
+
+    spice_printerr("migrate client with #channels %d", g_list_length(client->channels));
+    if (!pthread_equal(pthread_self(), client->thread_id)) {
+        spice_warning("client->thread_id (0x%lx) != pthread_self (0x%lx)."
+                      "If one of the threads is != io-thread && != vcpu-thread,"
+                      " this might be a BUG",
+                      client->thread_id, pthread_self());
+    }
+    FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
+        channel = red_channel_client_get_channel(rcc);
+        if (red_channel_client_is_connected(rcc)) {
+            red_channel_migrate_client(channel, rcc);
+        }
+    }
+}
+
+void red_client_destroy(RedClient *client)
+{
+    GListIter iter;
+    RedChannelClient *rcc;
+
+    spice_printerr("destroy client %p with #channels=%d", client, g_list_length(client->channels));
+    if (!pthread_equal(pthread_self(), client->thread_id)) {
+        spice_warning("client->thread_id (0x%lx) != pthread_self (0x%lx)."
+                      "If one of the threads is != io-thread && != vcpu-thread,"
+                      " this might be a BUG",
+                      client->thread_id,
+                      pthread_self());
+    }
+    FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
+        RedChannel *channel;
+        // some channels may be in other threads, so disconnection
+        // is not synchronous.
+        channel = red_channel_client_get_channel(rcc);
+        red_channel_client_set_destroying(rcc);
+        // some channels may be in other threads. However we currently
+        // assume disconnect is synchronous (we changed the dispatcher
+        // to wait for disconnection)
+        // TODO: should we go back to async. For this we need to use
+        // ref count for channel clients.
+        red_channel_disconnect_client(channel, rcc);
+        spice_assert(red_channel_client_pipe_is_empty(rcc));
+        spice_assert(red_channel_client_no_item_being_sent(rcc));
+        red_channel_client_destroy(rcc);
+    }
+    red_client_unref(client);
+}
+
+/* client->lock should be locked */
+RedChannelClient *red_client_get_channel(RedClient *client, int type, int id)
+{
+    GListIter iter;
+    RedChannelClient *rcc;
+    RedChannelClient *ret = NULL;
+
+    FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
+        int channel_type, channel_id;
+        RedChannel *channel;
+
+        channel = red_channel_client_get_channel(rcc);
+        g_object_get(channel, "channel-type", &channel_type, "id", &channel_id, NULL);
+        if (channel_type == type && channel_id == id) {
+            ret = rcc;
+            break;
+        }
+    }
+    return ret;
+}
+
+gboolean red_client_add_channel(RedClient *client, RedChannelClient *rcc, GError **error)
+{
+    uint32_t type, id;
+    RedChannel *channel;
+    gboolean result = TRUE;
+
+    spice_assert(rcc && client);
+    channel = red_channel_client_get_channel(rcc);
+
+    pthread_mutex_lock(&client->lock);
+
+    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
+    if (red_client_get_channel(client, type, id)) {
+        g_set_error(error,
+                    SPICE_SERVER_ERROR,
+                    SPICE_SERVER_ERROR_FAILED,
+                    "Client %p: duplicate channel type %d id %d",
+                    client, type, id);
+        result = FALSE;
+        goto cleanup;
+    }
+
+    client->channels = g_list_prepend(client->channels, rcc);
+    if (client->during_target_migrate && client->seamless_migrate) {
+        if (red_channel_client_set_migration_seamless(rcc)) {
+            client->num_migrated_channels++;
+        }
+    }
+
+cleanup:
+    pthread_mutex_unlock(&client->lock);
+    return result;
+}
+
+MainChannelClient *red_client_get_main(RedClient *client) {
+    return client->mcc;
+}
+
+void red_client_set_main(RedClient *client, MainChannelClient *mcc) {
+    client->mcc = mcc;
+}
+
+void red_client_semi_seamless_migrate_complete(RedClient *client)
+{
+    GListIter iter;
+    RedChannelClient *rcc;
+
+    pthread_mutex_lock(&client->lock);
+    if (!client->during_target_migrate || client->seamless_migrate) {
+        spice_error("unexpected");
+        pthread_mutex_unlock(&client->lock);
+        return;
+    }
+    client->during_target_migrate = FALSE;
+    FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
+        red_channel_client_semi_seamless_migration_complete(rcc);
+    }
+    pthread_mutex_unlock(&client->lock);
+    reds_on_client_semi_seamless_migrate_complete(client->reds, client);
+}
+
+/* should be called only from the main thread */
+int red_client_during_migrate_at_target(RedClient *client)
+{
+    int ret;
+    pthread_mutex_lock(&client->lock);
+    ret = client->during_target_migrate;
+    pthread_mutex_unlock(&client->lock);
+    return ret;
+}
+
+void red_client_remove_channel(RedChannelClient *rcc)
+{
+    RedClient *client = red_channel_client_get_client(rcc);
+    pthread_mutex_lock(&client->lock);
+    client->channels = g_list_remove(client->channels, rcc);
+    pthread_mutex_unlock(&client->lock);
+}
+
+/* returns TRUE If all channels are finished migrating, FALSE otherwise */
+gboolean red_client_seamless_migration_done_for_channel(RedClient *client)
+{
+    gboolean ret = FALSE;
+
+    pthread_mutex_lock(&client->lock);
+    client->num_migrated_channels--;
+    /* we assume we always have at least one channel who has migration data transfer,
+     * otherwise, this flag will never be set back to FALSE*/
+    if (!client->num_migrated_channels) {
+        client->during_target_migrate = FALSE;
+        client->seamless_migrate = FALSE;
+        /* migration completion might have been triggered from a different thread
+         * than the main thread */
+        main_dispatcher_seamless_migrate_dst_complete(reds_get_main_dispatcher(client->reds),
+                                                      client);
+        ret = TRUE;
+    }
+    pthread_mutex_unlock(&client->lock);
+
+    return ret;
+}
+
+gboolean red_client_is_disconnecting(RedClient *client)
+{
+    return client->disconnecting;
+}
+
+void red_client_set_disconnecting(RedClient *client)
+{
+    client->disconnecting = TRUE;
+}
+
+RedsState *red_client_get_server(RedClient *client)
+{
+    return client->reds;
+}
diff --git a/server/red-client.h b/server/red-client.h
new file mode 100644
index 0000000..5b87e62
--- /dev/null
+++ b/server/red-client.h
@@ -0,0 +1,65 @@
+/*
+    Copyright (C) 2009-2015 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef _H_RED_CLIENT
+#define _H_RED_CLIENT
+
+#include "main-channel-client.h"
+
+RedClient *red_client_new(RedsState *reds, int migrated);
+
+/*
+ * disconnects all the client's channels (should be called from the client's thread)
+ */
+void red_client_destroy(RedClient *client);
+
+RedClient *red_client_ref(RedClient *client);
+
+/*
+ * releases the client resources when refs == 0.
+ * We assume the red_client_destroy was called before
+ * we reached refs==0
+ */
+RedClient *red_client_unref(RedClient *client);
+
+gboolean red_client_add_channel(RedClient *client, RedChannelClient *rcc, GError **error);
+void red_client_remove_channel(RedChannelClient *rcc);
+RedChannelClient *red_client_get_channel(RedClient *client, int type, int id);
+
+MainChannelClient *red_client_get_main(RedClient *client);
+// main should be set once before all the other channels are created
+void red_client_set_main(RedClient *client, MainChannelClient *mcc);
+
+/* called when the migration handshake results in seamless migration (dst side).
+ * By default we assume semi-seamless */
+void red_client_set_migration_seamless(RedClient *client);
+void red_client_semi_seamless_migrate_complete(RedClient *client); /* dst side */
+gboolean red_client_seamless_migration_done_for_channel(RedClient *client);
+/* TRUE if the migration is seamless and there are still channels that wait from migration data.
+ * Or, during semi-seamless migration, and the main channel still waits for MIGRATE_END
+ * from the client.
+ * Note: Call it only from the main thread */
+int red_client_during_migrate_at_target(RedClient *client);
+
+void red_client_migrate(RedClient *client);
+
+gboolean red_client_is_disconnecting(RedClient *client);
+void red_client_set_disconnecting(RedClient *client);
+RedsState* red_client_get_server(RedClient *client);
+
+#endif /* _H_RED_CLIENT */
diff --git a/server/reds.c b/server/reds.c
index bfa479e..b3e2535 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -74,6 +74,7 @@
 #include "video-encoder.h"
 #include "red-channel-client.h"
 #include "main-channel-client.h"
+#include "red-client.h"
 
 static void reds_client_monitors_config(RedsState *reds, VDAgentMonitorsConfig *monitors_config);
 static gboolean reds_use_client_monitors_config(RedsState *reds);
@@ -565,7 +566,7 @@ void reds_client_disconnect(RedsState *reds, RedClient *client)
         exit(0);
     }
 
-    if (!client || client->disconnecting) {
+    if (!client || red_client_is_disconnecting(client)) {
         spice_debug("client %p already during disconnection", client);
         return;
     }
@@ -575,7 +576,7 @@ void reds_client_disconnect(RedsState *reds, RedClient *client)
      * main_channel_client_on_disconnect->
      *  reds_client_disconnect->red_client_destroy->main_channel...
      */
-    client->disconnecting = TRUE;
+    red_client_set_disconnecting(client);
 
     // TODO: we need to handle agent properly for all clients!!!! (e.g., cut and paste, how?)
     // We shouldn't initialize the agent when there are still clients connected
diff --git a/server/sound.c b/server/sound.c
index c222c8d..be9ad5f 100644
--- a/server/sound.c
+++ b/server/sound.c
@@ -39,6 +39,7 @@
 #include "red-channel-client.h"
 /* FIXME: for now, allow sound channel access to private RedChannelClient data */
 #include "red-channel-client-private.h"
+#include "red-client.h"
 #include "sound.h"
 #include <common/snd_codec.h>
 #include "demarshallers.h"
diff --git a/server/stream.c b/server/stream.c
index 4e70222..e5cad96 100644
--- a/server/stream.c
+++ b/server/stream.c
@@ -21,6 +21,7 @@
 #include "stream.h"
 #include "display-channel-private.h"
 #include "main-channel-client.h"
+#include "red-client.h"
 
 #define FPS_TEST_INTERVAL 1
 #define FOREACH_STREAMS(display, item)                  \
commit 1f4705e130575bf7b6b6cede67ee59281a4ea8e8
Author: Jonathon Jongsma <jjongsma at redhat.com>
Date:   Wed Oct 26 14:47:33 2016 -0500

    Re-arrange channel client creation to avoid exposing client lock
    
    Instead of requiring the channel client to lock the client's lock,
    re-arrange things so that all of the locking can be internal to
    RedClient methods. So instead of having a pre-create validate function,
    we simply move the check to red_client_add_channel() and return an error
    if a channel already exists. This encapsulates the client implementation
    better and also reduces code duplication in RedChannelClient and
    DummyChannelClient.
    
    Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/server/dummy-channel-client.c b/server/dummy-channel-client.c
index a242d51..c602412 100644
--- a/server/dummy-channel-client.c
+++ b/server/dummy-channel-client.c
@@ -35,46 +35,20 @@ struct DummyChannelClientPrivate
     gboolean connected;
 };
 
-static int dummy_channel_client_pre_create_validate(RedChannel *channel, RedClient  *client)
-{
-    uint32_t type, id;
-    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
-    if (red_client_get_channel(client, type, id)) {
-        spice_printerr("Error client %p: duplicate channel type %d id %d",
-                       client, type, id);
-        return FALSE;
-    }
-    return TRUE;
-}
-
 static gboolean dummy_channel_client_initable_init(GInitable *initable,
                                                    GCancellable *cancellable,
                                                    GError **error)
 {
     GError *local_error = NULL;
-    DummyChannelClient *self = DUMMY_CHANNEL_CLIENT(initable);
-    RedChannelClient *rcc = RED_CHANNEL_CLIENT(self);
+    RedChannelClient *rcc = RED_CHANNEL_CLIENT(initable);
     RedClient *client = red_channel_client_get_client(rcc);
     RedChannel *channel = red_channel_client_get_channel(rcc);
-    uint32_t type, id;
-
-    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
-    pthread_mutex_lock(&client->lock);
-    if (!dummy_channel_client_pre_create_validate(channel,
-                                                  client)) {
-        g_set_error(&local_error,
-                    SPICE_SERVER_ERROR,
-                    SPICE_SERVER_ERROR_FAILED,
-                    "Client %p: duplicate channel type %d id %d",
-                    client, type, id);
-        goto cleanup;
-    }
 
     red_channel_add_client(channel, rcc);
-    red_client_add_channel(client, rcc);
+    if (!red_client_add_channel(client, rcc, &local_error)) {
+        red_channel_remove_client(channel, rcc);
+    }
 
-cleanup:
-    pthread_mutex_unlock(&client->lock);
     if (local_error) {
         g_warning("Failed to create channel client: %s", local_error->message);
         g_propagate_error(error, local_error);
diff --git a/server/red-channel-client.c b/server/red-channel-client.c
index a28f5e6..bfa44b4 100644
--- a/server/red-channel-client.c
+++ b/server/red-channel-client.c
@@ -862,18 +862,6 @@ static const SpiceDataHeaderOpaque mini_header_wrapper = {NULL, sizeof(SpiceMini
                                                           mini_header_get_msg_type,
                                                           mini_header_get_msg_size};
 
-static int red_channel_client_pre_create_validate(RedChannel *channel, RedClient  *client)
-{
-    uint32_t type, id;
-    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
-    if (red_client_get_channel(client, type, id)) {
-        spice_printerr("Error client %p: duplicate channel type %d id %d",
-                       client, type, id);
-        return FALSE;
-    }
-    return TRUE;
-}
-
 static gboolean red_channel_client_initable_init(GInitable *initable,
                                                  GCancellable *cancellable,
                                                  GError **error)
@@ -881,20 +869,6 @@ static gboolean red_channel_client_initable_init(GInitable *initable,
     GError *local_error = NULL;
     SpiceCoreInterfaceInternal *core;
     RedChannelClient *self = RED_CHANNEL_CLIENT(initable);
-    pthread_mutex_lock(&self->priv->client->lock);
-    if (!red_channel_client_pre_create_validate(self->priv->channel, self->priv->client)) {
-        uint32_t id, type;
-        g_object_get(self->priv->channel,
-                     "channel-type", &type,
-                     "id", &id,
-                     NULL);
-        g_set_error(&local_error,
-                    SPICE_SERVER_ERROR,
-                    SPICE_SERVER_ERROR_FAILED,
-                    "Client %p: duplicate channel type %d id %d",
-                    self->priv->client, type, id);
-        goto cleanup;
-    }
 
     if (!red_channel_config_socket(self->priv->channel, self)) {
         g_set_error_literal(&local_error,
@@ -917,7 +891,7 @@ static gboolean red_channel_client_initable_init(GInitable *initable,
         self->priv->latency_monitor.timer =
             core->timer_add(core, red_channel_client_ping_timer, self);
 
-        if (!self->priv->client->during_target_migrate) {
+        if (!red_client_during_migrate_at_target(self->priv->client)) {
             red_channel_client_start_ping_timer(self,
                                                 PING_TEST_IDLE_NET_TIMEOUT_MS);
         }
@@ -925,10 +899,11 @@ static gboolean red_channel_client_initable_init(GInitable *initable,
     }
 
     red_channel_add_client(self->priv->channel, self);
-    red_client_add_channel(self->priv->client, self);
+    if (!red_client_add_channel(self->priv->client, self, &local_error)) {
+        red_channel_remove_client(self->priv->channel, self);
+    }
 
 cleanup:
-    pthread_mutex_unlock(&self->priv->client->lock);
     if (local_error) {
         g_warning("Failed to create channel client: %s", local_error->message);
         g_propagate_error(error, local_error);
diff --git a/server/red-channel.c b/server/red-channel.c
index 1b6ab32..f0f5160 100644
--- a/server/red-channel.c
+++ b/server/red-channel.c
@@ -797,15 +797,37 @@ RedChannelClient *red_client_get_channel(RedClient *client, int type, int id)
     return ret;
 }
 
-/* client->lock should be locked */
-void red_client_add_channel(RedClient *client, RedChannelClient *rcc)
+gboolean red_client_add_channel(RedClient *client, RedChannelClient *rcc, GError **error)
 {
+    uint32_t type, id;
+    RedChannel *channel;
+    gboolean result = TRUE;
+
     spice_assert(rcc && client);
+    channel = red_channel_client_get_channel(rcc);
+
+    pthread_mutex_lock(&client->lock);
+
+    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
+    if (red_client_get_channel(client, type, id)) {
+        g_set_error(error,
+                    SPICE_SERVER_ERROR,
+                    SPICE_SERVER_ERROR_FAILED,
+                    "Client %p: duplicate channel type %d id %d",
+                    client, type, id);
+        result = FALSE;
+        goto cleanup;
+    }
+
     client->channels = g_list_prepend(client->channels, rcc);
     if (client->during_target_migrate && client->seamless_migrate) {
         if (red_channel_client_set_migration_seamless(rcc))
             client->num_migrated_channels++;
     }
+
+cleanup:
+    pthread_mutex_unlock(&client->lock);
+    return result;
 }
 
 MainChannelClient *red_client_get_main(RedClient *client) {
diff --git a/server/red-channel.h b/server/red-channel.h
index cb0cc9b..61efcd9 100644
--- a/server/red-channel.h
+++ b/server/red-channel.h
@@ -363,7 +363,7 @@ RedClient *red_client_ref(RedClient *client);
 RedClient *red_client_unref(RedClient *client);
 
 /* client->lock should be locked */
-void red_client_add_channel(RedClient *client, RedChannelClient *rcc);
+gboolean red_client_add_channel(RedClient *client, RedChannelClient *rcc, GError **error);
 void red_client_remove_channel(RedChannelClient *rcc);
 RedChannelClient *red_client_get_channel(RedClient *client, int type, int id);
 


More information about the Spice-commits mailing list