summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathon Jongsma <jjongsma@redhat.com>2016-10-25 16:56:48 -0500
committerJonathon Jongsma <jjongsma@redhat.com>2016-10-26 14:39:12 -0500
commita884ee4f859f2a60f4367955c6546fc0a481bb70 (patch)
treee342f21b61b43fba01d0292b9a36199ef02b26a8
parentff9df95baead1700898b1894f66a30dd40c36cea (diff)
Move RedClient to a separate file
Also move the RedClient struct out of the header to avoid accessing the internals from other files.
-rw-r--r--server/Makefile.am2
-rw-r--r--server/char-device.c6
-rw-r--r--server/common-graphics-channel.c2
-rw-r--r--server/dcc.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.c4
-rw-r--r--server/red-channel-client.c2
-rw-r--r--server/red-channel.c240
-rw-r--r--server/red-channel.h62
-rw-r--r--server/red-client.c287
-rw-r--r--server/red-client.h65
-rw-r--r--server/reds.c5
-rw-r--r--server/sound.c1
-rw-r--r--server/stream.c1
17 files changed, 387 insertions, 295 deletions
diff --git a/server/Makefile.am b/server/Makefile.am
index 7aada48c..a74d5ee2 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -102,6 +102,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 7775c071..318bf3c9 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 e3a3deda..08c2c78f 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 d430d43a..86507c01 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 c937c87d..0d318a7e 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 aadcf4b5..ae8d393c 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 daa3dfc8..b2f4d56b 100644
--- a/server/main-channel-client.c
+++ b/server/main-channel-client.c
@@ -26,6 +26,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 cf5ee6ae..b900b626 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 bc0de241..fe29b2df 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 195ee3ef..86d2305f 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 eb63b07f..97a94382 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
@@ -393,29 +390,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;
@@ -574,14 +548,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);
@@ -672,201 +638,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.
*/
@@ -1059,3 +830,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 528babc4..5908a755 100644
--- a/server/red-channel.h
+++ b/server/red-channel.h
@@ -326,64 +326,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_derstroy 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.
*
@@ -395,6 +337,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..39846720
--- /dev/null
+++ b/server/red-client.c
@@ -0,0 +1,287 @@
+/*
+ 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 */
+ 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_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 00000000..3ab44409
--- /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_derstroy 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 90437c0b..089c3257 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 c222c8d6..be9ad5fa 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 4e702229..e5cad969 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) \