From a884ee4f859f2a60f4367955c6546fc0a481bb70 Mon Sep 17 00:00:00 2001 From: Jonathon Jongsma Date: Tue, 25 Oct 2016 16:56:48 -0500 Subject: Move RedClient to a separate file Also move the RedClient struct out of the header to avoid accessing the internals from other files. --- server/Makefile.am | 2 + server/char-device.c | 6 +- server/common-graphics-channel.c | 2 +- server/dcc.c | 1 + server/dummy-channel-client.c | 1 + server/inputs-channel.c | 1 + server/main-channel-client.c | 1 + server/main-channel.c | 1 + server/main-dispatcher.c | 4 +- server/red-channel-client.c | 2 +- server/red-channel.c | 240 ++------------------------------ server/red-channel.h | 62 +-------- server/red-client.c | 287 +++++++++++++++++++++++++++++++++++++++ server/red-client.h | 65 +++++++++ server/reds.c | 5 +- server/sound.c | 1 + server/stream.c | 1 + 17 files changed, 387 insertions(+), 295 deletions(-) create mode 100644 server/red-client.c create mode 100644 server/red-client.h 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 #include #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); @@ -671,201 +637,6 @@ int red_channel_no_item_being_sent(RedChannel *channel) return TRUE; } -/* - * 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 . + +*/ +#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 . + +*/ + +#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 #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) \ -- cgit v1.2.3