summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathon Jongsma <jjongsma@redhat.com>2015-05-28 13:19:37 -0500
committerJonathon Jongsma <jjongsma@redhat.com>2015-05-29 14:10:05 -0500
commite7bb7a20f37bce4ef71a400b30d6775450008043 (patch)
tree85f974c4105e76cb12da3ea9bea1d466ce6e7742
parent30d4cc06fecb0f992d5f85c020124073bdb95b81 (diff)
Convert RedClient to GObjectreplay-rebase
Move to a separate file, and add a few public methods to avoid accessing internal data.
-rw-r--r--server/Makefile.am2
-rw-r--r--server/char-device.c7
-rw-r--r--server/common-worker-channel-client.h2
-rw-r--r--server/common-worker-channel.c1
-rw-r--r--server/dummy-channel-client.c1
-rw-r--r--server/inputs-channel.c1
-rw-r--r--server/main-channel-client.c1
-rw-r--r--server/main-channel.c1
-rw-r--r--server/main-dispatcher.c16
-rw-r--r--server/red-channel-client.c1
-rw-r--r--server/red-channel-client.h2
-rw-r--r--server/red-channel.c247
-rw-r--r--server/red-channel.h6
-rw-r--r--server/red-client.c363
-rw-r--r--server/red-client.h88
-rw-r--r--server/reds-private.h3
-rw-r--r--server/reds.c58
17 files changed, 520 insertions, 280 deletions
diff --git a/server/Makefile.am b/server/Makefile.am
index e2ce41cf..4e01dbc1 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -101,6 +101,8 @@ libspice_server_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 d7afd4ca..adab0ea4 100644
--- a/server/char-device.c
+++ b/server/char-device.c
@@ -22,7 +22,7 @@
#include <config.h>
#include "char-device.h"
-#include "red-channel.h"
+#include "red-client.h"
#include "reds.h"
#define CHAR_DEVICE_WRITE_TO_TIMEOUT 100
@@ -726,8 +726,11 @@ SpiceCharDeviceClientState *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;
+
+ g_object_get(client, "spice-server", &reds, NULL);
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-worker-channel-client.h b/server/common-worker-channel-client.h
index 8fe28952..7bac03c4 100644
--- a/server/common-worker-channel-client.h
+++ b/server/common-worker-channel-client.h
@@ -50,7 +50,7 @@ struct _CommonWorkerChannelClientClass
GType common_worker_channel_client_get_type(void) G_GNUC_CONST;
typedef struct RedWorker RedWorker;
-typedef struct RedClient RedClient;
+typedef struct _RedClient RedClient;
typedef struct RedsStream RedsStream;
void common_worker_channel_client_set_low_bandwidth(CommonWorkerChannelClient *self,
diff --git a/server/common-worker-channel.c b/server/common-worker-channel.c
index df763008..8598ccb9 100644
--- a/server/common-worker-channel.c
+++ b/server/common-worker-channel.c
@@ -8,6 +8,7 @@
#include "common-worker-channel.h"
#include "common-worker-channel-client.h"
#include "red-worker.h"
+#include "red-client.h"
#define CHANNEL_RECEIVE_BUF_SIZE 1024
diff --git a/server/dummy-channel-client.c b/server/dummy-channel-client.c
index fa04bf84..2007c7bb 100644
--- a/server/dummy-channel-client.c
+++ b/server/dummy-channel-client.c
@@ -17,6 +17,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 034395c7..b231f69d 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 e204881e..d211547b 100644
--- a/server/main-channel-client.c
+++ b/server/main-channel-client.c
@@ -20,6 +20,7 @@
#include "main-channel-client.h"
#include "main-channel.h"
#include "red-channel-client-private.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 c2736cb8..241c59e3 100644
--- a/server/main-channel.c
+++ b/server/main-channel.c
@@ -44,6 +44,7 @@
#include "main-channel.h"
#include "main-channel-client.h"
#include "red-channel.h"
+#include "red-client.h"
#include "common.h"
#include "reds.h"
#include "migration-protocol.h"
diff --git a/server/main-dispatcher.c b/server/main-dispatcher.c
index 95a345da..d8ede461 100644
--- a/server/main-dispatcher.c
+++ b/server/main-dispatcher.c
@@ -23,7 +23,7 @@
#include "common.h"
#include "dispatcher.h"
#include "main-dispatcher.h"
-#include "red-channel.h"
+#include "red-client.h"
#include "reds.h"
/*
@@ -209,7 +209,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,
@@ -219,7 +219,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,
@@ -231,7 +231,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,
@@ -244,7 +244,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);
}
@@ -258,7 +258,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);
@@ -268,9 +268,9 @@ 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);
+ msg.client = g_object_ref(client);
dispatcher_send_message(DISPATCHER(self), MAIN_DISPATCHER_CLIENT_DISCONNECT,
&msg);
} else {
diff --git a/server/red-channel-client.c b/server/red-channel-client.c
index 8a1eb320..e32dc6dc 100644
--- a/server/red-channel-client.c
+++ b/server/red-channel-client.c
@@ -35,6 +35,7 @@
#include "common/generated_server_marshallers.h"
#include "red-channel-client-private.h"
#include "red-channel.h"
+#include "red-client.h"
static void red_channel_client_initable_interface_init(GInitableIface *iface);
diff --git a/server/red-channel-client.h b/server/red-channel-client.h
index 639c50c5..ca9245df 100644
--- a/server/red-channel-client.h
+++ b/server/red-channel-client.h
@@ -46,7 +46,7 @@ G_BEGIN_DECLS
#define RED_IS_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RED_TYPE_CHANNEL_CLIENT))
#define RED_CHANNEL_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_CHANNEL_CLIENT, RedChannelClientClass))
-typedef struct RedClient RedClient;
+typedef struct _RedClient RedClient;
typedef struct IncomingHandler IncomingHandler;
typedef struct _RedChannel RedChannel;
diff --git a/server/red-channel.c b/server/red-channel.c
index f621b1a8..b92c2bf3 100644
--- a/server/red-channel.c
+++ b/server/red-channel.c
@@ -395,30 +395,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,
- RedChannelClient *rcc)
-{
- 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_waits_for_migrate_data(RedChannel *channel)
{
RedChannelClient *rcc;
@@ -581,14 +557,6 @@ void red_channel_remove_client(RedChannel *channel, RedChannelClient *rcc)
// TODO: should we set rcc->channel to NULL???
}
-void red_client_remove_channel(RedClient *client, RedChannelClient *rcc)
-{
- g_return_if_fail(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);
@@ -682,210 +650,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);
- client->refs++;
- return client;
-}
-
-RedClient *red_client_unref(RedClient *client)
-{
- if (!--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
-{
- GList *link;
- 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 */
- for (link = client->channels; link != NULL; link = link->next) {
- if (red_channel_client_set_migration_seamless(link->data))
- client->num_migrated_channels++;
- }
- pthread_mutex_unlock(&client->lock);
-}
-
-void red_client_migrate(RedClient *client)
-{
- GList *link, *next;
- 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());
- }
- link = client->channels;
- while (link) {
- next = link->next;
- rcc = link->data;
- channel = red_channel_client_get_channel(rcc);
- if (red_channel_client_is_connected(rcc)) {
- channel->priv->client_cbs.migrate(rcc);
- }
- link = next;
- }
-}
-
-void red_client_destroy(RedClient *client)
-{
- GList *link, *next;
- 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());
- }
- link = client->channels;
- while (link) {
- RedChannel *channel;
- next = link->next;
- // some channels may be in other threads, so disconnection
- // is not synchronous.
- rcc = link->data;
- 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);
- link = next;
- }
- red_client_unref(client);
-}
-
-/* client->lock should be locked */
-RedChannelClient *red_client_get_channel(RedClient *client, int type, int id)
-{
- GList *link;
- RedChannelClient *rcc;
- RedChannelClient *ret = NULL;
-
- for (link = client->channels; link != NULL; link = link->next) {
- RedChannel *channel;
- rcc = link->data;
- 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_append(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)
-{
- GList *link, *next;
-
- 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;
- link = client->channels;
- while (link) {
- next = link->next;
- red_channel_client_semi_seamless_migration_complete(link->data);
- link = next;
- }
- 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.
*/
@@ -1138,3 +902,14 @@ RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel *self)
{
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 3bce3fb2..0e05070b 100644
--- a/server/red-channel.h
+++ b/server/red-channel.h
@@ -41,7 +41,7 @@
G_BEGIN_DECLS
typedef struct RedsStream RedsStream;
-typedef struct RedClient RedClient;
+typedef struct _RedClient RedClient;
typedef uint8_t *(*channel_alloc_msg_recv_buf_proc)(RedChannelClient *channel,
uint16_t type, uint32_t size);
@@ -352,6 +352,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 00000000..7ad989a6
--- /dev/null
+++ b/server/red-client.c
@@ -0,0 +1,363 @@
+/* red-client.c */
+
+#include "red-channel.h"
+#include "red-client.h"
+#include "reds.h"
+
+G_DEFINE_TYPE(RedClient, red_client, G_TYPE_OBJECT)
+
+#define CLIENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), RED_TYPE_CLIENT, RedClientPrivate))
+
+struct _RedClientPrivate
+{
+ RedsState *reds;
+ RingItem link;
+ GList *channels;
+ int channels_num;
+ 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 varaibles 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;
+};
+
+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->priv->reds);
+ break;
+ case PROP_MIGRATED:
+ g_value_set_boolean(value, self->priv->during_target_migrate);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+red_client_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ RedClient *self = RED_CLIENT(object);
+
+ switch (property_id)
+ {
+ case PROP_SPICE_SERVER:
+ self->priv->reds = g_value_get_pointer(value);
+ break;
+ case PROP_MIGRATED:
+ self->priv->during_target_migrate = g_value_get_boolean(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+red_client_dispose (GObject *object)
+{
+ G_OBJECT_CLASS (red_client_parent_class)->dispose (object);
+}
+
+static void
+red_client_finalize (GObject *object)
+{
+ RedClient *self = RED_CLIENT(object);
+
+ spice_debug("release client=%p", self);
+ pthread_mutex_destroy(&self->priv->lock);
+
+ G_OBJECT_CLASS (red_client_parent_class)->finalize (object);
+}
+
+static void
+red_client_class_init (RedClientClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (RedClientPrivate));
+
+ object_class->get_property = red_client_get_property;
+ object_class->set_property = red_client_set_property;
+ object_class->dispose = red_client_dispose;
+ 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));
+}
+
+static void
+red_client_init (RedClient *self)
+{
+ self->priv = CLIENT_PRIVATE (self);
+ pthread_mutex_init(&self->priv->lock, NULL);
+ self->priv->thread_id = pthread_self();
+}
+
+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
+{
+ GList *link;
+ spice_assert(client->priv->during_target_migrate);
+ pthread_mutex_lock(&client->priv->lock);
+ client->priv->seamless_migrate = TRUE;
+ /* update channel clients that got connected before the migration
+ * type was set. red_client_add_channel will handle newer channel clients */
+ for (link = client->priv->channels; link != NULL; link = link->next) {
+ if (red_channel_client_set_migration_seamless(link->data))
+ client->priv->num_migrated_channels++;
+ }
+ pthread_mutex_unlock(&client->priv->lock);
+}
+
+void red_client_migrate(RedClient *client)
+{
+ GList *link, *next;
+ RedChannelClient *rcc;
+ RedChannel *channel;
+
+ spice_printerr("migrate client with #channels %d", g_list_length(client->priv->channels));
+ if (!pthread_equal(pthread_self(), client->priv->thread_id)) {
+ spice_warning("client->priv->thread_id (0x%lx) != pthread_self (0x%lx)."
+ "If one of the threads is != io-thread && != vcpu-thread,"
+ " this might be a BUG",
+ client->priv->thread_id, pthread_self());
+ }
+ link = client->priv->channels;
+ while (link) {
+ next = link->next;
+ rcc = link->data;
+ channel = red_channel_client_get_channel(rcc);
+ if (red_channel_client_is_connected(rcc)) {
+ red_channel_migrate_client(channel, rcc);
+ }
+ link = next;
+ }
+}
+
+void red_client_destroy(RedClient *client)
+{
+ GList *link, *next;
+ RedChannelClient *rcc;
+
+ spice_printerr("destroy client %p with #channels=%d", client, g_list_length(client->priv->channels));
+ if (!pthread_equal(pthread_self(), client->priv->thread_id)) {
+ spice_warning("client->priv->thread_id (0x%lx) != pthread_self (0x%lx)."
+ "If one of the threads is != io-thread && != vcpu-thread,"
+ " this might be a BUG",
+ client->priv->thread_id,
+ pthread_self());
+ }
+ link = client->priv->channels;
+ while (link) {
+ RedChannel *channel;
+ next = link->next;
+ // some channels may be in other threads, so disconnection
+ // is not synchronous.
+ rcc = link->data;
+ 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);
+ link = next;
+ }
+ g_object_unref(client);
+}
+
+/* client->priv->lock should be locked */
+RedChannelClient *red_client_get_channel(RedClient *client, int type, int id)
+{
+ GList *link;
+ RedChannelClient *rcc;
+ RedChannelClient *ret = NULL;
+
+ for (link = client->priv->channels; link != NULL; link = link->next) {
+ int channel_type, channel_id;
+ RedChannel *channel;
+
+ rcc = link->data;
+ 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->priv->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->priv->channels = g_list_append(client->priv->channels, rcc);
+ if (client->priv->during_target_migrate && client->priv->seamless_migrate) {
+ if (red_channel_client_set_migration_seamless(rcc))
+ client->priv->num_migrated_channels++;
+ }
+
+cleanup:
+ pthread_mutex_unlock(&client->priv->lock);
+ return result;
+}
+
+MainChannelClient *red_client_get_main(RedClient *client) {
+ return client->priv->mcc;
+}
+
+void red_client_set_main(RedClient *client, MainChannelClient *mcc) {
+ client->priv->mcc = mcc;
+}
+
+void red_client_semi_seamless_migrate_complete(RedClient *client)
+{
+ GList *link, *next;
+
+ pthread_mutex_lock(&client->priv->lock);
+ if (!client->priv->during_target_migrate || client->priv->seamless_migrate) {
+ spice_error("unexpected");
+ pthread_mutex_unlock(&client->priv->lock);
+ return;
+ }
+ client->priv->during_target_migrate = FALSE;
+ link = client->priv->channels;
+ while (link) {
+ next = link->next;
+ red_channel_client_semi_seamless_migration_complete(link->data);
+ link = next;
+ }
+ pthread_mutex_unlock(&client->priv->lock);
+ reds_on_client_semi_seamless_migrate_complete(client->priv->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->priv->lock);
+ ret = client->priv->during_target_migrate;
+ pthread_mutex_unlock(&client->priv->lock);
+ return ret;
+}
+
+void red_client_remove_channel(RedClient *client, RedChannelClient *rcc)
+{
+ g_return_if_fail(client == red_channel_client_get_client(rcc));
+ pthread_mutex_lock(&client->priv->lock);
+ client->priv->channels = g_list_remove(client->priv->channels, rcc);
+ pthread_mutex_unlock(&client->priv->lock);
+}
+
+/* returns TRUE If all channels are finished migrating, FALSE otherwise */
+gboolean red_client_seamless_migration_done_for_channel(RedClient *client,
+ RedChannelClient *rcc)
+{
+ gboolean ret = FALSE;
+
+ pthread_mutex_lock(&client->priv->lock);
+ client->priv->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->priv->num_migrated_channels) {
+ client->priv->during_target_migrate = FALSE;
+ client->priv->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->priv->reds),
+ client);
+ ret = TRUE;
+ }
+ pthread_mutex_unlock(&client->priv->lock);
+
+ return ret;
+}
+
+void red_client_lock(RedClient* self)
+{
+ pthread_mutex_lock(&self->priv->lock);
+}
+
+void red_client_unlock(RedClient* self)
+{
+ pthread_mutex_unlock(&self->priv->lock);
+}
+
+gboolean red_client_is_disconnecting(RedClient *client)
+{
+ return client->priv->disconnecting;
+}
+
+void red_client_set_disconnecting(RedClient *client)
+{
+ client->priv->disconnecting = TRUE;
+}
diff --git a/server/red-client.h b/server/red-client.h
new file mode 100644
index 00000000..08288602
--- /dev/null
+++ b/server/red-client.h
@@ -0,0 +1,88 @@
+/*
+ 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 <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;
+typedef struct _RedClientPrivate RedClientPrivate;
+
+struct _RedClient
+{
+ GObject parent;
+
+ RedClientPrivate *priv;
+};
+
+struct _RedClientClass
+{
+ GObjectClass parent_class;
+};
+
+GType red_client_get_type (void) G_GNUC_CONST;
+
+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);
+
+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,
+ RedChannelClient *rcc);
+/* 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_add_channel(RedClient *client, RedChannelClient *rcc, GError **error);
+RedChannelClient *red_client_get_channel(RedClient *client, int type, int id);
+void red_client_remove_channel(RedClient *client, RedChannelClient *rcc);
+
+gboolean red_client_is_disconnecting(RedClient *client);
+void red_client_set_disconnecting(RedClient *client);
+
+G_END_DECLS
+
+#endif /* _H_RED_CLIENT */
diff --git a/server/reds-private.h b/server/reds-private.h
index 3c23bec0..1847346c 100644
--- a/server/reds-private.h
+++ b/server/reds-private.h
@@ -101,8 +101,7 @@ struct RedsState {
SpiceWatch *secure_listen_watch;
VDIPortState *agent_state;
int pending_mouse_event;
- Ring clients;
- int num_clients;
+ GList *clients;
MainChannel *main_channel;
InputsChannel *inputs_channel;
diff --git a/server/reds.c b/server/reds.c
index a17931a2..77fb44f4 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -71,6 +71,7 @@
#include "smartcard.h"
#endif
#include "reds-stream.h"
+#include "red-client.h"
#include "reds-private.h"
@@ -527,7 +528,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;
}
@@ -537,7 +538,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
@@ -559,13 +560,12 @@ void reds_client_disconnect(RedsState *reds, RedClient *client)
}
}
- ring_remove(&client->link);
- reds->num_clients--;
+ reds->clients = g_list_remove(reds->clients, client);
red_client_destroy(client);
// TODO: we need to handle agent properly for all clients!!!! (e.g., cut and paste, how? Maybe throw away messages
// if we are in the middle of one from another client)
- if (reds->num_clients == 0) {
+ if (g_list_length(reds->clients) == 0) {
/* Let the agent know the client is disconnected */
if (reds->agent_state->priv->agent_attached) {
SpiceCharDeviceWriteBuffer *char_dev_buf;
@@ -608,11 +608,14 @@ void reds_client_disconnect(RedsState *reds, RedClient *client)
// reds_client_disconnect
static void reds_disconnect(RedsState *reds)
{
- RingItem *link, *next;
+ GList *link, *next;
spice_info(NULL);
- RING_FOREACH_SAFE(link, next, &reds->clients) {
- reds_client_disconnect(reds, SPICE_CONTAINEROF(link, RedClient, link));
+ link = reds->clients;
+ while (link) {
+ next = link->next;
+ reds_client_disconnect(reds, link->data);
+ link = next;
}
reds_mig_cleanup(reds);
}
@@ -921,7 +924,7 @@ void reds_handle_agent_mouse_event(RedsState *reds, const VDAgentMouseState *mou
static int reds_get_n_clients(RedsState *reds)
{
- return reds ? reds->num_clients : 0;
+ return reds ? g_list_length(reds->clients) : 0;
}
SPICE_GNUC_VISIBLE int spice_server_get_num_clients(SpiceServer *s)
@@ -953,7 +956,7 @@ static void reds_fill_channels(RedsState *reds, SpiceMsgChannels *channels_info)
for (l = reds->channels; l != NULL; l = l->next) {
uint32_t type, id;
RedChannel *channel = l->data;
- if (reds->num_clients > 1 && !channel_is_secondary(channel)) {
+ if (g_list_length(reds->clients) > 1 && !channel_is_secondary(channel)) {
continue;
}
g_object_get(channel, "channel-type", &type, "id", &id, NULL);
@@ -1184,7 +1187,7 @@ void reds_on_main_channel_migrate(RedsState *reds, MainChannelClient *mcc)
VDIPortState *agent_state = reds->agent_state;
uint32_t read_data_len;
- spice_assert(reds->num_clients == 1);
+ spice_assert(g_list_length(reds->clients) == 1);
if (agent_state->priv->read_state != VDI_PORT_READ_STATE_READ_DATA) {
return;
@@ -1669,12 +1672,11 @@ static void reds_mig_target_client_disconnect_all(RedsState *reds)
static int reds_find_client(RedsState *reds, RedClient *client)
{
- RingItem *item;
+ GList *l;
- RING_FOREACH(item, &reds->clients) {
- RedClient *list_client;
+ for (l = reds->clients; l != NULL; l = l->next) {
+ RedClient *list_client = l->data;;
- list_client = SPICE_CONTAINEROF(item, RedClient, link);
if (list_client == client) {
return TRUE;
}
@@ -1685,13 +1687,13 @@ static int reds_find_client(RedsState *reds, RedClient *client)
/* should be used only when there is one client */
static RedClient *reds_get_client(RedsState *reds)
{
- spice_assert(reds->num_clients <= 1);
+ spice_assert(g_list_length(reds->clients) <= 1);
- if (reds->num_clients == 0) {
+ if (g_list_length(reds->clients) == 0) {
return NULL;
}
- return SPICE_CONTAINEROF(ring_get_head(&reds->clients), RedClient, link);
+ return RED_CLIENT(reds->clients->data);
}
// TODO: now that main is a separate channel this should
@@ -1738,8 +1740,7 @@ static void reds_handle_main_link(RedsState *reds, RedLinkInfo *link)
reds_link_free(link);
caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
client = red_client_new(reds, mig_target);
- ring_add(&reds->clients, &client->link);
- reds->num_clients++;
+ reds->clients = g_list_append(reds->clients, client);
mcc = main_channel_link(reds->main_channel, client,
stream, connection_id, mig_target,
link_mess->num_common_caps,
@@ -2912,13 +2913,13 @@ static void reds_mig_started(RedsState *reds)
static void reds_mig_fill_wait_disconnect(RedsState *reds)
{
- RingItem *client_item;
+ GList *l;
- spice_assert(reds->num_clients > 0);
+ spice_assert(g_list_length(reds->clients) > 0);
/* tracking the clients, in order to ignore disconnection
* of clients that got connected to the src after migration completion.*/
- RING_FOREACH(client_item, &reds->clients) {
- RedClient *client = SPICE_CONTAINEROF(client_item, RedClient, link);
+ for (l = reds->clients; l != NULL; l = l->next) {
+ RedClient *client = l->data;
reds->mig_wait_disconnect_clients = g_list_append(reds->mig_wait_disconnect_clients, client);
}
@@ -3366,8 +3367,7 @@ static int do_spice_init(RedsState *reds, SpiceCoreInterface *core_interface)
"self-token-released",
(GCallback)vdi_port_on_free_self_token,
reds);
- ring_init(&reds->clients);
- reds->num_clients = 0;
+ reds->clients = NULL;
reds->main_dispatcher = main_dispatcher_new(reds, reds->core);
ring_init(&reds->mig_target_clients);
reds->char_devices = NULL;
@@ -3906,7 +3906,7 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
try_seamless)) {
reds_mig_started(s);
} else {
- if (s->num_clients == 0) {
+ if (g_list_length(s->clients) == 0) {
reds_mig_release(s);
spice_info("no client connected");
}
@@ -3948,7 +3948,7 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_end(SpiceServer *s, int completed)
spice_assert(s->migration_interface);
sif = SPICE_CONTAINEROF(s->migration_interface->base.sif, SpiceMigrateInterface, base);
- if (completed && !s->expect_migrate && s->num_clients) {
+ if (completed && !s->expect_migrate && g_list_length(s->clients)) {
spice_warning("spice_server_migrate_info was not called, disconnecting clients");
reds_disconnect(s);
ret = -1;
@@ -3973,7 +3973,7 @@ complete:
SPICE_GNUC_VISIBLE int spice_server_migrate_switch(SpiceServer *s)
{
spice_info(NULL);
- if (!s->num_clients) {
+ if (!g_list_length(s->clients)) {
return 0;
}
s->expect_migrate = FALSE;